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>
This commit is contained in:
195
specs/006-create-event/research.md
Normal file
195
specs/006-create-event/research.md
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
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).
|
||||
Reference in New Issue
Block a user