PHASE 6: Communication & Events
Trigger: "Execute Phase 6 as per masterplan."
Goal: Email and push notification system, event/tournament management, and homepage generation for entities.
Prerequisite: Phase 5 is completed.
Step 6.0 — Technical Conception
Create doc/developer/conception/phase-6-conception.md.
(reference: Chapter 11 — Data Model, Chapter 12 — Integration)
Content: - Scope: membership-communication, membership-event (event part), homepage builder - Email Architecture: SMTP + Thymeleaf templates, per-entity branding, i18n (recipient locale) - Push Architecture: FCM (Android) + APNs (iOS), device token management - Homepage SSR: Thymeleaf server-side rendering, template schema, SEO - Design Chapter References: Ch11, Ch12
Result: Technical conception for Phase 6.
Step 6.1 — Communication Module
Create the membership-communication module.
(reference: Chapter 11 — Data Model, Chapter 12 — Integration)
Approach:
- Create JPA entities: - Communication fields:
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | Long | PK | |
| entityId | Long | FK NOT NULL | Organization |
| memberId | Long | FK nullable | Recipient member (null for broadcast) |
| templateId | Long | FK nullable | Source template |
| channelCd | String(20) | NOT NULL | EMAIL, PUSH, IN_APP, SMS |
| statusCd | String(20) | default 'PENDING' | PENDING / SENT / DELIVERED / FAILED / BOUNCED |
| subject | String(500) | NOT NULL | Rendered subject |
| body | String | NOT NULL | Rendered body (HTML for email, plain for push) |
| recipientEmail | String(255) | nullable | Email address |
| recipientDeviceToken | String(500) | nullable | FCM/APNs token |
| sentAt | Instant | nullable | When sent |
| deliveredAt | Instant | nullable | When delivered (bounce tracking) |
| errorMessage | String(500) | nullable | Delivery error |
| createdAt | Instant | auto | |
| version | Long | @Version |
- CommunicationTemplate fields:
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | Long | PK | |
| entityId | Long | FK nullable | null = system-wide template |
| name | String(200) | NOT NULL | Internal name, e.g., "welcome_email" |
| typeCd | String(50) | NOT NULL | WELCOME, VERIFICATION, PASSWORD_RESET, PAYMENT_RECEIPT, PAYMENT_FAILURE, CONTRACT_RENEWAL, COURSE_CANCELLATION, CUSTOM, SUPPORT_MACRO, HOMEPAGE |
| channelCd | String(20) | NOT NULL | EMAIL, PUSH, IN_APP |
| locale | String(10) | NOT NULL | e.g., 'de', 'en', 'ar' |
| subject | String(500) | NOT NULL | Subject with {{variables}} |
| body | String | NOT NULL | Thymeleaf template body |
| variables | JSONB | default '[]' | Available variables: [{name, description, example}] |
| active | boolean | default true | |
| createdAt | Instant | auto | |
| updatedAt | Instant | auto | |
| version | Long | @Version |
- Create services:
-
NotificationService: send via channel (email/push/in-app), resolve template + variables, select locale from recipient User.locale -TemplateService: CRUD, variable substitution (Thymeleaf), per-entity branding (logo, colors, footer from Organization.settings), locale fallback chain (member locale → entity locale → 'en') -BulkMessageService: send to filtered member list (all, active contracts, specific template, custom filter), RabbitMQ queuenotification.sendfor async processing, rate limiting (max 100/min per entity) -EmailSender: SMTP integration (Spring JavaMailSender), HTML rendering, per-entity SMTP config (or shared default), bounce tracking -PushSender: FCM (Android), APNs (iOS), device token management per member - Create controller:
-
POST /api/communications/send— Send notification (single or bulk) -GET /api/communications— History (paginated, filterable by channel/status/member) -GET /api/communication-templates— List templates -POST /api/communication-templates— Create template -PUT /api/communication-templates/{id}— Update template -POST /api/communication-templates/{id}/preview— Preview with sample data - Flyway:
V600__create_communication.sql- Indexes: (entityId, channelCd, statusCd), (memberId), (templateId) - Automated triggers (via RabbitMQ event listeners): - Member registration → welcome email - Email verification → verification link - Password reset → reset link - Payment received → payment receipt - Payment failed → payment failure notification - Contract renewal approaching → renewal reminder (30 days before) - Course cancelled → cancellation notification to all registrants
- Tests:
NotificationServiceTest,TemplateServiceTest,BulkMessageServiceTest
Result: Multi-channel notifications with templates, automation, branding, and i18n.
Step 6.2 — Event and Tournament Management
(reference: Chapter 11 — Data Model, Chapter 08 — User Journeys)
Approach:
- Create JPA entity:
Event- Event fields:
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | Long | PK | |
| entityId | Long | FK NOT NULL | Organization |
| title | String(255) | NOT NULL | Event title |
| description | String(4000) | Rich text description | |
| typeCd | String(50) | NOT NULL | TOURNAMENT, WORKSHOP, SOCIAL, CAMP, COMPETITION, OPEN_DAY |
| statusCd | String(50) | default 'DRAFT' | DRAFT / PUBLISHED / CANCELLED / COMPLETED |
| startDateTime | Instant | NOT NULL | Event start |
| endDateTime | Instant | NOT NULL | Event end |
| registrationDeadline | Instant | nullable | Registration closes |
| resourceId | Long | FK nullable | Booked resource/room |
| maxParticipants | Integer | nullable | Capacity (null = unlimited) |
| currentParticipants | Integer | default 0 | Current count |
| waitListEnabled | boolean | default false | Enable wait list |
| entryFee | BigDecimal(19,4) | nullable | Entry fee (null = free) |
| currency | String(3) | default 'EUR' | |
| isPublic | boolean | default true | Visible to non-members |
| imageUrl | String(500) | nullable | Event image |
| organizerUserId | Long | FK nullable | Responsible staff |
| customAttributes | JSONB | default '{}' | Category-specific fields |
| createdAt | Instant | auto | |
| updatedAt | Instant | auto | |
| version | Long | @Version |
EventRegistration: id, eventId, memberId, status (REGISTERED/WAIT_LIST/CANCELLED), transactionId (FK nullable, if paid), registeredAt
- Create
EventService: - CRUD with status transitions (DRAFT → PUBLISHED → COMPLETED) - Registration: validate capacity, move to wait list if full, generate Transaction if entryFee > 0 - Wait list: auto-promote when spot opens (notify via RabbitMQ) - Cancel registration: refund if paid (create storno transaction) - Cancel event: notify all registrants, refund all paid registrations - Upcoming events: public endpoint for published events, sorted by date - Resource booking: auto-book resource for event duration (conflict detection) - Create controller:
-
POST /api/events— Create event (CLUB_ADMIN) -GET /api/events— List events (admin: all; member: published only) -GET /api/events/upcoming/{entityId}— Public upcoming events (no auth) -GET /api/events/{id}— Event detail with participant list (admin) -PUT /api/events/{id}— Update event -POST /api/events/{id}/register— Register for event (member) -DELETE /api/events/{id}/register— Cancel registration (member) -POST /api/events/{id}/cancel— Cancel event (admin) - Member app: event list (card layout with image, date, title, spots remaining), event detail, register button
- Admin app: event management (DataTable), create/edit form, participant list with export
- Flyway:
V601__create_event.sql,V602__create_event_registration.sql - Tests:
EventServiceTest— registration, capacity, wait list promotion, fee transaction, cancellation refund
Result: Event management with registration, capacity, wait list, payment, and resource booking.
Step 6.3 — Homepage Generation
Create entity-specific landing pages for member acquisition.
(reference: Chapter 09 — Frontend Strategy)
Approach:
- Server-Side Rendering (Thymeleaf):
- Each entity gets a public page at
/p/{entity-slug}- Thymeleaf template loaded fromCommunicationTemplate(type: HOMEPAGE) - Data sources: organization settings, membership templates, course schedule, upcoming events - Template Schema:
-
HomepageConfigJSONB in Organization entity:json { "heroTitle": "Welcome to FitClub Berlin", "heroSubtitle": "Your fitness journey starts here", "showPricing": true, "showSchedule": true, "showEvents": true, "accentColor": "#FF5722", "contactEmail": "info@fitclub-berlin.de" }- Admin UI: visual editor for homepage config fields - SEO:
-
<meta>tags: title, description, og:image from organization logo - Structured data:LocalBusinessschema.org JSON-LD - Sitemap.xml generated from all active entity slugs - Subdomain Support (optional):
- Custom domain mapping:
fitclub-berlin.membership-one.comorwww.fitclub-berlin.de- DNS CNAME verification - TLS via Let's Encrypt wildcard or per-domain - Cache: - Redis cache (5 min TTL) for rendered pages - Cache bust on organization settings update
Result: Each organization has a public-facing, SEO-optimized landing page.
Step 6.4 — Phase 6 Documentation
Update all doc files with communication, event, homepage features.
Step 6.5 — Update Intranet
Run python doc/intranet/build.py.
Phase 6 — Quality Gate
| # | Check | Target |
|---|---|---|
| 1 | Conception | exists |
| 2 | Compilation + tests | 0 errors, 0 failures |
| 3 | Coverage | >= 65% |
| 4 | Email notification | sent to member, received |
| 5 | Event | create -> register -> capacity tracking |
| 6 | Homepage | /p/{slug} renders with organization data |
| 7 | SEO | meta tags, structured data, sitemap |
| 8 | Documentation + intranet | updated |
| 9 | CLAUDE.md | updated |
Report: "Phase 6 completed."