# Implementation Plan: Cancel RSVP **Branch**: `014-cancel-rsvp` | **Date**: 2026-03-09 | **Spec**: [spec.md](spec.md) **Input**: Feature specification from `/specs/014-cancel-rsvp/spec.md` ## Summary Allow guests to cancel their RSVP either explicitly from the event detail view (tap-to-reveal pattern on the RSVP bar) or implicitly when removing an RSVP'd event from their event list. The backend provides an idempotent DELETE endpoint; the frontend handles confirmation, API call, localStorage cleanup, and UI state reset. ## 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 + Mockito (backend unit), MockMvc + Testcontainers (backend integration), Vitest (frontend unit), Playwright + MSW (frontend E2E) **Target Platform**: Self-hosted PWA (web browser, mobile-first) **Project Type**: Web application (hexagonal backend + SPA frontend) **Performance Goals**: Cancel operation < 500ms server-side **Constraints**: Privacy by design (no analytics), token-based auth (no login), idempotent delete **Scale/Scope**: Single new endpoint, 3 modified frontend components, 1 new composable method ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* | Principle | Status | Notes | |-----------|--------|-------| | I. Privacy by Design | PASS | No new PII stored. Delete operation removes data. No analytics. | | II. Test-Driven Methodology | PASS | Plan includes TDD cycle: tests before implementation. E2E mandatory. | | III. API-First Development | PASS | OpenAPI spec updated first, types generated before implementation. | | IV. Simplicity & Quality | PASS | Minimal changes to existing code. No new abstractions. Idempotent delete is the simplest correct approach. | | V. Dependency Discipline | PASS | No new dependencies required. | | VI. Accessibility | PASS | Interactive elements will use semantic HTML, ARIA, keyboard navigation. Confirm dialog already accessible. | **Gate result**: ALL PASS — proceed to Phase 0. ## Project Structure ### Documentation (this feature) ```text specs/014-cancel-rsvp/ ├── plan.md # This file ├── research.md # Phase 0 output ├── data-model.md # Phase 1 output ├── contracts/ # Phase 1 output │ └── cancel-rsvp.yaml # DELETE endpoint contract └── tasks.md # Phase 2 output (by /speckit.tasks) ``` ### Source Code (repository root) ```text backend/ ├── src/main/java/de/fete/ │ ├── domain/ │ │ ├── model/Rsvp.java # existing (no changes) │ │ ├── model/RsvpToken.java # existing (no changes) │ │ └── port/ │ │ ├── in/CancelRsvpUseCase.java # NEW use case port │ │ └── out/RsvpRepository.java # MODIFY (add deleteByRsvpToken) │ ├── application/service/RsvpService.java # MODIFY (implement cancel) │ └── adapter/ │ ├── in/web/EventController.java # MODIFY (add DELETE handler) │ └── out/persistence/ │ ├── RsvpJpaRepository.java # MODIFY (add deleteByRsvpToken) │ └── RsvpPersistenceAdapter.java # MODIFY (implement delete) ├── src/main/resources/openapi/api.yaml # MODIFY (add DELETE endpoint) └── src/test/java/de/fete/ ├── application/service/RsvpServiceTest.java # MODIFY (add cancel tests) └── adapter/in/web/EventControllerIntegrationTest.java # MODIFY (add cancel tests) frontend/ ├── src/ │ ├── api/schema.d.ts # REGENERATE (from updated OpenAPI) │ ├── components/ │ │ ├── RsvpBar.vue # MODIFY (tap-to-reveal cancel) │ │ ├── EventList.vue # MODIFY (conditional dialog msg) │ │ └── ConfirmDialog.vue # existing (no changes) │ ├── composables/useEventStorage.ts # MODIFY (add removeRsvp) │ └── views/EventDetailView.vue # MODIFY (add cancel logic) └── e2e/ └── cancel-rsvp.spec.ts # NEW (E2E tests) ``` **Structure Decision**: Follows existing hexagonal architecture. New use case port + implementation in existing service. Single new endpoint added to existing controller. ## Complexity Tracking No constitution violations — table not needed.