# 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);
```
