Spec, research decisions, implementation plan, data model, API contract, and task breakdown for the RSVP feature. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
94 lines
4.4 KiB
Markdown
94 lines
4.4 KiB
Markdown
# 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);
|
|
```
|