--- date: 2026-03-04T21:04:31+00:00 git_commit: 747ed189456d2328147051bb8e7b3bbb43f47ea6 branch: master topic: "US-1: Create an Event — Codebase Research" tags: [research, codebase, us-1, event-creation, hexagonal-architecture] status: 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).