# Data Model: RSVP to an Event (008) **Date**: 2026-03-06 ## Entities ### Rsvp (NEW) | Field | Type | Required | Constraints | Notes | |------------|----------------|----------|--------------------------------|------------------------------------| | id | Long | yes | BIGSERIAL, PK | Internal only, never exposed | | rsvpToken | RsvpToken | yes | UNIQUE, NOT NULL | Server-generated UUID, returned to client | | eventId | Long | yes | FK -> events.id, NOT NULL | Which event this RSVP belongs to | | name | String | yes | 1-100 chars, NOT NULL | Guest's display name | **Notes**: - No `attending` boolean — existence of an entry implies attendance (per spec). - No `createdAt` — not required by the spec. Can be added later if needed (e.g. for guest list sorting in 009). - Duplicates from different devices or cleared localStorage are accepted (privacy trade-off). ### Token Value Objects (NEW) | Record | Field | Type | Notes | |------------------|-------|------|-----------------------------------------------| | `EventToken` | value | UUID | Immutable, non-null. Java record wrapping UUID | | `OrganizerToken` | value | UUID | Immutable, non-null. Java record wrapping UUID | | `RsvpToken` | value | UUID | Immutable, non-null. Java record wrapping UUID | **Purpose**: Type-safe wrappers preventing mix-ups between the three token types at compile time. All generated server-side via `UUID.randomUUID()`. JPA entities continue to use raw `UUID` columns — mapping happens in the persistence adapters. ### Event (MODIFIED — token fields change type) The Event domain model's `eventToken` and `organizerToken` fields change from raw `UUID` to their typed record wrappers. No database schema change — the JPA entity keeps raw `UUID` columns. | Field | Old Type | New Type | |-----------------|----------|------------------| | eventToken | UUID | EventToken | | organizerToken | UUID | OrganizerToken | The `attendeeCount` was already added to the API response in 007-view-event — it now gets populated from a count query instead of returning 0. ### StoredEvent (frontend localStorage — modified) | Field | Type | Required | Notes | |----------------|--------|----------|------------------------------------| | eventToken | string | yes | Existing | | organizerToken | string | no | Existing (organizer flow) | | title | string | yes | Existing | | dateTime | string | yes | Existing | | expiryDate | string | yes | Existing | | rsvpToken | string | no | **NEW** — set after RSVP submission | | rsvpName | string | no | **NEW** — guest's submitted name | ## Validation Rules - `name`: required, 1-100 characters, trimmed. Blank or whitespace-only is rejected. - `rsvpToken`: server-generated, never from client input on create. - `eventId`: must reference an existing, non-expired event. ## Relationships ``` Event 1 <---- * Rsvp | | eventToken rsvpToken (unique) (public) (returned to client) ``` ## Type Mapping (full stack) | Concept | Java | PostgreSQL | OpenAPI | TypeScript | |--------------|-------------------|---------------|---------------------|------------| | RSVP ID | `Long` | `bigserial` | N/A (not exposed) | N/A | | RSVP Token | `RsvpToken` | `uuid` | `string` `uuid` | `string` | | Event FK | `Long` | `bigint` | N/A (path param) | N/A | | Guest name | `String` | `varchar(100)`| `string` | `string` | | Attendee cnt | `long` | `count(*)` | `integer` | `number` | ## Database Migration New Liquibase changeset `003-create-rsvps-table.xml`: ```sql CREATE TABLE rsvps ( id BIGSERIAL PRIMARY KEY, rsvp_token UUID NOT NULL UNIQUE, event_id BIGINT NOT NULL REFERENCES events(id), name VARCHAR(100) NOT NULL ); CREATE INDEX idx_rsvps_event_id ON rsvps(event_id); CREATE INDEX idx_rsvps_rsvp_token ON rsvps(rsvp_token); ```