Files
fete/specs/008-rsvp/plan.md
nitrix 4828d06aba Add 008-rsvp feature spec and design artifacts
Spec, research decisions, implementation plan, data model,
API contract, and task breakdown for the RSVP feature.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 11:48:00 +01:00

7.5 KiB

Implementation Plan: RSVP to an Event

Branch: 008-rsvp | Date: 2026-03-06 | Spec: spec.md Input: Feature specification from /specs/008-rsvp/spec.md

Summary

Add RSVP functionality to the event detail page. Backend: new POST /api/events/{eventToken}/rsvps endpoint that persists an RSVP (guest name) and returns an rsvpToken. Populates the existing attendeeCount field with real data from a count query. Rejects RSVPs on expired events (409). Frontend: fullscreen event presentation with sticky bottom bar (RSVP CTA or status), bottom sheet with RSVP form (name + submit). localStorage stores rsvpToken and name per event. No account required.

Technical Context

Language/Version: Java 25 (backend), TypeScript 5.9 (frontend) Primary Dependencies: Spring Boot 3.5.x, Vue 3, Vue Router 5, openapi-fetch, openapi-typescript Storage: PostgreSQL (JPA via Spring Data, Liquibase migrations) Testing: JUnit (backend), Vitest (frontend unit), Playwright + MSW (frontend E2E) Target Platform: Self-hosted web application (Docker) Project Type: Web service + SPA Performance Goals: N/A (single-user scale, self-hosted) Constraints: No external resources (CDNs, fonts, tracking), WCAG AA, privacy-first, no PII logging Scale/Scope: New RSVP domain (model + service + controller + persistence), new frontend components (bottom sheet, sticky bar), modified event detail view

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle Status Notes
I. Privacy by Design PASS No PII logged. Only guest-entered name stored. No IP logging. No tracking. Attendee names not exposed publicly (count only). Unprotected endpoint is a conscious privacy trade-off per spec.
II. Test-Driven Methodology PASS TDD enforced: backend unit + integration tests, frontend unit tests, E2E tests. Tests written before implementation.
III. API-First Development PASS OpenAPI spec updated first. New endpoint + schemas with example: fields. Types generated before implementation.
IV. Simplicity & Quality PASS Minimal scope: one POST endpoint, one domain entity, one bottom sheet component. No CAPTCHA, no rate limiting, no edit/withdraw (deferred). Cancelled event guard deferred to US-18.
V. Dependency Discipline PASS No new dependencies. Bottom sheet is CSS + Vue (~50 lines). No UI library.
VI. Accessibility PASS Bottom sheet uses dialog role + aria-modal. Focus trap. ESC to close. Keyboard navigable. WCAG AA contrast via design system.

Post-Phase-1 re-check: All gates still pass. Three token value objects (EventToken, OrganizerToken, RsvpToken) introduced uniformly — justified by spec requirement for type-safe tokens. Refactoring existing Event model to use typed tokens is a mechanical change well-covered by existing tests.

Project Structure

Documentation (this feature)

specs/008-rsvp/
├── plan.md              # This file
├── spec.md              # Feature specification
├── research.md          # Phase 0: research decisions (R-1 through R-8)
├── data-model.md        # Phase 1: Rsvp entity, RsvpToken value object
├── quickstart.md        # Phase 1: implementation overview
├── contracts/
│   └── create-rsvp.yaml # Phase 1: POST endpoint contract
└── tasks.md             # Phase 2: implementation tasks (via /speckit.tasks)

Source Code (repository root)

backend/
├── src/main/java/de/fete/
│   ├── domain/
│   │   ├── model/
│   │   │   ├── Event.java                             # MODIFIED: UUID → EventToken/OrganizerToken
│   │   │   ├── EventToken.java                        # NEW: typed token record
│   │   │   ├── OrganizerToken.java                    # NEW: typed token record
│   │   │   ├── Rsvp.java                              # NEW: RSVP domain entity
│   │   │   └── RsvpToken.java                         # NEW: typed token record
│   │   └── port/
│   │       ├── in/CreateRsvpUseCase.java               # NEW: inbound port
│   │       └── out/RsvpRepository.java                 # NEW: outbound port
│   ├── application/service/
│   │   ├── EventService.java                            # MODIFIED: use typed tokens
│   │   └── RsvpService.java                             # NEW: RSVP business logic
│   ├── adapter/
│   │   ├── in/web/
│   │   │   ├── EventController.java                    # MODIFIED: typed tokens + attendee count + createRsvp()
│   │   │   └── GlobalExceptionHandler.java             # MODIFIED: handle EventExpiredException
│   │   └── out/persistence/
│   │       ├── EventPersistenceAdapter.java            # MODIFIED: map typed tokens
│   │       ├── RsvpJpaEntity.java                      # NEW: JPA entity
│   │       ├── RsvpJpaRepository.java                  # NEW: Spring Data interface
│   │       └── RsvpPersistenceAdapter.java             # NEW: port implementation
├── src/main/resources/
│   ├── openapi/api.yaml                                 # MODIFIED: add RSVP endpoint + schemas
│   └── db/changelog/
│       ├── db.changelog-master.xml                      # MODIFIED: include 003
│       └── 003-create-rsvps-table.xml                   # NEW: rsvps table
└── src/test/java/de/fete/
    ├── application/service/
    │   ├── EventServiceTest.java                         # MODIFIED: use typed tokens
    │   └── RsvpServiceTest.java                          # NEW: unit tests
    └── adapter/in/web/
        └── EventControllerIntegrationTest.java            # MODIFIED: typed tokens + RSVP integration tests

frontend/
├── src/
│   ├── api/schema.d.ts                                  # REGENERATED from OpenAPI
│   ├── components/
│   │   ├── BottomSheet.vue                              # NEW: reusable bottom sheet
│   │   └── RsvpBar.vue                                  # NEW: sticky bottom bar
│   ├── views/EventDetailView.vue                        # MODIFIED: integrate RSVP bar + sheet
│   ├── composables/useEventStorage.ts                   # MODIFIED: add rsvpToken/rsvpName
│   └── assets/main.css                                  # MODIFIED: bottom sheet + bar styles
├── src/views/__tests__/EventDetailView.spec.ts           # MODIFIED: RSVP integration tests
├── src/components/__tests__/
│   ├── BottomSheet.spec.ts                              # NEW: unit tests
│   └── RsvpBar.spec.ts                                  # NEW: unit tests
├── src/composables/__tests__/useEventStorage.spec.ts     # MODIFIED: test new fields
└── e2e/
    └── event-rsvp.spec.ts                               # NEW: E2E tests

Structure Decision: Extends the existing web application structure (backend + frontend). Adds a new RSVP domain following the same hexagonal architecture pattern established in 006-create-event and 007-view-event. Cross-cutting refactoring introduces typed token value objects (EventToken, OrganizerToken, RsvpToken) across all layers. Two new frontend components (BottomSheet, RsvpBar) are the first entries in src/components/ — justified because they're reusable UI primitives, not view-specific markup.

Complexity Tracking

No constitution violations. No entries needed.