Files
fete/specs/016-cancel-event/data-model.md
2026-03-12 19:03:57 +01:00

83 lines
2.4 KiB
Markdown

# Data Model: Cancel Event
**Feature Branch**: `016-cancel-event` | **Date**: 2026-03-12
## Entity Changes
### Event (extended)
Two new fields added to the existing Event entity:
| Field | Type | Constraints | Description |
|--------------------|----------------|------------------------------|--------------------------------------------------|
| cancelled | boolean | NOT NULL, DEFAULT false | Whether the event has been cancelled |
| cancellationReason | String (2000) | Nullable | Optional reason provided by organizer |
### State Transition
```
ACTIVE ──cancel()──► CANCELLED
```
- One-way transition only. No path from CANCELLED back to ACTIVE.
- `cancel()` sets `cancelled = true` and optionally sets `cancellationReason`.
- Once cancelled, the event remains visible but RSVP creation is blocked.
### Validation Rules
- `cancellationReason` max length: 2000 characters (matches description field).
- `cancellationReason` is plain text only (no HTML/markdown).
- `cancelled` can only transition from `false` to `true`, never back.
- Existing RSVPs are preserved when an event is cancelled (no cascade).
## Database Migration (Liquibase Changeset 004)
```xml
<changeSet id="004-add-cancellation-columns" author="fete">
<addColumn tableName="events">
<column name="cancelled" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="cancellation_reason" type="VARCHAR(2000)"/>
</addColumn>
</changeSet>
```
## Domain Model Impact
### Event.java (domain)
Add fields:
```java
private boolean cancelled;
private String cancellationReason;
```
Add method:
```java
public void cancel(String reason) {
if (this.cancelled) {
throw new EventAlreadyCancelledException();
}
this.cancelled = true;
this.cancellationReason = reason;
}
```
### EventJpaEntity.java (persistence)
Add columns:
```java
@Column(name = "cancelled", nullable = false)
private boolean cancelled;
@Column(name = "cancellation_reason", length = 2000)
private String cancellationReason;
```
## RSVP Impact
- `POST /events/{eventToken}/rsvps` must check `event.isCancelled()` before accepting.
- If cancelled → return `409 Conflict`.
- Existing RSVPs remain untouched — no delete, no status change.