Research reports on datetime handling, RFC 9457, font selection. Implementation plans for US-1 create event and post-review fixes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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 |
|
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), matchingWebConfig.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:
- API prefix (line 19): All
@RestControllerbeans are prefixed with/api. So the OpenAPI path/eventsbecomes/api/eventsat runtime. - 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-utilsfor 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.mdfor 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 specificationspec/implementation-phases.md:7— US-1 is first in implementation orderbackend/src/main/resources/openapi/api.yaml:1-38— OpenAPI spec (extension point)backend/src/main/java/de/fete/config/WebConfig.java:19— API prefix/apibackend/src/main/java/de/fete/config/WebConfig.java:23-39— SPA fallback routingbackend/src/main/resources/application.properties:4— JPA ddl-auto=validatebackend/src/main/resources/application.properties:8— Liquibase changelog configbackend/src/main/resources/db/changelog/db.changelog-master.xml:8— Single include, extend herebackend/src/main/resources/db/changelog/000-baseline.xml:8-10— Empty baseline changesetbackend/src/main/resources/application-prod.properties:1-4— DB env varsbackend/src/test/java/de/fete/HexagonalArchitectureTest.java:1-63— Architecture constraintsbackend/src/test/java/de/fete/TestcontainersConfig.java:1-17— Test DB containerfrontend/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).