Events & Experiences - Domain Specification
First Introduced: V2.0 Status: Specification Complete Last Updated: 2025-10-25
Overview
Events & Experiences is a new product line in V2.0 that extends the villa rental platform to include event management, ticketing, and experience booking. This domain enables event organizers to create and manage events/experiences, guests to discover and book activities, and property managers to bundle events with villa stays for enhanced packages. The domain handles event definitions, scheduling, capacity management, ticket sales, and attendee tracking.
Responsibilities
This domain IS responsible for:
- Event and experience definition and lifecycle management
- Event occurrence scheduling (one-time and recurring events)
- Ticket type definition and inventory management
- Ticket sales and attendee registration
- Event capacity tracking and enforcement
- Waitlist management for sold-out events
- Event cancellation and refund coordination
- Event discovery and search integration
- Package creation (villa + event bundles)
This domain is NOT responsible for:
- Payment processing (→ Payments & Financials domain)
- User identity and authentication (→ Identity & Tenancy domain)
- Authorization and permissions (→ Authorization & Access domain)
- Content storage and media assets (→ Content & Media domain)
- Search indexing implementation (→ Search & Indexing domain)
- Audit trail logging (→ Analytics & Audit domain)
- Villa availability management (→ Availability & Calendars domain)
Relationships
Depends On:
- Identity & Tenancy - Event organizers are Accounts within Orgs
- Authorization & Access - Permission to create/manage events
- Supply - Events may be linked to specific Spaces (venues)
- Pricing & Revenue - Ticket pricing and revenue split rules
- Payments & Financials - Ticket payment processing
- Content & Media - Event descriptions and photos
Depended On By:
- Bookings - Villa bookings may reference event packages
- Search & Indexing - Event discovery and filtering
- Analytics & Audit - Event performance metrics
Related Domains:
- Availability & Calendars - Event occurrences create time-based inventory
- Channels & Distribution - Event syndication to partner sites
Core Concepts
Entity: EventDefinition
Purpose: Template/master record for an event or experience, defining what it is and how it operates.
Key Attributes:
- id(UUID, primary key)
- org_id(UUID, foreign key → organizations.id)
- account_id(UUID, foreign key → accounts.id)
- name(VARCHAR, required) - Event name (e.g., "Beach Yoga Sunrise Session")
- slug(VARCHAR, unique per org) - URL-friendly identifier
- category(ENUM) - tour | activity | class | workshop | dining | wellness | cultural | adventure | entertainment | other
- event_type(ENUM) - one_time | recurring | multi_day
- duration_minutes(INTEGER) - Standard duration per occurrence
- venue_type(ENUM) - on_property | off_property | virtual | mobile
- venue_space_id(UUID, nullable, foreign key → spaces.id) - If event happens at a specific Space
- venue_name(VARCHAR) - Venue name if not a Space
- venue_address(JSONB) - Venue location details
- organizer_name(VARCHAR) - Name of event organizer/instructor
- organizer_contact(JSONB) - Contact info for organizer
- max_capacity(INTEGER, nullable) - Maximum attendees per occurrence (null = unlimited)
- min_capacity(INTEGER, default 1) - Minimum attendees to run event
- status(ENUM) - draft | active | paused | archived
- settings(JSONB) - Event-specific configuration
- created_at,- updated_at(timestamps)
Relationships:
- EventDefinition → Org (*, many-to-one)
- EventDefinition → Account (*, many-to-one)
- EventDefinition → Space (*, many-to-one, optional venue)
- EventDefinition → EventOccurrence (1:*)
- EventDefinition → TicketType (1:*)
- EventDefinition → Package (1:*) - Events can be in multiple packages
Lifecycle:
- Created: Event organizer creates event template
- Activated: Set to status='active'when ready for bookings
- Paused: Temporarily disable new bookings
- Archived: No longer offered, historical record only
Business Rules:
- Event definitions scoped to Org + Account (organizer)
- Slug must be unique within org_id
- If venue_space_idis set, event location is tied to that Space
- Cannot delete EventDefinition with active EventOccurrences (archive only)
- Multi-day events have event_type='multi_day'and multiple linked occurrences
Entity: EventOccurrence
Purpose: Specific instance of an event happening at a particular date/time.
Key Attributes:
- id(UUID, primary key)
- org_id(UUID, foreign key → organizations.id)
- account_id(UUID, foreign key → accounts.id)
- event_definition_id(UUID, foreign key → event_definitions.id)
- occurrence_name(VARCHAR, nullable) - Optional override name
- start_at(TIMESTAMP WITH TIME ZONE, required)
- end_at(TIMESTAMP WITH TIME ZONE, required)
- timezone(VARCHAR) - Event timezone (e.g., "America/New_York")
- capacity_override(INTEGER, nullable) - Override max_capacity for this occurrence
- status(ENUM) - scheduled | confirmed | in_progress | completed | canceled
- cancellation_reason(TEXT, nullable)
- canceled_at(TIMESTAMP, nullable)
- seats_sold(INTEGER, default 0) - Current ticket sales count
- seats_available(INTEGER GENERATED) - Computed: capacity - seats_sold
- waitlist_count(INTEGER, default 0)
- settings(JSONB) - Occurrence-specific overrides
- created_at,- updated_at(timestamps)
Relationships:
- EventOccurrence → EventDefinition (*, many-to-one)
- EventOccurrence → Org (*, many-to-one)
- EventOccurrence → Account (*, many-to-one)
- EventOccurrence → Ticket (1:*)
- EventOccurrence → Attendee (1:*)
- EventOccurrence → WaitlistEntry (1:*)
Lifecycle:
- Created: When event organizer schedules specific date/time
- Confirmed: Minimum capacity reached, event will proceed
- In Progress: Event currently happening
- Completed: Event finished successfully
- Canceled: Event canceled (triggers refunds)
Business Rules:
- end_atmust be after- start_at
- Cannot schedule overlapping occurrences for same venue
- Capacity enforced: seats_soldcannot exceedcapacity_overrideorevent_definition.max_capacity
- Status transitions: scheduled → confirmed → in_progress → completed
- Cancellation allowed up to cutoff time (defined in settings)
- Canceled events retain all data for refund processing
Entity: TicketType
Purpose: Defines pricing tiers and ticket categories for an event.
Key Attributes:
- id(UUID, primary key)
- org_id(UUID, foreign key → organizations.id)
- account_id(UUID, foreign key → accounts.id)
- event_definition_id(UUID, foreign key → event_definitions.id)
- name(VARCHAR, required) - e.g., "General Admission", "VIP", "Early Bird"
- description(TEXT) - What this ticket includes
- tier(ENUM) - general | vip | early_bird | group | child | senior | student | member
- price_cents(INTEGER, required) - Base price in minor currency units
- currency(CHAR(3), default 'USD')
- quantity_available(INTEGER, nullable) - Limited quantity (null = unlimited)
- quantity_sold(INTEGER, default 0)
- valid_from(TIMESTAMP, nullable) - When this ticket type becomes available
- valid_until(TIMESTAMP, nullable) - When this ticket type expires
- min_quantity(INTEGER, default 1) - Minimum purchase (e.g., 2 for couples ticket)
- max_quantity(INTEGER, default 10) - Maximum per transaction
- status(ENUM) - active | sold_out | expired | disabled
- settings(JSONB) - Additional rules (e.g., discount codes, restrictions)
- created_at,- updated_at(timestamps)
Relationships:
- TicketType → EventDefinition (*, many-to-one)
- TicketType → Org (*, many-to-one)
- TicketType → Account (*, many-to-one)
- TicketType → Ticket (1:*) - Sold tickets of this type
Lifecycle:
- Created: When event organizer defines pricing tiers
- Activated: Available for purchase
- Sold Out: All quantity_available tickets sold
- Expired: Past valid_until date
- Disabled: Manually paused by organizer
Business Rules:
- At least one TicketType required per EventDefinition
- quantity_soldcannot exceed- quantity_available
- valid_frommust be before- valid_until
- Price must be non-negative
- Status auto-updates: sold_out when quantity_sold >= quantity_available
- Expired tickets cannot be purchased even if quantity remains
Entity: Ticket
Purpose: Individual ticket instance representing a sold ticket for an event occurrence.
Key Attributes:
- id(UUID, primary key)
- org_id(UUID, foreign key → organizations.id)
- account_id(UUID, foreign key → accounts.id)
- event_occurrence_id(UUID, foreign key → event_occurrences.id)
- ticket_type_id(UUID, foreign key → ticket_types.id)
- attendee_id(UUID, foreign key → attendees.id)
- booking_id(UUID, nullable, foreign key → bookings.id) - If part of villa package
- confirmation_code(VARCHAR, unique) - Human-readable code
- qr_code_data(TEXT) - QR code payload for check-in
- price_paid_cents(INTEGER) - Actual price paid (may differ from ticket_type.price)
- currency(CHAR(3))
- status(ENUM) - reserved | confirmed | checked_in | canceled | refunded
- reserved_at(TIMESTAMP) - When ticket was reserved (holds inventory)
- confirmed_at(TIMESTAMP) - When payment completed
- checked_in_at(TIMESTAMP, nullable) - When attendee arrived
- canceled_at(TIMESTAMP, nullable)
- refund_amount_cents(INTEGER, default 0)
- settings(JSONB) - Ticket-specific metadata
- created_at,- updated_at(timestamps)
Relationships:
- Ticket → EventOccurrence (*, many-to-one)
- Ticket → TicketType (*, many-to-one)
- Ticket → Attendee (*, many-to-one)
- Ticket → Booking (*, many-to-one, optional if part of villa package)
- Ticket → Payment (via booking or direct payment)
Lifecycle:
- Reserved: Ticket held during checkout (TTL 15 minutes)
- Confirmed: Payment succeeded, ticket issued
- Checked In: Attendee scanned at event
- Canceled: Ticket voided before event
- Refunded: Payment returned to customer
Business Rules:
- Confirmation code must be globally unique
- Reserved tickets expire after 15 minutes if not confirmed
- Cannot check in before event start_at time
- Canceled tickets release capacity back to occurrence
- Refund policies determined by event_definition.settings
- QR code generated on confirmation, unique per ticket
Entity: Attendee
Purpose: Person attending an event (may or may not be a registered User).
Key Attributes:
- id(UUID, primary key)
- org_id(UUID, foreign key → organizations.id)
- user_id(UUID, nullable, foreign key → users.id) - If attendee is a registered user
- email(VARCHAR, required) - Contact email
- given_name(VARCHAR, required)
- family_name(VARCHAR)
- phone(VARCHAR, nullable)
- emergency_contact(JSONB, nullable) - Emergency contact info
- dietary_restrictions(TEXT[], nullable)
- accessibility_needs(TEXT[], nullable)
- notes(TEXT, nullable) - Special requests
- created_at,- updated_at(timestamps)
Relationships:
- Attendee → Org (*, many-to-one)
- Attendee → User (*, many-to-one, optional)
- Attendee → Ticket (1:*)
- Attendee → WaitlistEntry (1:*)
Lifecycle:
- Created: When ticket is purchased or waitlist entry added
- Updated: Attendee updates their information
- Linked to User: If attendee creates account later
Business Rules:
- Email required for ticket delivery and communication
- If user_id is set, attendee is a registered platform user
- Same person can attend multiple events (multiple Attendee records)
- Dietary and accessibility info used for event planning
- GDPR compliant: attendees can request data deletion
Entity: WaitlistEntry
Purpose: Queue for sold-out events, tracks people waiting for available tickets.
Key Attributes:
- id(UUID, primary key)
- org_id(UUID, foreign key → organizations.id)
- event_occurrence_id(UUID, foreign key → event_occurrences.id)
- attendee_id(UUID, foreign key → attendees.id)
- position(INTEGER) - Queue position (1 = first in line)
- quantity_requested(INTEGER, default 1)
- ticket_type_id(UUID, nullable, foreign key → ticket_types.id) - Preferred ticket type
- status(ENUM) - waiting | notified | converted | expired | canceled
- notified_at(TIMESTAMP, nullable) - When availability notification sent
- expires_at(TIMESTAMP, nullable) - When notification expires
- converted_ticket_id(UUID, nullable, foreign key → tickets.id)
- created_at,- updated_at(timestamps)
Relationships:
- WaitlistEntry → EventOccurrence (*, many-to-one)
- WaitlistEntry → Attendee (*, many-to-one)
- WaitlistEntry → TicketType (*, many-to-one, optional)
- WaitlistEntry → Ticket (1:1, optional after conversion)
Lifecycle:
- Created: When user joins waitlist for sold-out event
- Notified: Ticket becomes available, notification sent
- Converted: User purchased ticket from waitlist
- Expired: Notification timeout passed without purchase
- Canceled: User left waitlist
Business Rules:
- Position auto-assigned based on creation timestamp
- Notification sent when capacity opens up
- 24-hour window to purchase after notification
- One waitlist entry per attendee per occurrence
- Converted entries create Ticket and remove from waitlist
Entity: EventPackage
Purpose: Bundles events with villa stays or combines multiple events into packages.
Key Attributes:
- id(UUID, primary key)
- org_id(UUID, foreign key → organizations.id)
- account_id(UUID, foreign key → accounts.id)
- name(VARCHAR, required) - e.g., "Ultimate Wellness Retreat"
- slug(VARCHAR, unique per org)
- package_type(ENUM) - villa_event | multi_event | experience_bundle
- description(TEXT)
- base_price_cents(INTEGER, nullable) - Package price override
- currency(CHAR(3), default 'USD')
- discount_percent(DECIMAL, default 0) - Discount when purchased as package
- status(ENUM) - draft | active | paused | archived
- valid_from(DATE, nullable)
- valid_until(DATE, nullable)
- settings(JSONB) - Package rules and inclusions
- created_at,- updated_at(timestamps)
Relationships:
- EventPackage → Org (*, many-to-one)
- EventPackage → Account (*, many-to-one)
- EventPackage → EventPackageItem (1:*)
- EventPackage → Booking (1:*) - Bookings using this package
Lifecycle:
- Created: Property manager or event organizer creates bundle
- Activated: Available for guest purchase
- Paused: Temporarily unavailable
- Archived: No longer offered
Business Rules:
- Package must include at least 1 item (event or villa)
- Discount applies to total of individual component prices
- All package components must be available for selected dates
- Package booking creates individual tickets for each event
- Cannot modify package while active bookings exist
Entity: EventPackageItem
Purpose: Links specific events or villas to packages.
Key Attributes:
- id(UUID, primary key)
- package_id(UUID, foreign key → event_packages.id)
- item_type(ENUM) - event | villa
- event_definition_id(UUID, nullable, foreign key → event_definitions.id)
- space_id(UUID, nullable, foreign key → spaces.id)
- quantity(INTEGER, default 1) - Number of tickets/nights included
- is_required(BOOLEAN, default true) - Must be selected
- is_customizable(BOOLEAN, default false) - Guest can choose quantity
- position(INTEGER) - Display order
- created_at,- updated_at(timestamps)
Relationships:
- EventPackageItem → EventPackage (*, many-to-one)
- EventPackageItem → EventDefinition (*, many-to-one, if event)
- EventPackageItem → Space (*, many-to-one, if villa)
Business Rules:
- Either event_definition_id or space_id must be set (not both)
- Required items must be included in package booking
- Customizable items allow guest to adjust quantity
- Position determines display order in UI
Workflows
Workflow: Create Recurring Event
- Event organizer creates EventDefinition with event_type='recurring'
- Define recurrence rule in settings (e.g., "Every Tuesday 7am-8am")
- Generate EventOccurrences for next 90 days (batch job)
- Create TicketTypes (e.g., "General Admission $25", "Early Bird $20")
- Activate EventDefinition (status='active')
- Publish to search index for guest discovery
- Return success with event_definition_id
Postconditions:
- Event appears in search results
- Guests can browse and book any future occurrence
- Organizer can manage all occurrences from one definition
Workflow: Book Event Ticket
- Guest searches events by date, category, location
- Select event occurrence and ticket type
- Specify quantity and attendee details
- Acquire capacity lock on EventOccurrence
- Create Ticket records with status='reserved'
- Create Attendee records for each person
- Generate Quote with ticket prices and fees
- Hold reservation for 15 minutes (TTL)
- Guest completes payment via Payments domain
- On payment success:
- Update Tickets to status='confirmed'
- Generate QR codes and confirmation codes
- Send confirmation email with ticket PDFs
- Update EventOccurrence.seats_sold
 
- Update Tickets to 
- On timeout: Release tickets, decrement seats_sold
Postconditions:
- Tickets reserved in guest's account
- Capacity updated to reflect sales
- Payment recorded and revenue split applied
- Confirmation sent with event details
Workflow: Join Waitlist
- Guest attempts to book sold-out event
- System offers waitlist option
- Guest provides attendee details and quantity
- Create WaitlistEntry with next available position
- Send confirmation email "You're #5 on the waitlist"
- When ticket becomes available (cancellation or capacity increase):
- Find first WaitlistEntry with matching quantity
- Send notification email with purchase link
- Set status='notified',expires_at=now()+24h
 
- If guest purchases: Convert WaitlistEntry → Ticket
- If expires: Move to next person in queue
Postconditions:
- Guest queued for notification
- Auto-notification when capacity opens
- Fair queue management (FIFO)
Workflow: Create Villa + Event Package
- Property manager creates EventPackage with package_type='villa_event'
- Add package items:
- EventPackageItem: space_id=villa123, quantity=5 (nights)
- EventPackageItem: event_definition_id=cooking_class, quantity=2 (tickets)
- EventPackageItem: event_definition_id=wine_tour, quantity=2
 
- Set discount (e.g., 15% off total)
- Define date constraints (valid_from/valid_until)
- Activate package (status='active')
- Publish to search and package catalog
- Guest books package:
- Creates Booking for villa stay
- Creates Tickets for each event
- Links all via package_id
- Applies package discount to total
 
- Return unified confirmation with itinerary
Postconditions:
- Single booking with multiple components
- Coordinated availability across villa and events
- Unified payment and receipt
- Enhanced guest experience
Business Rules
- Capacity Enforcement: seats_soldcannot exceedmax_capacity(atomic lock required)
- Reservation Timeout: Reserved tickets auto-release after 15 minutes if not confirmed
- Event Timing: Occurrences cannot overlap for same venue/space
- Cancellation Cutoff: Events can be canceled up to configured hours before start_at
- Minimum Capacity: Events with min_capacityauto-cancel if not met by cutoff
- Refund Policy: Defined per EventDefinition, processed via Payments domain
- QR Code Uniqueness: Each ticket has unique QR code for check-in scanning
- Package Availability: All package components must be available for selected dates
- Waitlist Notification: 24-hour window to purchase after notification
- Multi-Day Events: Linked occurrences with same event_definition_id and sequential dates
Implementation Notes
MVP.0 Scope
OUT OF SCOPE for MVP.0:
- Entire Events & Experiences domain deferred to V2.0
- MVP.0 focuses only on villa rentals
V1.0 Scope
OUT OF SCOPE for V1.0:
- Events & Experiences domain not included
- V1.0 focuses on villa marketplace maturity
V2.0 Scope (Full Implementation)
Included:
- Complete Events & Experiences domain
- Event definitions and occurrence scheduling
- Ticket types and sales
- Attendee management and check-in
- Waitlist functionality
- Villa + event package creation
- Event search and discovery
- Integration with existing villa platform
Implementation Phases:
V2.0 Phase 1 (Months 13-14):
- EventDefinition, EventOccurrence, TicketType entities
- Basic ticket sales and attendee records
- One-time and recurring event support
- QR code generation for check-in
V2.0 Phase 2 (Months 15-16):
- Waitlist management
- EventPackage for villa bundles
- Multi-event packages
- Discount and pricing rules
V2.0 Phase 3 (Months 17-18):
- Event search optimization
- Analytics and reporting
- Mobile check-in app
- Organizer dashboard
Database Indexes
Critical for performance:
-- Event lookups
CREATE INDEX idx_event_definitions_org_account ON event_definitions(org_id, account_id);
CREATE INDEX idx_event_definitions_status ON event_definitions(status) WHERE status = 'active';
CREATE INDEX idx_event_definitions_category ON event_definitions(category);
-- Occurrence queries
CREATE INDEX idx_event_occurrences_definition ON event_occurrences(event_definition_id);
CREATE INDEX idx_event_occurrences_time ON event_occurrences(start_at, end_at);
CREATE INDEX idx_event_occurrences_status ON event_occurrences(status);
-- Ticket management
CREATE INDEX idx_tickets_occurrence ON tickets(event_occurrence_id);
CREATE INDEX idx_tickets_attendee ON tickets(attendee_id);
CREATE INDEX idx_tickets_confirmation ON tickets(confirmation_code);
CREATE INDEX idx_tickets_status ON tickets(status);
-- Attendee lookups
CREATE INDEX idx_attendees_email ON attendees(email);
CREATE INDEX idx_attendees_user ON attendees(user_id) WHERE user_id IS NOT NULL;
-- Waitlist processing
CREATE INDEX idx_waitlist_occurrence_position ON waitlist_entries(event_occurrence_id, position);
CREATE INDEX idx_waitlist_status ON waitlist_entries(status) WHERE status = 'waiting';
-- Package queries
CREATE INDEX idx_event_packages_org_account ON event_packages(org_id, account_id);
CREATE INDEX idx_package_items_package ON event_package_items(package_id);
Constraints
Enforce data integrity:
-- Event definitions
ALTER TABLE event_definitions ADD CONSTRAINT event_def_org_slug_unique UNIQUE (org_id, slug);
ALTER TABLE event_definitions ADD CONSTRAINT event_def_capacity_check CHECK (max_capacity IS NULL OR max_capacity > 0);
-- Event occurrences
ALTER TABLE event_occurrences ADD CONSTRAINT event_occ_time_check CHECK (end_at > start_at);
ALTER TABLE event_occurrences ADD CONSTRAINT event_occ_seats_check CHECK (seats_sold >= 0);
-- Ticket types
ALTER TABLE ticket_types ADD CONSTRAINT ticket_type_price_check CHECK (price_cents >= 0);
ALTER TABLE ticket_types ADD CONSTRAINT ticket_type_quantity_check CHECK (quantity_sold <= quantity_available);
-- Tickets
ALTER TABLE tickets ADD CONSTRAINT ticket_confirmation_unique UNIQUE (confirmation_code);
ALTER TABLE tickets ADD CONSTRAINT ticket_qr_unique UNIQUE (qr_code_data);
-- Packages
ALTER TABLE event_packages ADD CONSTRAINT package_org_slug_unique UNIQUE (org_id, slug);
ALTER TABLE event_package_items ADD CONSTRAINT package_item_type_check
  CHECK ((event_definition_id IS NOT NULL AND space_id IS NULL) OR
         (event_definition_id IS NULL AND space_id IS NOT NULL));
Future Enhancements
V2.1: Advanced Event Features
- Multi-session events (courses, workshops)
- Event series and memberships
- Loyalty points for repeat attendees
- Gift vouchers and promotional codes
V2.2: Enhanced Ticketing
- Dynamic pricing based on demand
- Group booking discounts
- Corporate event management
- Private event bookings
V2.3: Organizer Tools
- Revenue analytics dashboard
- Attendee communication tools
- Check-in mobile app with offline mode
- Automated event reminders
V3.0: Marketplace Expansion
- White-label event platforms
- Multi-vendor event marketplace
- Franchise/partner organizer programs
- Global event search and syndication
Integration Points
With Existing Domains
Identity & Tenancy:
- Event organizers are Accounts within Orgs
- Attendees optionally linked to User records
- Multi-tenant event isolation
Authorization & Access:
- Permissions: event.create,event.manage,ticket.sell,attendee.view
- Organizers manage only their events
- Package creators need both event and villa permissions
Supply (Spaces & Units):
- Events can specify venue via space_id
- Packages link villas and events
- Venue capacity constraints
Availability & Calendars:
- Event occurrences consume time slots
- Integrated calendar view (villas + events)
- Conflict detection for shared venues
Pricing & Revenue:
- TicketType pricing uses RatePlan concepts
- Revenue splits between organizers and platform
- Package discounting rules
Payments & Financials:
- Ticket sales create Payment records
- Refunds processed through existing flows
- Payout to event organizers
Bookings:
- Event packages extend Booking entity
- Combined villa + event reservations
- Unified confirmation and itinerary
Content & Media:
- Event photos and descriptions
- Organizer profiles and bios
- Marketing assets
Search & Indexing:
- Event discovery by date/location/category
- Featured events and recommendations
- Availability-aware search
Analytics & Audit:
- Event performance metrics
- Sales and attendance tracking
- Audit trail for cancellations and refunds
Related Documents
- V2 Product Specification - Events & Experiences vision
- V2 Data Model - Complete event schemas
- Booking Domain - Package booking integration
- Pricing Domain - Ticket pricing patterns
- Payments Domain - Ticket payment flows