83 lines
2.4 KiB
Markdown
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.
|