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

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() 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)

<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}/rsvps must check event.isCancelled() before accepting.
  • If cancelled → return 409 Conflict.
  • Existing RSVPs remain untouched — no delete, no status change.