2.4 KiB
2.4 KiB
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()setscancelled = trueand optionally setscancellationReason.- Once cancelled, the event remains visible but RSVP creation is blocked.
Validation Rules
cancellationReasonmax length: 2000 characters (matches description field).cancellationReasonis plain text only (no HTML/markdown).cancelledcan only transition fromfalsetotrue, never back.- Existing RSVPs are preserved when an event is cancelled (no cascade).
Database Migration (Liquibase Changeset 004)
<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:
private boolean cancelled;
private String cancellationReason;
Add method:
public void cancel(String reason) {
if (this.cancelled) {
throw new EventAlreadyCancelledException();
}
this.cancelled = true;
this.cancellationReason = reason;
}
EventJpaEntity.java (persistence)
Add columns:
@Column(name = "cancelled", nullable = false)
private boolean cancelled;
@Column(name = "cancellation_reason", length = 2000)
private String cancellationReason;
RSVP Impact
POST /events/{eventToken}/rsvpsmust checkevent.isCancelled()before accepting.- If cancelled → return
409 Conflict. - Existing RSVPs remain untouched — no delete, no status change.