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:
- Read the existing CLAUDE.md template (already created with design documents).
- Verify and update all sections with actual project state.
- 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:
- Create the root
pom.xml(aggregator) listing all 17 modules. - Create
membership-parent/pom.xmlwith: - 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 - Create
membership-core/pom.xmland core module with: - Base entity class (BaseEntitywith id, version, createdAt, updatedAt) - Tenant context (TenantContext,TenantInterceptor) - Security configuration (JWT filter, SecurityConfig) - Exception handling (GlobalExceptionHandler) - Common DTOs (PageRequest, PageResponse, ErrorResponse) - Create
membership-runner/pom.xmland runner module with: -MembershipApplication.java(Spring Boot main class) -application.ymlwith 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:
- 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) - Create
AbstractIntegrationTestbase class inmembership-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) { ... } } - Create
application-test.ymlinmembership-core/src/test/resources/: -spring.jpa.hibernate.ddl-auto: validate-spring.flyway.enabled: true- Testcontainers dynamic properties - Verify: write a simple test extending
AbstractIntegrationTestthat 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:
- Add
spring-boot-starter-data-redistomembership-core/pom.xml. - Create
RedisConfigurationclass: -RedisConnectionFactoryconfigured viaspring.data.redis.*properties -RedisCacheManagerwith default TTL of 5 minutes -RedisTemplate<String, Object>with JSON serialization - Create
@Cacheableabstraction: - Cache names:organizations,products,membershipTemplates,users- Cache eviction on mutations via@CacheEvict - Create rate-limiter infrastructure using Bucket4j + Redis:
-
RateLimiterServicewith configurable limits per endpoint group - Default: 100 requests/minute for authenticated, 20 requests/minute for anonymous - Redis-backed token buckets for distributed rate limiting - 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:
- Add
spring-boot-starter-amqptomembership-core/pom.xml. - Create
RabbitConfigurationclass: -ConnectionFactoryconfigured viaspring.rabbitmq.*properties -RabbitTemplatewith JSON message converter -SimpleRabbitListenerContainerFactorywith error handling and retry - 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 - Create
MessagePublisherutility: -publish(String routingKey, Object message)— send JSON message to exchange - Includes correlation ID for tracing - Create
@RabbitListenerpattern documentation in code comments. - 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} - Dead-letter queue configuration: failed messages routed to
*.dlqqueues 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:
- Add dependencies to
membership-core/pom.xml: -spring-boot-starter-actuator-micrometer-registry-prometheus - 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 - 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) - Health indicators:
-
Cash360HealthIndicator— checks Cash360 API reachability -RedisHealthIndicator— built-in via Spring Boot -RabbitHealthIndicator— built-in via Spring Boot - 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:
- Create Flutter project in
frontend/membership_app/. - Add dependencies:
dio,flutter_riverpod,go_router,freezed,json_serializable,flutter_secure_storage,intl,logger,flutter_localizations. - Create architecture:
-
lib/core/— Theme, routing, API client, constants -lib/features/— Feature modules -lib/shared/— Reusable widgets -lib/l10n/— Localization (ARB files, generated classes) - Create API client with: - Base URL configuration - JWT interceptor (access token in header, refresh on 401) - Error handling
- 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:
- Create
backend/docker-compose.ymlwith: - 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 - Create
.envfile with default values. - Test:
docker compose up -dstarts 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:
- Create
V000__seed_data.sqlinmembership-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 ...; ```
- Create corresponding entity classes in
membership-core:Currency,Country,Role,Permission,RolePermission,ChartOfAccount,VatRate. - Create Flyway migration for the seed tables:
V000__seed_tables.sql(DDL) followed byV001__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:
- Create
.gitlab-ci.ymlfor GitLab CI/CD: - Trigger on push todevelopandmain- Stages: build, test, package, deploy - Steps: checkout, setup JDK 25, Maven compile, unit tests, build Docker image - Cache Maven dependencies - Create
docker/Dockerfilefor 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:
- 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*) - 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 - Create
.mcp.jsonif 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:
- Configure Spring Boot
MessageSourcewithResourceBundleMessageSource: - Base name:messages- Default encoding: UTF-8 - Fallback to English (en) as base language - UseAccept-Languageheader for locale resolution - Create
LocaleResolverbean (AcceptHeaderLocaleResolver) andLocaleChangeInterceptor: - Default locale:en- Supported locales: all 32 languages - Create message properties files in
membership-core/src/main/resources/i18n/: -messages.properties(English — base/fallback) -messages_bg.propertiesthroughmessages_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) - 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) - Create
I18nServiceutility inmembership-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 - Add
localefield toUserentity (preferred language, defaulten) - API responses include translated messages based on:
-
Accept-Languageheader (for anonymous requests) - User's preferred locale (for authenticated requests)
Frontend i18n Setup
Approach:
- Enable Flutter localization in
pubspec.yaml:yaml flutter: generate: true dependencies: flutter_localizations: sdk: flutter intl: ^0.19.0 - Create
l10n.yamlconfiguration:yaml arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart output-class: AppLocalizations - Create ARB files in
lib/l10n/for all 32 languages: -app_en.arb(English — template/base) - 24 EU language ARB files (app_bg.arbthroughapp_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 - Populate
app_en.arbwith initial keys: - Navigation labels, auth screen text, common actions, status labels, error messages - Register all 32 supported locales in
MaterialApp. - Create
LocaleProvider(Riverpod) for runtime language switching. - RTL support: wrap app in
Directionalitywidget, detect RTL locales (ar), test with forced RTL.
Translation Workflow
- Base language: English (
en). All new keys first added tomessages.propertiesandapp_en.arb. - Placeholder convention: Non-translated languages initially copy English values with
[XX]prefix. - 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:
- Create
doc/business/executive-summary.md: - Product vision and market opportunity (derived fromdoc/design/01-executive-summary.md) - Target audience (sports clubs, fitness studios) - Key differentiators, revenue model - Create
doc/business/product-overview.md: - Feature overview by module, user roles, integration points, roadmap summary - 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:
- Run
python doc/intranet/build.py - 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.