Add agent research and implementation plan docs for US-1
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>
This commit is contained in:
107
docs/agents/research/2026-03-04-datetime-best-practices.md
Normal file
107
docs/agents/research/2026-03-04-datetime-best-practices.md
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
date: 2026-03-04T21:15:50+00:00
|
||||
git_commit: b8421274b47c6d1778b83c6b0acb70fd82891e71
|
||||
branch: master
|
||||
topic: "Date/Time Handling Best Practices for the fete Stack"
|
||||
tags: [research, datetime, java, postgresql, openapi, typescript]
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Research: Date/Time Handling Best Practices
|
||||
|
||||
## Research Question
|
||||
|
||||
What are the best practices for handling dates and times across the full fete stack (Java 25 / Spring Boot 3.5.x / PostgreSQL / OpenAPI 3.1 / Vue 3 / TypeScript)?
|
||||
|
||||
## Summary
|
||||
|
||||
The project has two distinct date/time concepts: **event date/time** (when something happens) and **expiry date** (after which data is deleted). These map to different types at every layer. The recommendations align Java types, PostgreSQL column types, OpenAPI formats, and TypeScript representations into a consistent stack-wide approach.
|
||||
|
||||
## Detailed Findings
|
||||
|
||||
### Type Mapping Across the Stack
|
||||
|
||||
| Concept | Java | PostgreSQL | OpenAPI | TypeScript | Example |
|
||||
|---------|------|------------|---------|------------|---------|
|
||||
| Event date/time | `OffsetDateTime` | `timestamptz` | `string`, `format: date-time` | `string` | `2026-03-15T20:00:00+01:00` |
|
||||
| Expiry date | `LocalDate` | `date` | `string`, `format: date` | `string` | `2026-06-15` |
|
||||
| Audit timestamps (createdAt, etc.) | `OffsetDateTime` | `timestamptz` | `string`, `format: date-time` | `string` | `2026-03-04T14:22:00Z` |
|
||||
|
||||
### Event Date/Time: `OffsetDateTime` + `timestamptz`
|
||||
|
||||
**Why `OffsetDateTime`, not `LocalDateTime`:**
|
||||
|
||||
- PostgreSQL best practice explicitly recommends `timestamptz` over `timestamp` — the PostgreSQL wiki says ["don't use `timestamp`"](https://wiki.postgresql.org/wiki/Don't_Do_This). `timestamptz` maps naturally to `OffsetDateTime`.
|
||||
- Hibernate 6 (Spring Boot 3.5.x) has native `OffsetDateTime` ↔ `timestamptz` support. `LocalDateTime` requires extra care to avoid silent timezone bugs at the JDBC driver level.
|
||||
- An ISO 8601 string with offset (`2026-03-15T20:00:00+01:00`) is unambiguous in the API. A bare `LocalDateTime` string forces the client to guess the timezone.
|
||||
- The OpenAPI `date-time` format and `openapi-generator` default to `OffsetDateTime` in Java — no custom type mappings needed.
|
||||
|
||||
**Why not `ZonedDateTime`:** Carries IANA zone IDs (e.g. `Europe/Berlin`) which add complexity without value for this use case. Worse JDBC support than `OffsetDateTime`.
|
||||
|
||||
**How PostgreSQL stores it:** `timestamptz` does **not** store the timezone. It converts input to UTC and stores UTC. On retrieval, it converts to the session's timezone setting. The offset is preserved in the Java `OffsetDateTime` via the JDBC driver.
|
||||
|
||||
**Practical flow:** The frontend sends the offset based on the organizer's browser locale. The server stores UTC. Display-side conversion happens in the frontend.
|
||||
|
||||
### Expiry Date: `LocalDate` + `date`
|
||||
|
||||
The expiry date is a calendar-date concept ("after which day should data be deleted"), not a point-in-time. A cleanup job runs periodically and deletes events where `expiryDate < today`. Sub-day precision adds no value and complicates the UX.
|
||||
|
||||
### Jackson Serialization (Spring Boot 3.5.x)
|
||||
|
||||
Spring Boot 3.x auto-configures `jackson-datatype-jsr310` (JavaTimeModule) and disables `WRITE_DATES_AS_TIMESTAMPS` by default:
|
||||
|
||||
- `OffsetDateTime` serializes to `"2026-03-15T20:00:00+01:00"` (ISO 8601 string)
|
||||
- `LocalDate` serializes to `"2026-06-15"`
|
||||
|
||||
No additional configuration needed. For explicitness, can add to `application.properties`:
|
||||
```properties
|
||||
spring.jackson.serialization.write-dates-as-timestamps=false
|
||||
```
|
||||
|
||||
### Hibernate 6 Configuration
|
||||
|
||||
With Hibernate 6, `OffsetDateTime` maps to `timestamptz` using the `NATIVE` timezone storage strategy by default on PostgreSQL. Can be made explicit:
|
||||
|
||||
```properties
|
||||
spring.jpa.properties.hibernate.timezone.default_storage=NATIVE
|
||||
```
|
||||
|
||||
This tells Hibernate to use the database's native `TIMESTAMP WITH TIME ZONE` type directly.
|
||||
|
||||
### OpenAPI Schema Definitions
|
||||
|
||||
```yaml
|
||||
# Event date/time
|
||||
eventDateTime:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2026-03-15T20:00:00+01:00"
|
||||
|
||||
# Expiry date
|
||||
expiryDate:
|
||||
type: string
|
||||
format: date
|
||||
example: "2026-06-15"
|
||||
```
|
||||
|
||||
**Code-generation mapping (defaults, no customization needed):**
|
||||
|
||||
| OpenAPI format | Java type (openapi-generator) | TypeScript type (openapi-typescript) |
|
||||
|---------------|-------------------------------|--------------------------------------|
|
||||
| `date-time` | `java.time.OffsetDateTime` | `string` |
|
||||
| `date` | `java.time.LocalDate` | `string` |
|
||||
|
||||
### Frontend (TypeScript)
|
||||
|
||||
`openapi-typescript` generates `string` for both `format: date-time` and `format: date`. This is correct — JSON has no native date type, so dates travel as strings. Parsing to `Date` objects happens explicitly at the application boundary when needed (e.g. for display formatting).
|
||||
|
||||
## Sources
|
||||
|
||||
- [PostgreSQL Wiki: Don't Do This](https://wiki.postgresql.org/wiki/Don't_Do_This) — recommends `timestamptz` over `timestamp`
|
||||
- [PostgreSQL Docs: Date/Time Types](https://www.postgresql.org/docs/current/datatype-datetime.html)
|
||||
- [Thorben Janssen: Hibernate 6 OffsetDateTime and ZonedDateTime](https://thorben-janssen.com/hibernate-6-offsetdatetime-and-zoneddatetime/)
|
||||
- [Baeldung: OffsetDateTime Serialization With Jackson](https://www.baeldung.com/java-jackson-offsetdatetime)
|
||||
- [Baeldung: Map Date Types With OpenAPI Generator](https://www.baeldung.com/openapi-map-date-types)
|
||||
- [Baeldung: ZonedDateTime vs OffsetDateTime](https://www.baeldung.com/java-zoneddatetime-offsetdatetime)
|
||||
- [Reflectoring: Handling Timezones in Spring Boot](https://reflectoring.io/spring-timezones/)
|
||||
- [openapi-typescript documentation](https://openapi-ts.dev/)
|
||||
202
docs/agents/research/2026-03-04-rfc9457-problem-details.md
Normal file
202
docs/agents/research/2026-03-04-rfc9457-problem-details.md
Normal file
@@ -0,0 +1,202 @@
|
||||
---
|
||||
date: 2026-03-04T21:15:50+00:00
|
||||
git_commit: b8421274b47c6d1778b83c6b0acb70fd82891e71
|
||||
branch: master
|
||||
topic: "RFC 9457 Problem Details for HTTP API Error Responses"
|
||||
tags: [research, error-handling, rfc9457, spring-boot, openapi]
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Research: RFC 9457 Problem Details
|
||||
|
||||
## Research Question
|
||||
|
||||
How should the fete API structure error responses? What does RFC 9457 (Problem Details) specify, and how does it integrate with Spring Boot 3.5.x, OpenAPI 3.1, and openapi-fetch?
|
||||
|
||||
## Summary
|
||||
|
||||
RFC 9457 (successor to RFC 7807) defines a standard JSON format (`application/problem+json`) for machine-readable HTTP API errors. Spring Boot 3.x has first-class support via `ProblemDetail`, `ErrorResponseException`, and `ResponseEntityExceptionHandler`. The recommended approach is a single `@RestControllerAdvice` that handles all exceptions consistently — no `spring.mvc.problemdetails.enabled` property, no fallback to legacy error format.
|
||||
|
||||
## Detailed Findings
|
||||
|
||||
### RFC 9457 Format
|
||||
|
||||
Standard fields:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `type` | URI | Identifies the problem type. Defaults to `about:blank`. |
|
||||
| `title` | string | Short, human-readable summary. Should not change between occurrences. |
|
||||
| `status` | int | HTTP status code. |
|
||||
| `detail` | string | Human-readable explanation specific to this occurrence. |
|
||||
| `instance` | URI | Identifies the specific occurrence (e.g. correlation ID). |
|
||||
|
||||
Extension members (additional JSON properties) are explicitly permitted. This is the mechanism for validation errors, error codes, etc.
|
||||
|
||||
**Key rule:** With `type: "about:blank"`, the `title` must match the HTTP status phrase exactly. Use a custom `type` URI when providing a custom `title`.
|
||||
|
||||
### Spring Boot 3.x Built-in Support
|
||||
|
||||
- **`ProblemDetail`** — container class for the five standard fields + a `properties` Map for extensions.
|
||||
- **`ErrorResponseException`** — base class for custom exceptions that carry their own `ProblemDetail`.
|
||||
- **`ResponseEntityExceptionHandler`** — `@ControllerAdvice` base class that handles all Spring MVC exceptions and renders them as `application/problem+json`.
|
||||
- **`ProblemDetailJacksonMixin`** — automatically unwraps the `properties` Map as top-level JSON fields during serialization.
|
||||
|
||||
### Recommended Configuration
|
||||
|
||||
Use a single `@RestControllerAdvice` extending `ResponseEntityExceptionHandler`. Do **not** use the `spring.mvc.problemdetails.enabled` property.
|
||||
|
||||
```java
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
// All Spring MVC exceptions are handled automatically.
|
||||
// Add @ExceptionHandler methods for domain exceptions here.
|
||||
// Add a catch-all for Exception.class to prevent legacy error format.
|
||||
}
|
||||
```
|
||||
|
||||
Reasons to avoid the property-based approach:
|
||||
1. No place to add custom `@ExceptionHandler` methods.
|
||||
2. Having both the property AND a custom `ResponseEntityExceptionHandler` bean causes a conflict.
|
||||
3. The property ignores `server.error.include-*` properties.
|
||||
|
||||
### Validation Errors (Field-Level)
|
||||
|
||||
Spring deliberately does **not** include field-level validation errors in `ProblemDetail` by default (security rationale). Override `handleMethodArgumentNotValid`:
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleMethodArgumentNotValid(
|
||||
MethodArgumentNotValidException ex,
|
||||
HttpHeaders headers,
|
||||
HttpStatusCode status,
|
||||
WebRequest request) {
|
||||
|
||||
ProblemDetail problemDetail = ex.getBody();
|
||||
problemDetail.setTitle("Validation Failed");
|
||||
problemDetail.setType(URI.create("urn:problem-type:validation-error"));
|
||||
|
||||
List<Map<String, String>> fieldErrors = ex.getBindingResult()
|
||||
.getFieldErrors()
|
||||
.stream()
|
||||
.map(fe -> Map.of(
|
||||
"field", fe.getField(),
|
||||
"message", fe.getDefaultMessage()
|
||||
))
|
||||
.toList();
|
||||
|
||||
problemDetail.setProperty("fieldErrors", fieldErrors);
|
||||
return handleExceptionInternal(ex, problemDetail, headers, status, request);
|
||||
}
|
||||
```
|
||||
|
||||
Resulting response:
|
||||
```json
|
||||
{
|
||||
"type": "urn:problem-type:validation-error",
|
||||
"title": "Validation Failed",
|
||||
"status": 400,
|
||||
"detail": "Invalid request content.",
|
||||
"instance": "/api/events",
|
||||
"fieldErrors": [
|
||||
{ "field": "title", "message": "must not be blank" },
|
||||
{ "field": "expiryDate", "message": "must be a future date" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### OpenAPI Schema Definition
|
||||
|
||||
```yaml
|
||||
components:
|
||||
schemas:
|
||||
ProblemDetail:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
format: uri
|
||||
default: "about:blank"
|
||||
title:
|
||||
type: string
|
||||
status:
|
||||
type: integer
|
||||
detail:
|
||||
type: string
|
||||
instance:
|
||||
type: string
|
||||
format: uri
|
||||
additionalProperties: true
|
||||
|
||||
ValidationProblemDetail:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/ProblemDetail'
|
||||
- type: object
|
||||
properties:
|
||||
fieldErrors:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
field:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
required:
|
||||
- field
|
||||
- message
|
||||
|
||||
responses:
|
||||
BadRequest:
|
||||
description: Validation failed
|
||||
content:
|
||||
application/problem+json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationProblemDetail'
|
||||
NotFound:
|
||||
description: Resource not found
|
||||
content:
|
||||
application/problem+json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetail'
|
||||
```
|
||||
|
||||
Use media type `application/problem+json` in response definitions. Set `additionalProperties: true` on the base schema.
|
||||
|
||||
### Frontend Consumption (openapi-fetch)
|
||||
|
||||
openapi-fetch uses a discriminated union for responses:
|
||||
|
||||
```typescript
|
||||
const { data, error } = await client.POST('/api/events', { body: eventData })
|
||||
|
||||
if (error) {
|
||||
// `error` is typed from the OpenAPI error response schema
|
||||
console.log(error.title) // "Validation Failed"
|
||||
console.log(error.fieldErrors) // [{ field: "title", message: "..." }]
|
||||
return
|
||||
}
|
||||
|
||||
// `data` is the typed success response
|
||||
```
|
||||
|
||||
The `error` object is already typed from the generated schema — no manual type assertions needed for defined error shapes.
|
||||
|
||||
### Known Pitfalls
|
||||
|
||||
| Pitfall | Description | Mitigation |
|
||||
|---------|-------------|------------|
|
||||
| **Inconsistent formats** | Exceptions escaping to Spring Boot's `BasicErrorController` return legacy format (`timestamp`, `error`, `path`), not Problem Details. | Add a catch-all `@ExceptionHandler(Exception.class)` in the `@RestControllerAdvice`. |
|
||||
| **`server.error.include-*` ignored** | When Problem Details is active, these properties have no effect. | Control content via `ProblemDetail` directly. |
|
||||
| **Validation errors hidden by default** | Spring returns only `"Invalid request content."` without field details. | Override `handleMethodArgumentNotValid` explicitly. |
|
||||
| **Content negotiation** | `application/problem+json` is only returned when the client accepts it. `openapi-fetch` sends `Accept: application/json` which Spring considers compatible. | No action needed for SPA clients. |
|
||||
| **`about:blank` semantics** | With `type: "about:blank"`, `title` must match the HTTP status phrase. Custom titles require a custom `type` URI. | Use `urn:problem-type:*` URIs for custom problem types. |
|
||||
|
||||
## Sources
|
||||
|
||||
- [RFC 9457 Full Text](https://www.rfc-editor.org/rfc/rfc9457.html)
|
||||
- [Spring Framework Docs: Error Responses](https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-ann-rest-exceptions.html)
|
||||
- [Swagger Blog: Problem Details RFC 9457](https://swagger.io/blog/problem-details-rfc9457-doing-api-errors-well/)
|
||||
- [Baeldung: Returning Errors Using ProblemDetail](https://www.baeldung.com/spring-boot-return-errors-problemdetail)
|
||||
- [SivaLabs: Spring Boot 3 Error Reporting](https://www.sivalabs.in/blog/spring-boot-3-error-reporting-using-problem-details/)
|
||||
- [Spring Boot Issue #43850: Render global errors as Problem Details](https://github.com/spring-projects/spring-boot/issues/43850)
|
||||
404
docs/agents/research/2026-03-04-sans-serif-fonts.md
Normal file
404
docs/agents/research/2026-03-04-sans-serif-fonts.md
Normal file
@@ -0,0 +1,404 @@
|
||||
# Research: Modern Sans-Serif Fonts for Mobile-First PWA
|
||||
|
||||
**Date:** 2026-03-04
|
||||
**Context:** Selecting a primary typeface for fete, a privacy-focused PWA for event announcements and RSVPs. The font must be open-source with permissive licensing, modern geometric/neo-grotesque style, excellent mobile readability, and strong weight range.
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Based on research of 9 candidate fonts, **6 meet all requirements** for self-hosting and redistribution under permissive licenses. Two do not qualify:
|
||||
|
||||
- **General Sans**: Proprietary (ITF Free Font License, non-commercial personal use only)
|
||||
- **Satoshi**: License ambiguity; sources conflict between full OFL and ITF restrictions
|
||||
|
||||
The remaining **6 fonts are fully open-source** and suitable for the project:
|
||||
|
||||
| Font | License | Design | Weights | Status |
|
||||
|------|---------|--------|---------|--------|
|
||||
| Inter | OFL-1.1 | Neo-grotesque, humanist | 9 (Thin–Black) | ✅ Recommended |
|
||||
| Plus Jakarta Sans | OFL-1.1 | Geometric, modern | 7 (ExtraLight–ExtraBold) | ✅ Recommended |
|
||||
| Outfit | OFL-1.1 | Geometric | 9 (Thin–Black) | ✅ Recommended |
|
||||
| Space Grotesk | OFL-1.1 | Neo-grotesque, distinctive | 5 (Light–Bold) | ✅ Recommended |
|
||||
| Manrope | OFL-1.1 | Geometric, humanist | 7 (ExtraLight–ExtraBold) | ✅ Recommended |
|
||||
| DM Sans | OFL-1.1 | Geometric, low-contrast | 9 (Thin–Black) | ✅ Recommended |
|
||||
| Sora | OFL-1.1 | Geometric | 8 (Thin–ExtraBold) | ✅ Recommended |
|
||||
|
||||
---
|
||||
|
||||
## Detailed Candidate Analysis
|
||||
|
||||
### 1. Inter
|
||||
|
||||
**License:** SIL Open Font License 1.1 (OFL-1.1)
|
||||
|
||||
**Download Location:**
|
||||
- **Official:** https://github.com/rsms/inter (releases page)
|
||||
- **NPM:** `inter-ui` package
|
||||
- **Homebrew:** `font-inter`
|
||||
- **Official CDN:** https://rsms.me/inter/inter.css
|
||||
|
||||
**Design Character:** Neo-grotesque with humanist touches. High x-height for enhanced legibility on screens. Geometric letterforms with open apertures. Designed specifically for UI and on-screen use.
|
||||
|
||||
**Available Weights:** 9 weights from Thin (100) to Black (900), each with italic variant. Also available as a variable font with weight axis.
|
||||
|
||||
**Notable Apps/Products:**
|
||||
- **UX/Design tools:** Figma, Notion, Pixar Presto
|
||||
- **OS:** Elementary OS, GNOME
|
||||
- **Web:** GitLab, ISO, Mozilla, NASA
|
||||
- **Why:** Chosen by product teams valuing clarity and modern minimalism; default choice for UI designers
|
||||
|
||||
**Mobile Suitability:** Excellent. Specifically engineered for screen readability with high x-height and open apertures. Performs well at 14–16px body text.
|
||||
|
||||
**Distinctive Strengths:**
|
||||
- Purpose-built for digital interfaces
|
||||
- Exceptional clarity in dense UI layouts
|
||||
- Strong brand identity (recognizable across tech products)
|
||||
- Extensive OpenType features
|
||||
|
||||
**Weakness:** Very widely used; less distinctive for a bold brand identity. Considered the "safe" choice.
|
||||
|
||||
---
|
||||
|
||||
### 2. Plus Jakarta Sans
|
||||
|
||||
**License:** SIL Open Font License 1.1 (OFL-1.1)
|
||||
|
||||
**Download Location:**
|
||||
- **Official Repository:** https://github.com/tokotype/PlusJakartaSans
|
||||
- **Source Files:** `sources/`, compiled fonts in `fonts/` directory
|
||||
- **Designer Contact:** mail@tokotype.com (Gumpita Rahayu, Tokotype)
|
||||
- **Latest Version:** 2.7.1 (May 2023)
|
||||
- **Build Command:** `gftools builder sources/builder.yaml`
|
||||
|
||||
**Design Character:** Geometric sans-serif with modern, clean-cut forms. Inspired by Neuzeit Grotesk and Futura but with contemporary refinement. Slightly taller x-height for clear spacing between caps and lowercase. Open counters and balanced spacing for legibility across sizes. **Bold, distinctive look** with personality.
|
||||
|
||||
**Available Weights:** 7 weights from ExtraLight (200) to ExtraBold (800), with matching italics.
|
||||
|
||||
**Notable Apps/Products:**
|
||||
- Original commission: Jakarta Provincial Government's "+Jakarta City of Collaboration" program (2020)
|
||||
- Now widely used in: Branding projects, modern web design, UI design
|
||||
- **Why:** Chosen for fresh, contemporary feel without generic blandness
|
||||
|
||||
**Mobile Suitability:** Excellent. Designed with mobile UI in mind. Clean letterforms render crisply on small screens.
|
||||
|
||||
**Distinctive Strengths:**
|
||||
- **Stylistic sets:** Sharp, Straight, and Swirl variants add design flexibility
|
||||
- Modern geometric with Indonesian design heritage (unique perspective)
|
||||
- Excellent for branding (not generic like Inter)
|
||||
- OpenType features for sophisticated typography
|
||||
- Well-maintained, active development
|
||||
|
||||
**Weakness:** Less ubiquitous than Inter; smaller ecosystem of design tool integrations.
|
||||
|
||||
---
|
||||
|
||||
### 3. Outfit
|
||||
|
||||
**License:** SIL Open Font License 1.1 (OFL-1.1)
|
||||
|
||||
**Download Location:**
|
||||
- **Official Repository:** https://github.com/Outfitio/Outfit-Fonts
|
||||
- **Fonts Directory:** `/fonts` in repository
|
||||
- **OFL Text:** `OFL.txt` in repository
|
||||
- **Designer:** Rodrigo Fuenzalida (originally for Outfit.io)
|
||||
- **Status:** Repository archived Feb 25, 2025 (read-only, downloads remain accessible)
|
||||
|
||||
**Design Character:** Geometric sans-serif with warm, friendly appearance. Generous x-height, balanced spacing, low contrast. Nine static weights plus variable font with weight axis.
|
||||
|
||||
**Available Weights:** 9 weights from Thin (100) to Black (900). No italics.
|
||||
|
||||
**Notable Apps/Products:**
|
||||
- Originally created for Outfit.io platform
|
||||
- Good readability for body text (≈16px) and strong headline presence
|
||||
- Used in design tools (Figma integration)
|
||||
|
||||
**Mobile Suitability:** Good. Geometric forms and generous spacing work well on mobile, though low contrast may require careful pairing with sufficient color contrast.
|
||||
|
||||
**Distinctive Strengths:**
|
||||
- Full weight range (Thin–Black)
|
||||
- Variable font option for granular weight control
|
||||
- Stylistic alternates and rare ligatures
|
||||
- Accessible character set
|
||||
|
||||
**Weakness:** Archived repository; no active development. Low contrast design requires careful color/contrast pairing for accessibility.
|
||||
|
||||
---
|
||||
|
||||
### 4. Space Grotesk
|
||||
|
||||
**License:** SIL Open Font License 1.1 (OFL-1.1)
|
||||
|
||||
**Download Location:**
|
||||
- **Official Repository:** https://github.com/floriankarsten/space-grotesk
|
||||
- **Official Site:** https://fonts.floriankarsten.com/space-grotesk
|
||||
- **Designer:** Florian Karsten
|
||||
- **Variants:** Variable font with weight axis
|
||||
|
||||
**Design Character:** Neo-grotesque with distinctive personality. Proportional variant of Space Mono (Colophon Foundry, 2016). Retains Space Mono's idiosyncratic details while optimizing for improved readability. Bold, tech-forward aesthetic with monowidth heritage visible in character design.
|
||||
|
||||
**Available Weights:** 5 weights—Light (300), Regular (400), Medium (500), SemiBold (600), Bold (700). No italics.
|
||||
|
||||
**Notable Apps/Products:**
|
||||
- Modern tech companies and startups seeking distinctive branding
|
||||
- Popular in neo-brutalist web design
|
||||
- Good for headlines and display use
|
||||
|
||||
**Mobile Suitability:** Good. Clean proportional forms with distinctive character. Works well for headlines; body text at 14px+ is readable.
|
||||
|
||||
**Distinctive Strengths:**
|
||||
- **Bold, tech-forward personality** — immediately recognizable
|
||||
- Heritage from Space Mono adds character without looking dated
|
||||
- Excellent OpenType support (old-style figures, tabular figures, superscript, subscript, fractions, stylistic alternates)
|
||||
- **Supports extended language coverage:** Latin, Vietnamese, Pinyin, Central/South-Eastern European
|
||||
|
||||
**Weakness:** Only 5 weights (lightest is 300, no Thin). Fewer weight options than Inter or DM Sans.
|
||||
|
||||
---
|
||||
|
||||
### 5. Manrope
|
||||
|
||||
**License:** SIL Open Font License 1.1 (OFL-1.1)
|
||||
|
||||
**Download Location:**
|
||||
- **Official Repository:** https://github.com/sharanda/manrope
|
||||
- **Designer:** Mikhail Sharanda (2018), converted to variable by Mirko Velimirovic (2019)
|
||||
- **Alternative Sources:** Multiple community forks on GitHub, npm packages
|
||||
- **NPM Package:** `@fontsource/manrope`, `@fontsource-variable/manrope`
|
||||
|
||||
**Design Character:** Modern geometric sans-serif blending geometric shapes with humanistic elements. Semi-condensed structure with clean, contemporary feel. Geometric digits, packed with OpenType features.
|
||||
|
||||
**Available Weights:** 7 weights from ExtraLight (200) to ExtraBold (800). Available as variable font.
|
||||
|
||||
**Notable Apps/Products:**
|
||||
- Widely used in modern design systems
|
||||
- Popular in product/SaaS design
|
||||
- Good for both UI and branding
|
||||
|
||||
**Mobile Suitability:** Excellent. Clean geometric design with humanistic touches; balanced proportions work well on mobile.
|
||||
|
||||
**Distinctive Strengths:**
|
||||
- Geometric + humanistic blend (best of both worlds)
|
||||
- Well-maintained active project
|
||||
- Variable font available
|
||||
- Strong design community around the font
|
||||
|
||||
**Weakness:** None significant; solid all-around choice.
|
||||
|
||||
---
|
||||
|
||||
### 6. DM Sans
|
||||
|
||||
**License:** SIL Open Font License 1.1 (OFL-1.1)
|
||||
|
||||
**Download Location:**
|
||||
- **Official Repository:** https://github.com/googlefonts/dm-fonts
|
||||
- **Releases Page:** https://github.com/googlefonts/dm-fonts/releases
|
||||
- **Google Fonts:** https://fonts.google.com/specimen/DM+Sans
|
||||
- **Design:** Commissioned from Colophon Foundry; Creative Direction: MultiAdaptor & DeepMind
|
||||
|
||||
**Design Character:** Low-contrast geometric sans-serif optimized for text at smaller sizes. Part of the DM suite (DM Sans, DM Serif Text, DM Serif Display). Designed for clarity and efficiency in dense typography.
|
||||
|
||||
**Available Weights:** 9 weights from Thin (100) to Black (900), each with italic variant.
|
||||
|
||||
**Notable Apps/Products:**
|
||||
- DeepMind products (by commission)
|
||||
- Tech companies favoring geometric clarity
|
||||
- Professional and commercial products requiring text legibility
|
||||
|
||||
**Mobile Suitability:** Excellent. Specifically optimized for small text sizes; low contrast minimizes visual noise on mobile screens.
|
||||
|
||||
**Distinctive Strengths:**
|
||||
- **Optimized for small text** — superior at 12–14px
|
||||
- Full weight range (Thin–Black)
|
||||
- Active Google Fonts maintenance
|
||||
- Italic variants (unlike Outfit or Space Grotesk)
|
||||
- Commissioned by reputable team (DeepMind)
|
||||
|
||||
**Weakness:** Low contrast may feel less bold on headlines without careful sizing/weight adjustment.
|
||||
|
||||
---
|
||||
|
||||
### 7. Sora
|
||||
|
||||
**License:** SIL Open Font License 1.1 (OFL-1.1)
|
||||
|
||||
**Download Location:**
|
||||
- **Official Repository:** https://github.com/sora-xor/sora-font
|
||||
- **GitHub Releases:** Direct TTF/OTF downloads available
|
||||
- **NPM Packages:** `@fontsource/sora`, `@fontsource-variable/sora`
|
||||
- **Original Purpose:** Custom typeface for SORA decentralized autonomous economy
|
||||
|
||||
**Design Character:** Geometric sans-serif with contemporary, clean aesthetic. Available as both static fonts and variable font. Designed as a branding solution for decentralized systems.
|
||||
|
||||
**Available Weights:** 8 weights from Thin (100) to ExtraBold (800), each with italic variant. Variable font available.
|
||||
|
||||
**Notable Apps/Products:**
|
||||
- Sora (XOR) decentralized projects
|
||||
- Crypto/blockchain projects using modern typography
|
||||
- Web3 products seeking distinctive branding
|
||||
|
||||
**Mobile Suitability:** Good. Clean geometric forms render well on mobile; italics available for emphasis.
|
||||
|
||||
**Distinctive Strengths:**
|
||||
- Full weight range with italics
|
||||
- Variable font option
|
||||
- Designed for digital-first branding
|
||||
- GitHub-native distribution
|
||||
|
||||
**Weakness:** Less established than Inter or DM Sans in mainstream product design; smaller ecosystem.
|
||||
|
||||
---
|
||||
|
||||
## Rejected Candidates
|
||||
|
||||
### General Sans
|
||||
|
||||
**Status:** ❌ Does not meet licensing requirements
|
||||
|
||||
**License:** ITF Free Font License (proprietary, non-commercial personal use only)
|
||||
|
||||
**Why Rejected:** This is a **paid commercial font** distributed by the Indian Type Foundry (not open-source). The ITF Free Font License permits personal use only; commercial use requires a separate paid license. Does not meet the "open-source with permissive license" requirement.
|
||||
|
||||
**Designer:** Frode Helland (published by Indian Type Foundry)
|
||||
|
||||
---
|
||||
|
||||
### Satoshi
|
||||
|
||||
**Status:** ⚠️ License ambiguity — conflicting sources
|
||||
|
||||
**Documented License:**
|
||||
- Some sources claim SIL Open Font License (OFL-1.1)
|
||||
- Other sources indicate ITF Free Font License (personal use only) similar to General Sans
|
||||
|
||||
**Design:** Swiss-style modernist sans-serif (Light to Black, 5–10 weights)
|
||||
|
||||
**Download:** Fontshare (Indian Type Foundry's free font service)
|
||||
|
||||
**Why Not Recommended:** The license status is unclear. While Fontshare advertises "free for personal and commercial use," the font's origin (Indian Type Foundry) and conflicting license documentation create uncertainty. For a privacy-focused project with clear open-source requirements, Satoshi's ambiguous licensing creates unnecessary legal risk. Better alternatives with unambiguous OFL-1.1 licensing are available.
|
||||
|
||||
**Recommendation:** If clarity is needed, contact Fontshare/ITF directly. For now, exclude from consideration to reduce licensing complexity.
|
||||
|
||||
---
|
||||
|
||||
## Comparative Table: Qualified Fonts
|
||||
|
||||
| Metric | Inter | Plus Jakarta Sans | Outfit | Space Grotesk | Manrope | DM Sans | Sora |
|
||||
|--------|-------|-------------------|--------|---------------|---------|---------|------|
|
||||
| **License** | OFL-1.1 | OFL-1.1 | OFL-1.1 | OFL-1.1 | OFL-1.1 | OFL-1.1 | OFL-1.1 |
|
||||
| **Weights** | 9 | 7 | 9 | 5 | 7 | 9 | 8 |
|
||||
| **Italics** | ✅ Yes | ✅ Yes | ❌ No | ❌ No | ❌ No | ✅ Yes | ✅ Yes |
|
||||
| **Variable Font** | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
|
||||
| **Design** | Neo-grotesque | Geometric | Geometric | Neo-grotesque | Geo + Humanist | Geometric | Geometric |
|
||||
| **Personality** | Generic/Safe | Bold/Fresh | Warm/Friendly | Tech-Forward | Balanced | Efficient/Clean | Contemporary |
|
||||
| **Mobile Text** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| **Distinctiveness** | Low | High | Medium | High | High | Medium | Medium |
|
||||
| **Ecosystem** | Very Large | Growing | Medium | Growing | Growing | Large | Small |
|
||||
| **Active Dev** | ✅ Yes | ✅ Yes | ❌ Archived | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
|
||||
### For Bold App-Native Branding
|
||||
|
||||
**Primary Choice: Plus Jakarta Sans**
|
||||
|
||||
**Rationale:**
|
||||
- Fully open-source (OFL-1.1) with unambiguous licensing
|
||||
- Bold, modern geometric aesthetic suitable for app branding
|
||||
- Stylistic sets (Sharp, Straight, Swirl) provide design flexibility
|
||||
- Well-maintained by Tokotype with clear development history
|
||||
- Strong presence in modern UI/web design
|
||||
- Excellent mobile readability with thoughtful character spacing
|
||||
- Indonesian design heritage adds unique perspective (not generic)
|
||||
|
||||
**Alternative: Space Grotesk**
|
||||
|
||||
If you prefer **even more distinctive character:**
|
||||
- Neo-grotesque with tech-forward personality
|
||||
- Smaller weight range (5 weights) but strong identity
|
||||
- Popular in contemporary design circles
|
||||
- Good for headlines; pair with a more neutral font for body text if needed
|
||||
|
||||
---
|
||||
|
||||
### For Safe, Professional UI
|
||||
|
||||
**Primary Choice: Inter or DM Sans**
|
||||
|
||||
**Inter if:**
|
||||
- Maximum ecosystem and tool support desired
|
||||
- Designing for broad recognition and trust
|
||||
- Team already familiar with Inter (widespread in tech)
|
||||
|
||||
**DM Sans if:**
|
||||
- Emphasis on small text legibility (optimized for 12–14px)
|
||||
- Prefer italic variants
|
||||
- Want active maintenance from Google Fonts community
|
||||
|
||||
---
|
||||
|
||||
### For Balanced Approach
|
||||
|
||||
**Manrope**
|
||||
|
||||
- Geometric + humanistic blend (versatile)
|
||||
- Excellent mobile performance
|
||||
- Strong weight range (7 weights)
|
||||
- Underrated choice; often overlooked for bolder options but delivers polish
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes for Self-Hosting
|
||||
|
||||
All recommended fonts can be self-hosted:
|
||||
|
||||
1. **Download:** Clone repository or download from releases page
|
||||
2. **Generate Web Formats:** Use FontForge, FontTools, or online converters to generate WOFF2 (required for modern browsers)
|
||||
3. **CSS:** Include via `@font-face` with local file paths
|
||||
4. **License:** Include `LICENSE.txt` or `OFL.txt` in the distribution
|
||||
|
||||
Example self-hosted CSS:
|
||||
```css
|
||||
@font-face {
|
||||
font-family: 'Plus Jakarta Sans';
|
||||
src: url('/fonts/PlusJakartaSans-Regular.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Privacy Considerations
|
||||
|
||||
All selected fonts are self-hosted open-source projects with no telemetry, no external CDN dependencies, and no tracking. Fully compliant with the project's privacy-first principles.
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
**Inter, Plus Jakarta Sans, and Space Grotesk** are the strongest candidates. The choice depends on brand positioning:
|
||||
|
||||
- **Generic + Safe → Inter**
|
||||
- **Bold + Modern → Plus Jakarta Sans**
|
||||
- **Tech-Forward + Distinctive → Space Grotesk**
|
||||
|
||||
All seven recommended fonts meet the strict licensing, openness, mobile readability, and weight-range requirements. Any of them are viable; the decision is primarily aesthetic.
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
- [Inter Font GitHub Repository](https://github.com/rsms/inter)
|
||||
- [Plus Jakarta Sans GitHub Repository](https://github.com/tokotype/PlusJakartaSans)
|
||||
- [Outfit Fonts GitHub Repository](https://github.com/Outfitio/Outfit-Fonts)
|
||||
- [Space Grotesk GitHub Repository](https://github.com/floriankarsten/space-grotesk)
|
||||
- [Manrope GitHub Repository](https://github.com/sharanda/manrope)
|
||||
- [DM Fonts GitHub Repository](https://github.com/googlefonts/dm-fonts)
|
||||
- [Sora Font GitHub Repository](https://github.com/sora-xor/sora-font)
|
||||
- [SIL Open Font License](https://openfontlicense.org/)
|
||||
- [Google Fonts (reference)](https://fonts.google.com)
|
||||
- [Fontshare (reference)](https://www.fontshare.com)
|
||||
195
docs/agents/research/2026-03-04-us1-create-event.md
Normal file
195
docs/agents/research/2026-03-04-us1-create-event.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