PHASE 0: Project Setup

Trigger: "Execute Phase 0 as per masterplan."

Goal: The project skeleton is ready: directory structure, build system, Docker infrastructure, CI/CD pipeline, i18n setup (32 languages), Testcontainers, Redis caching, RabbitMQ messaging, monitoring, seed data, and CLAUDE.md are in place. A "Hello World" endpoint responds at localhost:8080/api/health.

Prerequisite: Claude Code is started in the membership project directory.

Step 0.1 — Create CLAUDE.md

Create the file CLAUDE.md in the membership project root (membership/CLAUDE.md).

Approach:

  1. Read the existing CLAUDE.md template (already created with design documents).
  2. Verify and update all sections with actual project state.
  3. Ensure the directory structure section reflects the actual layout.

Result: CLAUDE.md exists with all sections populated.


Step 0.2 — Create Project Directory Structure

Create the complete directory structure for the membership project.

(reference: Chapter 06 — Module Architecture)

membership/
├── CLAUDE.md
├── MASTERPLAN.md
├── .mcp.json
├── .claude/
│   ├── settings.json
│   └── commands/
├── doc/
│   ├── design/                  ← Design document chapters (already exist)
│   ├── index/                   ← Index files (populated later)
│   ├── api/                     ← API documentation
│   ├── testing/                 ← Test strategy and reports
│   ├── business/                ← Business documentation
│   │   ├── executive-summary.md
│   │   ├── product-overview.md
│   │   └── marketing-guide.md
│   ├── developer/               ← Developer documentation
│   │   ├── backend-guide.md
│   │   ├── frontend-guide.md
│   │   ├── api-reference.md
│   │   ├── database-guide.md
│   │   └── conception/          ← Technical conceptions per phase
│   ├── enduser/                 ← End-user documentation
│   │   ├── admin-manual.md
│   │   ├── trainer-manual.md
│   │   ├── consumer-manual.md
│   │   └── faq.md
│   ├── operations/              ← Operations documentation
│   │   ├── deployment-guide.md
│   │   ├── monitoring-guide.md
│   │   ├── support-runbook.md
│   │   └── itops-guide.md
│   └── intranet/                ← HTML documentation site (auto-generated)
│       └── build.py             ← Intranet build script
├── backend/
│   ├── pom.xml                  ← Root POM (aggregator)
│   ├── membership-parent/       ← Parent POM (dependency management)
│   │   └── pom.xml
│   ├── membership-core/         ← Core module (shared utilities, security, config)
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-entity/       ← Entity/organization management module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-auth/         ← Authentication and authorization module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-member/       ← Member management module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-contract/     ← Contract/membership management module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-product/      ← Product/service management module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-payment/      ← Payment and billing module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-resource/     ← Resource management module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-event/        ← Event and course management module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-communication/← Communication module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-checkin/      ← Check-in and access control module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-import/       ← Data import module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-document/     ← Document management module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-crm/          ← CRM and sales module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-support/      ← Support and ticketing module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-accounting/   ← Accounting and DATEV module
│   │   ├── pom.xml
│   │   └── src/
│   ├── membership-runner/       ← Application runner (main entry point)
│   │   ├── pom.xml
│   │   └── src/
│   └── docker-compose.yml       ← Local dev infrastructure
├── frontend/
│   ├── membership_app/          ← Main Flutter app (role-based views)
│   │   ├── pubspec.yaml
│   │   ├── lib/
│   │   └── test/
│   └── README.md
├── docker/
│   ├── Dockerfile               ← Backend container image
│   ├── Dockerfile.frontend      ← Frontend container image
│   └── docker-compose.prod.yml  ← Production Docker Compose
└── infra/
    ├── k8s/                     ← Kubernetes manifests
    └── ci/                      ← CI/CD pipeline configuration

Result: Empty directory structure prepared for all phases, including doc/developer/conception/ for technical conceptions.


Step 0.3 — Initialize Java Backend

Create the Maven multi-module project structure.

(reference: Chapter 06 — Module Architecture)

Approach:

  1. Create the root pom.xml (aggregator) listing all 17 modules.
  2. Create membership-parent/pom.xml with: - Java 25 compiler settings - Spring Boot 4.0.2 parent - Dependency management: PostgreSQL 18, Redis 7, RabbitMQ 4, Flyway, JWT (java-jwt RS256), Jackson, Lombok, Resilience4j, Guava, Testcontainers, WireMock, JaCoCo, Bucket4j - Plugin management: maven-compiler-plugin, spring-boot-maven-plugin, surefire, failsafe, jacoco
  3. Create membership-core/pom.xml and core module with: - Base entity class (BaseEntity with id, version, createdAt, updatedAt) - Tenant context (TenantContext, TenantInterceptor) - Security configuration (JWT filter, SecurityConfig) - Exception handling (GlobalExceptionHandler) - Common DTOs (PageRequest, PageResponse, ErrorResponse)
  4. Create membership-runner/pom.xml and runner module with: - MembershipApplication.java (Spring Boot main class) - application.yml with profile-based configuration (local, dev, staging, prod, test) - Health endpoint responding at /api/health

Validation:

cd backend && ./mvnw clean compile
# Must succeed with zero errors

Result: Maven project compiles. membership-runner starts and serves /api/health.


Step 0.3a — Testcontainers Setup

Configure Testcontainers for integration testing.

(reference: Chapter 06 — Module Architecture, Chapter 14 — Infrastructure)

Approach:

  1. Add Testcontainers dependencies to membership-parent/pom.xml: - org.testcontainers:testcontainers (BOM) - org.testcontainers:postgresql - org.testcontainers:junit-jupiter - com.redis:testcontainers-redis-junit-jupiter (Redis Testcontainer)
  2. Create AbstractIntegrationTest base class in membership-core/src/test/java/: java @SpringBootTest @Testcontainers public abstract class AbstractIntegrationTest { @Container static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18-alpine"); @Container static GenericContainer<?> redis = new GenericContainer<>("redis:7-alpine").withExposedPorts(6379); @DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { ... } }
  3. Create application-test.yml in membership-core/src/test/resources/: - spring.jpa.hibernate.ddl-auto: validate - spring.flyway.enabled: true - Testcontainers dynamic properties
  4. Verify: write a simple test extending AbstractIntegrationTest that asserts DB connection works.

Validation:

cd backend && ./mvnw test -pl membership-core -Dtest=IntegrationTestSmokeTest

Result: Testcontainers infrastructure ready. Integration tests start PostgreSQL 18 + Redis 7 containers automatically.


Step 0.3b — Redis Integration

Configure Redis for caching and rate limiting.

(reference: Chapter 06 — Module Architecture, Chapter 13 — Security)

Approach:

  1. Add spring-boot-starter-data-redis to membership-core/pom.xml.
  2. Create RedisConfiguration class: - RedisConnectionFactory configured via spring.data.redis.* properties - RedisCacheManager with default TTL of 5 minutes - RedisTemplate<String, Object> with JSON serialization
  3. Create @Cacheable abstraction: - Cache names: organizations, products, membershipTemplates, users - Cache eviction on mutations via @CacheEvict
  4. Create rate-limiter infrastructure using Bucket4j + Redis: - RateLimiterService with configurable limits per endpoint group - Default: 100 requests/minute for authenticated, 20 requests/minute for anonymous - Redis-backed token buckets for distributed rate limiting
  5. Add Redis properties to application.yml: yaml spring.data.redis: host: ${REDIS_HOST:localhost} port: ${REDIS_PORT:6379}

Validation:

cd backend && ./mvnw test -pl membership-core -Dtest=RedisConfigurationTest

Result: Redis caching and rate limiting infrastructure ready. @Cacheable annotations can be used by all modules.


Step 0.3c — RabbitMQ Integration

Configure RabbitMQ for asynchronous messaging.

(reference: Chapter 06 — Module Architecture, Chapter 14 — Infrastructure)

Approach:

  1. Add spring-boot-starter-amqp to membership-core/pom.xml.
  2. Create RabbitConfiguration class: - ConnectionFactory configured via spring.rabbitmq.* properties - RabbitTemplate with JSON message converter - SimpleRabbitListenerContainerFactory with error handling and retry
  3. Define exchange and queue topology: java // Exchange: membership.topic (TopicExchange) // Queues and routing keys: public static final String EXCHANGE = "membership.topic"; public static final String QUEUE_PROVISIONING = "provisioning.create"; // tenant provisioning public static final String QUEUE_NOTIFICATION = "notification.send"; // email/push dispatch public static final String QUEUE_BILLING = "billing.execute"; // billing cycle public static final String QUEUE_IMPORT = "import.process"; // CSV import jobs public static final String QUEUE_DOCUMENT = "document.generate"; // PDF generation
  4. Create MessagePublisher utility: - publish(String routingKey, Object message) — send JSON message to exchange - Includes correlation ID for tracing
  5. Create @RabbitListener pattern documentation in code comments.
  6. Add RabbitMQ properties to application.yml: yaml spring.rabbitmq: host: ${RMQ_HOST:localhost} port: ${RMQ_PORT:5672} username: ${RMQ_USER:guest} password: ${RMQ_PASS:guest}
  7. Dead-letter queue configuration: failed messages routed to *.dlq queues after 3 retries.

Validation:

cd backend && ./mvnw test -pl membership-core -Dtest=RabbitConfigurationTest

Result: RabbitMQ messaging infrastructure ready. Queues for provisioning, notification, billing, import, and document generation defined.


Step 0.3d — Monitoring Setup

Configure application monitoring with Actuator and Micrometer.

(reference: Chapter 14 — Infrastructure and Deployment)

Approach:

  1. Add dependencies to membership-core/pom.xml: - spring-boot-starter-actuator - micrometer-registry-prometheus
  2. Configure Actuator endpoints in application.yml: yaml management: endpoints.web.exposure.include: health,info,metrics,prometheus endpoint.health.show-details: when_authorized metrics.tags.application: membership-one
  3. Create custom business metrics in MetricsService: - membership.members.active (Gauge) — active member count per entity - membership.checkins.total (Counter) — check-ins processed - membership.billing.transactions (Counter) — billing transactions created - membership.provisioning.duration (Timer) — tenant provisioning time - membership.api.requests (Counter, tagged by endpoint group)
  4. Health indicators: - Cash360HealthIndicator — checks Cash360 API reachability - RedisHealthIndicator — built-in via Spring Boot - RabbitHealthIndicator — built-in via Spring Boot
  5. Prometheus endpoint at /actuator/prometheus (used by Grafana in Phase 12).

Validation:

# Start application, then:
curl http://localhost:8080/api/actuator/health
curl http://localhost:8080/api/actuator/prometheus

Result: Monitoring endpoints active. Custom business metrics registered. Prometheus-compatible.


Step 0.4 — Initialize Flutter Frontend

Create the Flutter project for the member/admin app.

(reference: Chapter 09 — Frontend Strategy)

Approach:

  1. Create Flutter project in frontend/membership_app/.
  2. Add dependencies: dio, flutter_riverpod, go_router, freezed, json_serializable, flutter_secure_storage, intl, logger, flutter_localizations.
  3. Create architecture: - lib/core/ — Theme, routing, API client, constants - lib/features/ — Feature modules - lib/shared/ — Reusable widgets - lib/l10n/ — Localization (ARB files, generated classes)
  4. Create API client with: - Base URL configuration - JWT interceptor (access token in header, refresh on 401) - Error handling
  5. Create placeholder login screen.

Validation:

cd frontend/membership_app && flutter analyze
# Must show 0 issues

Result: Flutter app compiles and shows a login screen.


Step 0.5 — Docker Infrastructure

Create Docker Compose for local development.

(reference: Chapter 14 — Infrastructure and Deployment)

Approach:

  1. Create backend/docker-compose.yml with: - PostgreSQL 18 (port 5432, DB: membership, user: membership, schema: public) - Redis 7 (port 6379) - RabbitMQ 4 (ports 5672, 15672) - Health checks on all containers - Named volumes for persistence
  2. Create .env file with default values.
  3. Test: docker compose up -d starts all containers healthy.

Validation:

cd backend && docker compose up -d && docker compose ps
# All containers must show "healthy"

Result: Docker infrastructure running. Application can connect to all services.


Step 0.5a — Seed Data

Create Flyway seed data migration for reference data.

(reference: Chapter 11 — Data Model, Chapter 12 — Integration)

Approach:

  1. Create V000__seed_data.sql in membership-core/src/main/resources/db/migration/: ```sql -- Currencies (ISO 4217) INSERT INTO currency (code, name, symbol, decimal_places) VALUES ('EUR', 'Euro', '€', 2), ('CHF', 'Swiss Franc', 'CHF', 2), ('GBP', 'British Pound', '£', 2), ('USD', 'US Dollar', '$', 2), ('SEK', 'Swedish Krona', 'kr', 2), ('DKK', 'Danish Krone', 'kr', 2), ('NOK', 'Norwegian Krone', 'kr', 2), ('PLN', 'Polish Zloty', 'zł', 2), ('CZK', 'Czech Koruna', 'Kč', 2), ('HUF', 'Hungarian Forint', 'Ft', 0), ('RON', 'Romanian Leu', 'lei', 2), ('BGN', 'Bulgarian Lev', 'лв', 2), ('TRY', 'Turkish Lira', '₺', 2), ('RSD', 'Serbian Dinar', 'din', 2), ('ALL', 'Albanian Lek', 'L', 2), ('UAH', 'Ukrainian Hryvnia', '₴', 2);

-- Countries (ISO 3166-1, 27 EU + 7 non-EU priority = 34) INSERT INTO country (code, name, eu_member, currency_code, phone_prefix) VALUES -- EU members (27) ('AT', 'Austria', true, 'EUR', '+43'), ('BE', 'Belgium', true, 'EUR', '+32'), ('BG', 'Bulgaria', true, 'BGN', '+359'), ('HR', 'Croatia', true, 'EUR', '+385'), ('CY', 'Cyprus', true, 'EUR', '+357'), ('CZ', 'Czech Republic', true, 'CZK', '+420'), ('DK', 'Denmark', true, 'DKK', '+45'), ('EE', 'Estonia', true, 'EUR', '+372'), ('FI', 'Finland', true, 'EUR', '+358'), ('FR', 'France', true, 'EUR', '+33'), ('DE', 'Germany', true, 'EUR', '+49'), ('GR', 'Greece', true, 'EUR', '+30'), ('HU', 'Hungary', true, 'HUF', '+36'), ('IE', 'Ireland', true, 'EUR', '+353'), ('IT', 'Italy', true, 'EUR', '+39'), ('LV', 'Latvia', true, 'EUR', '+371'), ('LT', 'Lithuania', true, 'EUR', '+370'), ('LU', 'Luxembourg', true, 'EUR', '+352'), ('MT', 'Malta', true, 'EUR', '+356'), ('NL', 'Netherlands', true, 'EUR', '+31'), ('PL', 'Poland', true, 'PLN', '+48'), ('PT', 'Portugal', true, 'EUR', '+351'), ('RO', 'Romania', true, 'RON', '+40'), ('SK', 'Slovakia', true, 'EUR', '+421'), ('SI', 'Slovenia', true, 'EUR', '+386'), ('ES', 'Spain', true, 'EUR', '+34'), ('SE', 'Sweden', true, 'SEK', '+46'), -- Non-EU priority (7) ('TR', 'Turkey', false, 'TRY', '+90'), ('GB', 'United Kingdom', false, 'GBP', '+44'), ('CH', 'Switzerland', false, 'CHF', '+41'), ('NO', 'Norway', false, 'NOK', '+47'), ('RS', 'Serbia', false, 'RSD', '+381'), ('AL', 'Albania', false, 'ALL', '+355'), ('UA', 'Ukraine', false, 'UAH', '+380');

-- Roles (~16, per Chapter 07) INSERT INTO role (code, name, level, description) VALUES ('SUPER_ADMIN', 'Super Admin', 'SYSTEM', 'Membership One platform admin'), ('FRANCHISE_OWNER', 'Franchise Owner', 'MANUFACTURER', 'Franchise network owner'), ('FRANCHISE_ADMIN', 'Franchise Admin', 'FRANCHISE', 'Franchise admin'), ('CLUB_ADMIN', 'Club Admin', 'CLUB', 'Club/studio administrator'), ('RECEPTIONIST', 'Receptionist', 'CLUB', 'Front desk staff'), ('MEMBER', 'Member', 'CLUB', 'Regular member'), ('TRAINER', 'Trainer', 'VENDOR', 'External trainer'), ('PHYSIOTHERAPIST', 'Physiotherapist', 'VENDOR', 'External physiotherapist'), ('CLEANING_SERVICE', 'Cleaning Service', 'VENDOR', 'External cleaning'), ('ACCOUNTANT', 'Accountant', 'LATERAL', 'Accounting role'), ('MARKETING_MANAGER', 'Marketing Manager', 'LATERAL', 'Marketing role'), ('SALES_MANAGER', 'Sales Manager', 'LATERAL', 'Sales/CRM role'), ('SUPPORT_AGENT', 'Support Agent', 'LATERAL', 'Support/ticketing role'), ('COURSE_MANAGER', 'Course Manager', 'LATERAL', 'Course scheduling role'), ('API_SERVICE', 'API Service', 'SPECIAL', 'Service-to-service account'), ('AUDITOR', 'Auditor', 'SPECIAL', 'Read-only audit access');

-- Chart of Accounts (SKR03 subset for membership management, ~30 accounts) INSERT INTO chart_of_account (code, name, type, category) VALUES -- Assets (Aktiva) ('1000', 'Kasse', 'ASSET', 'CURRENT_ASSETS'), ('1200', 'Bank', 'ASSET', 'CURRENT_ASSETS'), ('1210', 'Bank (Treuhandkonto/Fremdgelder)', 'ASSET', 'CURRENT_ASSETS'), ('1400', 'Forderungen aus L+L', 'ASSET', 'RECEIVABLES'), ('1410', 'Forderungen Mitgliedsbeiträge', 'ASSET', 'RECEIVABLES'), ('1450', 'Forderungen Kurse/Events', 'ASSET', 'RECEIVABLES'), ('1460', 'Forderungen Shop', 'ASSET', 'RECEIVABLES'), ('1510', 'Forderungen SEPA-Rücklastschriften', 'ASSET', 'RECEIVABLES'), -- Liabilities (Passiva) ('1600', 'Verbindlichkeiten aus L+L', 'LIABILITY', 'PAYABLES'), ('1700', 'Sonstige Verbindlichkeiten', 'LIABILITY', 'PAYABLES'), ('1710', 'Verbindlichkeiten Fremdbeträge', 'LIABILITY', 'PAYABLES'), ('1780', 'Umsatzsteuer (Vorjahr)', 'LIABILITY', 'TAX'), ('1790', 'Umsatzsteuer lfd. Jahr', 'LIABILITY', 'TAX'), ('1800', 'Vorsteuer', 'ASSET', 'TAX'), -- Revenue (Erlöse) ('4400', 'Erlöse Mitgliedsbeiträge', 'REVENUE', 'OPERATING'), ('4401', 'Erlöse Kurse/Events', 'REVENUE', 'OPERATING'), ('4402', 'Erlöse Shop/Merchandise', 'REVENUE', 'OPERATING'), ('4403', 'Erlöse Setup-Gebühren', 'REVENUE', 'OPERATING'), ('4404', 'Erlöse Tageskarten/Day Passes', 'REVENUE', 'OPERATING'), ('4405', 'Erlöse Personal Training', 'REVENUE', 'OPERATING'), ('4410', 'Erlösschmälerungen (Storno/Gutschriften)', 'REVENUE', 'ADJUSTMENTS'), ('4440', 'Weiterbelastete Fremdbeträge (Durchlaufposten)', 'REVENUE', 'PASS_THROUGH'), -- Expenses (Aufwand) ('6300', 'Hosting/Cloud-Kosten', 'EXPENSE', 'INFRASTRUCTURE'), ('6310', 'Software-Lizenzen (extern)', 'EXPENSE', 'INFRASTRUCTURE'), ('6400', 'Versicherungen', 'EXPENSE', 'OPERATING'), ('6800', 'Porto und Versand', 'EXPENSE', 'OPERATING'), ('6815', 'Payment-Provider-Gebühren', 'EXPENSE', 'OPERATING'), ('6820', 'SEPA-Rücklastschrift-Kosten', 'EXPENSE', 'OPERATING'), ('6830', 'Bankgebühren', 'EXPENSE', 'OPERATING'), ('7000', 'Personalkosten', 'EXPENSE', 'PERSONNEL');

-- VAT rates (EU standard + reduced, per country — all 27 EU + CH, NO, UK) INSERT INTO vat_rate (country_code, rate, name, type) VALUES ('DE', 19.00, 'Standard', 'STANDARD'), ('DE', 7.00, 'Ermäßigt', 'REDUCED'), ('AT', 20.00, 'Standard', 'STANDARD'), ('AT', 10.00, 'Ermäßigt', 'REDUCED'), ('BE', 21.00, 'Standard', 'STANDARD'), ('BE', 6.00, 'Réduit', 'REDUCED'), ('BG', 20.00, 'Standard', 'STANDARD'), ('BG', 9.00, 'Reduced', 'REDUCED'), ('HR', 25.00, 'Standard', 'STANDARD'), ('HR', 13.00, 'Reduced', 'REDUCED'), ('CY', 19.00, 'Standard', 'STANDARD'), ('CY', 5.00, 'Reduced', 'REDUCED'), ('CZ', 21.00, 'Standard', 'STANDARD'), ('CZ', 12.00, 'Reduced', 'REDUCED'), ('DK', 25.00, 'Standard', 'STANDARD'), ('EE', 22.00, 'Standard', 'STANDARD'), ('EE', 9.00, 'Reduced', 'REDUCED'), ('FI', 25.50, 'Standard', 'STANDARD'), ('FI', 14.00, 'Reduced', 'REDUCED'), ('FR', 20.00, 'Standard', 'STANDARD'), ('FR', 5.50, 'Réduit', 'REDUCED'), ('GR', 24.00, 'Standard', 'STANDARD'), ('GR', 6.00, 'Reduced', 'REDUCED'), ('HU', 27.00, 'Standard', 'STANDARD'), ('HU', 5.00, 'Reduced', 'REDUCED'), ('IE', 23.00, 'Standard', 'STANDARD'), ('IE', 9.00, 'Reduced', 'REDUCED'), ('IT', 22.00, 'Standard', 'STANDARD'), ('IT', 10.00, 'Ridotta', 'REDUCED'), ('LV', 21.00, 'Standard', 'STANDARD'), ('LV', 12.00, 'Reduced', 'REDUCED'), ('LT', 21.00, 'Standard', 'STANDARD'), ('LT', 9.00, 'Reduced', 'REDUCED'), ('LU', 17.00, 'Standard', 'STANDARD'), ('LU', 8.00, 'Réduit', 'REDUCED'), ('MT', 18.00, 'Standard', 'STANDARD'), ('MT', 5.00, 'Reduced', 'REDUCED'), ('NL', 21.00, 'Standard', 'STANDARD'), ('NL', 9.00, 'Verlaagd', 'REDUCED'), ('PL', 23.00, 'Standard', 'STANDARD'), ('PL', 8.00, 'Reduced', 'REDUCED'), ('PT', 23.00, 'Standard', 'STANDARD'), ('PT', 6.00, 'Reduzida', 'REDUCED'), ('RO', 19.00, 'Standard', 'STANDARD'), ('RO', 9.00, 'Reduced', 'REDUCED'), ('SK', 23.00, 'Standard', 'STANDARD'), ('SK', 10.00, 'Reduced', 'REDUCED'), ('SI', 22.00, 'Standard', 'STANDARD'), ('SI', 9.50, 'Reduced', 'REDUCED'), ('ES', 21.00, 'Standard', 'STANDARD'), ('ES', 10.00, 'Reducido', 'REDUCED'), ('SE', 25.00, 'Standard', 'STANDARD'), ('SE', 6.00, 'Reducerad', 'REDUCED'), -- Non-EU ('CH', 8.10, 'Standard', 'STANDARD'), ('CH', 2.60, 'Ermäßigt', 'REDUCED'), ('GB', 20.00, 'Standard', 'STANDARD'), ('GB', 5.00, 'Reduced', 'REDUCED'), ('NO', 25.00, 'Standard', 'STANDARD'), ('NO', 12.00, 'Reduced', 'REDUCED');

-- Permissions (~40 total, per Chapter 07 permission matrix) INSERT INTO permission (code, module, description) VALUES -- Member module ('MEMBER_READ', 'member', 'View members'), ('MEMBER_WRITE', 'member', 'Create/edit members'), ('MEMBER_DELETE', 'member', 'Delete/archive members'), ('MEMBER_EXPORT', 'member', 'Export member data'), ('MEMBER_IMPORT', 'member', 'Import member data'), -- Contract module ('CONTRACT_READ', 'contract', 'View contracts'), ('CONTRACT_WRITE', 'contract', 'Create/manage contracts'), ('CONTRACT_CANCEL', 'contract', 'Cancel contracts'), -- Product module ('PRODUCT_READ', 'product', 'View products'), ('PRODUCT_WRITE', 'product', 'Create/edit products'), -- Payment/Billing module ('BILLING_READ', 'payment', 'View transactions and billing'), ('BILLING_EXECUTE', 'payment', 'Run billing cycle'), ('BILLING_STORNO', 'payment', 'Execute storno/refund'), ('SEPA_EXPORT', 'payment', 'Generate SEPA exports'), -- Entity/Organization module ('ENTITY_READ', 'entity', 'View organization info'), ('ENTITY_SETTINGS', 'entity', 'Manage organization settings'), ('ENTITY_BRANDING', 'entity', 'Manage branding/logo'), -- Auth/User module ('USER_READ', 'auth', 'View users'), ('USER_MANAGE', 'auth', 'Create/edit/deactivate users'), ('ROLE_MANAGE', 'auth', 'Assign/revoke roles'), -- Check-in module ('CHECKIN_READ', 'checkin', 'View check-in logs'), ('CHECKIN_MANAGE', 'checkin', 'Manage access zones/rules'), ('CHECKIN_OVERRIDE', 'checkin', 'Manual check-in override'), -- Resource/Booking module ('RESOURCE_READ', 'resource', 'View resources and bookings'), ('RESOURCE_WRITE', 'resource', 'Manage resources'), ('BOOKING_CREATE', 'resource', 'Create bookings'), ('BOOKING_MANAGE', 'resource', 'Manage all bookings'), -- Course/Event module ('COURSE_READ', 'event', 'View courses and events'), ('COURSE_WRITE', 'event', 'Create/edit courses'), ('COURSE_ATTENDANCE', 'event', 'Mark attendance'), ('EVENT_WRITE', 'event', 'Create/edit events'), -- Communication module ('COMMUNICATION_READ', 'communication', 'View sent messages'), ('COMMUNICATION_SEND', 'communication', 'Send notifications/emails'), ('TEMPLATE_MANAGE', 'communication', 'Manage email templates'), -- CRM module ('CRM_READ', 'crm', 'View leads and deals'), ('CRM_WRITE', 'crm', 'Manage leads/deals/activities'), -- Support module ('SUPPORT_READ', 'support', 'View support tickets'), ('SUPPORT_WRITE', 'support', 'Manage tickets, respond'), ('KB_MANAGE', 'support', 'Manage knowledge base articles'), -- Accounting module ('ACCOUNTING_READ', 'accounting', 'View accounting entries'), ('ACCOUNTING_WRITE', 'accounting', 'Create manual entries'), ('ACCOUNTING_EXPORT', 'accounting', 'DATEV export'), ('ACCOUNTING_CLOSE', 'accounting', 'Close accounting period'), -- Reports ('REPORT_VIEW', 'report', 'View reports and dashboards'), ('REPORT_EXPORT', 'report', 'Export reports (CSV/PDF)'), -- Document module ('DOCUMENT_READ', 'document', 'View/download documents'), ('DOCUMENT_WRITE', 'document', 'Upload documents'), -- Franchise module ('FRANCHISE_READ', 'franchise', 'View cross-entity data'), ('FRANCHISE_MANAGE', 'franchise', 'Manage franchise config');

-- Role-Permission mapping (key assignments per Chapter 07) -- SUPER_ADMIN: ALL permissions -- FRANCHISE_OWNER: ALL except ACCOUNTING_CLOSE, USER_MANAGE (delegates to CLUB_ADMIN) -- FRANCHISE_ADMIN: FRANCHISE_, REPORT_, MEMBER_READ, CONTRACT_READ, BILLING_READ -- CLUB_ADMIN: MEMBER_, CONTRACT_, BILLING_, PRODUCT_, ENTITY_, USER_, RESOURCE_, -- COURSE_, EVENT_, COMMUNICATION_, REPORT_, DOCUMENT_, CHECKIN_, BOOKING_ -- RECEPTIONIST: MEMBER_READ, MEMBER_WRITE, CHECKIN_, BOOKING_CREATE, COURSE_READ, -- COURSE_ATTENDANCE, COMMUNICATION_READ, DOCUMENT_READ -- TRAINER: MEMBER_READ, COURSE_READ, COURSE_ATTENDANCE, RESOURCE_READ, BOOKING_CREATE -- MEMBER: (self-service only — no admin permissions, controlled via separate mobile endpoints) -- ACCOUNTANT: ACCOUNTING_, BILLING_READ, SEPA_EXPORT, REPORT_VIEW, REPORT_EXPORT -- MARKETING_MANAGER: CRM_, COMMUNICATION_, REPORT_VIEW, MEMBER_READ, MEMBER_EXPORT -- SALES_MANAGER: CRM_, MEMBER_READ, CONTRACT_READ, REPORT_VIEW -- SUPPORT_AGENT: SUPPORT_, KB_MANAGE, MEMBER_READ, BILLING_READ, COMMUNICATION_SEND -- COURSE_MANAGER: COURSE_, EVENT_, RESOURCE_, BOOKING_MANAGE, MEMBER_READ -- API_SERVICE: ALL (service-to-service, validated by API key, not JWT) -- AUDITOR: all _READ permissions + REPORT_VIEW + REPORT_EXPORT (read-only across all modules) INSERT INTO role_permission (role_code, permission_code) VALUES ...; ```

  1. Create corresponding entity classes in membership-core: Currency, Country, Role, Permission, RolePermission, ChartOfAccount, VatRate.
  2. Create Flyway migration for the seed tables: V000__seed_tables.sql (DDL) followed by V001__seed_data.sql (DML).

Validation:

cd backend && ./mvnw test -pl membership-core -Dtest=SeedDataTest
# Verify all seed data loads correctly

Result: Reference data available at application startup: currencies, countries, roles, permissions, SKR03 chart of accounts, VAT rates.


Step 0.6 — CI/CD Pipeline

Create CI/CD pipeline configuration.

(reference: Chapter 14 — Infrastructure and Deployment)

Approach:

  1. Create .gitlab-ci.yml for GitLab CI/CD: - Trigger on push to develop and main - Stages: build, test, package, deploy - Steps: checkout, setup JDK 25, Maven compile, unit tests, build Docker image - Cache Maven dependencies
  2. Create docker/Dockerfile for the backend: - Multi-stage build: Maven build -> JRE runtime - Base image: eclipse-temurin:25-jre-alpine - Expose port 8080 - Health check endpoint

Result: CI pipeline runs on push. Docker image builds successfully.


Step 0.7 — Configure Claude Code

Create Claude Code configuration for the membership project.

Approach:

  1. Create .claude/settings.json: - Allow: Read(**), Edit(doc/**), Edit(backend/**), Edit(frontend/**), Edit(docker/**), Edit(infra/**), Edit(CLAUDE.md), Edit(MASTERPLAN.md), Bash(mvn *), Bash(docker *), Bash(flutter *), Bash(dart *) - Deny: Edit(**/*secret*), Edit(**/*password*), Edit(**/*credential*)
  2. Create slash commands in .claude/commands/: - generate-module.md — Generate a full module (entity, DTO, repository, service, controller, tests) - generate-tests.md — Generate unit tests for a class - run-backend.md — Build and run the backend - reset-db.md — Reset the development database - api-test.md — Test an API endpoint with curl
  3. Create .mcp.json if MCP servers are available.

Result: Claude Code configured with permissions and slash commands.


Step 0.8 — Internationalization (i18n) Setup

Set up full multilingual support for backend and frontend.

(reference: Chapter 02 — Vision and Goals, Chapter 09 — Frontend Strategy)

Supported languages (32 total — 24 EU official + 8 non-EU priority):

EU official (24): Bulgarian (bg), Croatian (hr), Czech (cs), Danish (da), Dutch (nl), English (en), Estonian (et), Finnish (fi), French (fr), German (de), Greek (el), Hungarian (hu), Irish (ga), Italian (it), Latvian (lv), Lithuanian (lt), Maltese (mt), Polish (pl), Portuguese (pt), Romanian (ro), Slovak (sk), Slovenian (sl), Spanish (es), Swedish (sv).

Non-EU priority (8): Turkish (tr), Arabic (ar — RTL), Russian (ru), Ukrainian (uk), Serbian (sr), Albanian (sq), Chinese Simplified (zh), Hebrew (he — RTL).

Backend i18n Setup

Approach:

  1. Configure Spring Boot MessageSource with ResourceBundleMessageSource: - Base name: messages - Default encoding: UTF-8 - Fallback to English (en) as base language - Use Accept-Language header for locale resolution
  2. Create LocaleResolver bean (AcceptHeaderLocaleResolver) and LocaleChangeInterceptor: - Default locale: en - Supported locales: all 32 languages
  3. Create message properties files in membership-core/src/main/resources/i18n/: - messages.properties (English — base/fallback) - messages_bg.properties through messages_sv.properties (24 EU languages) - messages_tr.properties (Turkish) - messages_ar.properties (Arabic — RTL) - messages_ru.properties (Russian) - messages_uk.properties (Ukrainian) - messages_sr.properties (Serbian) - messages_sq.properties (Albanian) - messages_zh.properties (Chinese Simplified) - messages_he.properties (Hebrew — RTL)
  4. Populate messages.properties (English) with initial key categories: - error.* — Error messages (validation, auth, business logic) - email.* — Email templates (subject lines, body text) - notification.* — Push notification text - enum.* — Enum display values (status codes, roles, types) - field.* — Field labels (for API error messages)
  5. Create I18nService utility in membership-core: - getMessage(String key, Object... args) — resolve message for current locale - getMessage(String key, Locale locale, Object... args) — resolve for specific locale - Used by all modules for user-facing text
  6. Add locale field to User entity (preferred language, default en)
  7. API responses include translated messages based on: - Accept-Language header (for anonymous requests) - User's preferred locale (for authenticated requests)

Frontend i18n Setup

Approach:

  1. Enable Flutter localization in pubspec.yaml: yaml flutter: generate: true dependencies: flutter_localizations: sdk: flutter intl: ^0.19.0
  2. Create l10n.yaml configuration: yaml arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart output-class: AppLocalizations
  3. Create ARB files in lib/l10n/ for all 32 languages: - app_en.arb (English — template/base) - 24 EU language ARB files (app_bg.arb through app_sv.arb) - 8 non-EU ARB files: app_tr.arb, app_ar.arb, app_ru.arb, app_uk.arb, app_sr.arb, app_sq.arb, app_zh.arb, app_he.arb
  4. Populate app_en.arb with initial keys: - Navigation labels, auth screen text, common actions, status labels, error messages
  5. Register all 32 supported locales in MaterialApp.
  6. Create LocaleProvider (Riverpod) for runtime language switching.
  7. RTL support: wrap app in Directionality widget, detect RTL locales (ar), test with forced RTL.

Translation Workflow

  1. Base language: English (en). All new keys first added to messages.properties and app_en.arb.
  2. Placeholder convention: Non-translated languages initially copy English values with [XX] prefix.
  3. CI validation: Check all 32 language files contain the same keys. Fail build on missing keys.

Validation:

cd backend && ./mvnw test -pl membership-core -Dtest=I18nServiceTest
cd frontend/membership_app && flutter gen-l10n && flutter analyze

Result: i18n infrastructure ready for 32 languages including RTL. All language files created.


Step 0.9 — Initial Documentation

Create the initial documentation deliverables for Phase 0.

Approach:

  1. Create doc/business/executive-summary.md: - Product vision and market opportunity (derived from doc/design/01-executive-summary.md) - Target audience (sports clubs, fitness studios) - Key differentiators, revenue model
  2. Create doc/business/product-overview.md: - Feature overview by module, user roles, integration points, roadmap summary
  3. Create doc/intranet/build.py: - Python script that converts all markdown files to static HTML pages - Same pattern as Cash360 intranet build script

Result: Initial business documentation created. Intranet build script ready.


Step 0.10 — Update Intranet

Regenerate the HTML documentation site from all markdown files.

Approach:

  1. Run python doc/intranet/build.py
  2. Verify generated HTML files are complete and navigation works.

Result: Intranet site regenerated with all current documentation.


Phase 0 — Quality Gate

# Check Target
1 CLAUDE.md exists with all sections
2 Directory structure exists (including doc/developer/conception/)
3 backend/pom.xml exists and compiles (./mvnw clean compile) 0 errors
4 frontend/membership_app/pubspec.yaml exists, flutter analyze passes 0 issues
5 docker compose up -d starts all containers (Postgres, Redis, RabbitMQ) all healthy
6 Backend starts and responds at /api/health HTTP 200
7 Testcontainers smoke test passes green
8 Redis configuration test passes green
9 RabbitMQ configuration test passes green
10 Actuator endpoints respond (/actuator/health, /actuator/prometheus) HTTP 200
11 Seed data loads (currencies, countries, roles, permissions, SKR03, VAT) all present
12 All 32 i18n .properties files exist in backend 32 files
13 All 32 ARB files exist in frontend, flutter gen-l10n succeeds 32 files
14 Slash commands exist in .claude/commands/ 5 commands
15 Intranet site generated HTML files present
16 CLAUDE.md changelog updated

Report to user: "Phase 0 completed." with summary.

---