Files
fete/specs/006-create-event/research.md
nitrix 6aeb4b8bca Migrate project artifacts to spec-kit format
- Move cross-cutting docs (personas, design system, implementation phases,
  Ideen.md) to .specify/memory/
- Move cross-cutting research and plans to .specify/memory/research/ and
  .specify/memory/plans/
- Extract 5 setup tasks from spec/setup-tasks.md into individual
  specs/001-005/spec.md files with spec-kit template format
- Extract 20 user stories from spec/userstories.md into individual
  specs/006-026/spec.md files with spec-kit template format
- Relocate feature-specific research and plan docs into specs/[feature]/
- Add spec-kit constitution, templates, scripts, and slash commands
- Slim down CLAUDE.md to Claude-Code-specific config, delegate principles
  to .specify/memory/constitution.md
- Update ralph.sh with stream-json output and per-iteration logging
- Delete old spec/ and docs/agents/ directories
- Gitignore Ralph iteration JSONL logs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:19:41 +01:00

11 KiB

date, git_commit, branch, topic, tags, status
date git_commit branch topic tags status
2026-03-04T21:04:31+00:00 747ed18945 master US-1: Create an Event — Codebase Research
research
codebase
us-1
event-creation
hexagonal-architecture
complete

Research: US-1 — Create an Event

Research Question

What is the current state of the codebase relevant to implementing US-1 (Create an event)? What exists, what infrastructure is in place, and what needs to be built?

Summary

US-1 is the first user story to be implemented. All setup tasks (T-1 through T-5) are complete. The codebase provides a hexagonal architecture skeleton with ArchUnit enforcement, an API-first workflow (OpenAPI spec → generated interfaces + TypeScript types), Liquibase migration tooling with an empty baseline, Testcontainers for integration tests, and a Vue 3 SPA frontend with typed API client. No domain models, use cases, persistence adapters, or controllers exist yet — the entire business logic layer is empty and waiting for US-1.

US-1 Acceptance Criteria (from spec/userstories.md:21-40)

  • Organizer fills in: title (required), description (optional), date/time (required), location (optional), expiry date (required)
  • Server stores event, returns event token (UUID) + organizer token (UUID) in creation response
  • Organizer redirected to event page after creation
  • Organizer token stored in localStorage for organizer access on this device
  • Event token, title, date stored in localStorage for local overview (US-7)
  • No account, login, or personal data required
  • Expiry date is mandatory, cannot be left blank
  • Event not discoverable except via direct link

Dependencies: T-4 (complete).

Detailed Findings

1. Backend Architecture Skeleton

The hexagonal architecture is fully scaffolded but empty. All business-logic packages contain only package-info.java documentation files:

Package Location Status
de.fete.domain.model backend/src/main/java/de/fete/domain/model/ Empty — domain entities go here
de.fete.domain.port.in backend/src/main/java/de/fete/domain/port/in/ Empty — use case interfaces go here
de.fete.domain.port.out backend/src/main/java/de/fete/domain/port/out/ Empty — repository ports go here
de.fete.application.service backend/src/main/java/de/fete/application/service/ Empty — use case implementations go here
de.fete.adapter.in.web backend/src/main/java/de/fete/adapter/in/web/ Empty hand-written code — generated HealthApi interface exists in target/
de.fete.adapter.out.persistence backend/src/main/java/de/fete/adapter/out/persistence/ Empty — JPA entities + Spring Data repos go here

Architecture constraints are enforced by ArchUnit (HexagonalArchitectureTest.java:1-63):

  • Domain layer must not depend on adapters, application, config, or Spring
  • Inbound and outbound ports must be interfaces
  • Web adapter and persistence adapter must not depend on each other
  • Onion architecture layers validated via onionArchitecture() rule

2. OpenAPI Spec — Current State and Extension Point

The OpenAPI spec at backend/src/main/resources/openapi/api.yaml:1-38 currently defines only the health check endpoint. US-1 requires adding:

  • New path: POST /events — create event endpoint
  • New schemas: Request body (title, description, dateTime, location, expiryDate) and response (eventToken, organizerToken)
  • Error responses: RFC 9457 Problem Details format (see docs/agents/research/2026-03-04-rfc9457-problem-details.md)
  • Server base: Already set to /api (line 11), matching WebConfig.java:19

Generated code lands in target/generated-sources/openapi/:

  • Interfaces: de.fete.adapter.in.web.api — controller must implement generated interface
  • Models: de.fete.adapter.in.web.model — request/response DTOs

Frontend types are generated via npm run generate:api into frontend/src/api/schema.d.ts.

3. Web Configuration

WebConfig.java:1-41 configures two things relevant to US-1:

  1. API prefix (line 19): All @RestController beans are prefixed with /api. So the OpenAPI path /events becomes /api/events at runtime.
  2. SPA fallback (lines 23-39): Any non-API, non-static-asset request falls through to index.html. This means Vue Router handles client-side routes like /events/:token.

4. Database Infrastructure

Liquibase is configured in application.properties:8:

spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.xml

The master changelog (db.changelog-master.xml:1-10) includes a single empty baseline (000-baseline.xml:1-13). US-1 needs a new migration file (e.g. 001-create-event-table.xml) added to the master changelog.

JPA is configured with ddl-auto=validate (application.properties:4), meaning Hibernate validates entity mappings against the schema but never auto-creates tables. Liquibase is the sole schema management tool.

PostgreSQL connection is externalized via environment variables in application-prod.properties:1-4:

spring.datasource.url=${DATABASE_URL}
spring.datasource.username=${DATABASE_USERNAME}
spring.datasource.password=${DATABASE_PASSWORD}

5. Test Infrastructure

Backend:

  • JUnit 5 + Spring Boot Test + MockMvc (see FeteApplicationTest.java)
  • Testcontainers PostgreSQL (TestcontainersConfig.java:1-17) — real database for integration tests
  • ArchUnit for architecture validation
  • Checkstyle (Google Checks) and SpotBugs configured as build plugins

Frontend:

  • Vitest with jsdom environment (vitest.config.ts)
  • @vue/test-utils for component testing
  • Single placeholder test exists (HelloWorld.spec.ts)
  • Test pattern: src/**/__tests__/*.spec.ts

6. Frontend — Router, API Client, and localStorage

Router (frontend/src/router/index.ts:1-23): Currently has two placeholder routes (/ and /about). US-1 needs:

  • A route for the event creation form (e.g. /create)
  • A route for the event page (e.g. /events/:token) — needed for post-creation redirect

API client (frontend/src/api/client.ts:1-4): Singleton openapi-fetch client typed against generated schema. Base URL /api. Ready for use — just needs the new endpoints in the generated types.

localStorage: No utilities exist yet. The composables/ directory contains only .gitkeep. US-1 needs:

  • A composable or utility for storing/retrieving organizer tokens per event
  • Storage of event token, title, and date for the local overview (US-7)

Components: Only Vue/Vite scaffold defaults (HelloWorld, TheWelcome, icons). All need to be replaced with the actual event creation form.

7. Token Model

The spec defines three token types (userstories.md:12-18):

  • Event token: Public UUID v4 in the event URL. Used by guests to access event pages.
  • Organizer token: Secret UUID v4 stored in localStorage. Used to authenticate organizer actions.
  • Internal DB ID: Never exposed — implementation detail only.

UUID v4 (random) is used for both tokens. KISS — no time-ordering (v7) needed for this use case. Generated server-side via java.util.UUID.randomUUID().

8. Cross-Cutting Concerns

  • Date/time handling: See docs/agents/research/2026-03-04-datetime-best-practices.md for the full stack-wide type mapping. Event dateTime → OffsetDateTime / timestamptz. Expiry date → LocalDate / date.
  • Error responses: RFC 9457 Problem Details format. See docs/agents/research/2026-03-04-rfc9457-problem-details.md.
  • Honeypot fields: Removed from scope — overengineered for this project.

Code References

  • spec/userstories.md:21-40 — US-1 full specification
  • spec/implementation-phases.md:7 — US-1 is first in implementation order
  • backend/src/main/resources/openapi/api.yaml:1-38 — OpenAPI spec (extension point)
  • backend/src/main/java/de/fete/config/WebConfig.java:19 — API prefix /api
  • backend/src/main/java/de/fete/config/WebConfig.java:23-39 — SPA fallback routing
  • backend/src/main/resources/application.properties:4 — JPA ddl-auto=validate
  • backend/src/main/resources/application.properties:8 — Liquibase changelog config
  • backend/src/main/resources/db/changelog/db.changelog-master.xml:8 — Single include, extend here
  • backend/src/main/resources/db/changelog/000-baseline.xml:8-10 — Empty baseline changeset
  • backend/src/main/resources/application-prod.properties:1-4 — DB env vars
  • backend/src/test/java/de/fete/HexagonalArchitectureTest.java:1-63 — Architecture constraints
  • backend/src/test/java/de/fete/TestcontainersConfig.java:1-17 — Test DB container
  • frontend/src/router/index.ts:1-23 — Vue Router (extend with event routes)
  • frontend/src/api/client.ts:1-4 — API client (ready to use with generated types)
  • frontend/src/composables/.gitkeep — Empty composables directory

Architecture Documentation

Hexagonal Layer Mapping for US-1

Layer Package US-1 Artifacts
Domain Model de.fete.domain.model Event entity (title, description, dateTime, location, expiryDate, eventToken, organizerToken, createdAt)
Inbound Port de.fete.domain.port.in CreateEventUseCase interface
Outbound Port de.fete.domain.port.out EventRepository interface (save, findByToken)
Application Service de.fete.application.service EventService implementing CreateEventUseCase
Web Adapter de.fete.adapter.in.web Controller implementing generated EventsApi interface
Persistence Adapter de.fete.adapter.out.persistence JPA entity + Spring Data repository implementing EventRepository port
Config de.fete.config (existing WebConfig sufficient)

API-First Flow

api.yaml (edit) → mvn compile → HealthApi.java + EventsApi.java (generated)
                                 HealthResponse.java + CreateEventRequest.java + CreateEventResponse.java (generated)
                → npm run generate:api → schema.d.ts (generated TypeScript types)

The hand-written controller in adapter.in.web implements the generated interface. The frontend uses the generated types via openapi-fetch.

Database Schema Required

US-1 needs a single events table with columns mapping to the domain model. The migration file goes into db/changelog/ and must be included in db.changelog-master.xml.

Frontend Data Flow

EventCreateForm.vue → api.post('/events', body) → backend
                    ← { eventToken, organizerToken }
                    → localStorage.setItem (organizer token, event meta)
                    → router.push(`/events/${eventToken}`)

Resolved Questions

  • Expiry date validation at creation: Yes — the server enforces that the expiry date is in the future at creation time, not only at edit time (US-5). Rationale: an event should never exist in an invalid state. If it's never edited, a past expiry date would be nonsensical. This extends US-1 AC7 beyond "mandatory" to "mandatory and in the future".
  • Event page after creation: Option A — create a minimal stub route (/events/:token) with a placeholder view (e.g. "Event created" confirmation). The full event page is built in US-2. This keeps story boundaries clean while satisfying US-1 AC3 (redirect after creation).