Unified Domain Model
This document provides the bounded context map for the BusFlow platform — the architectural overview showing how contexts relate, not what they contain. For entity dictionaries within each context, follow the spoke links at the bottom of this page.
The platform uses a Modular Monolith architecture, grouping highly cohesive Bounded Contexts into five database schemas: Backoffice, Commerce, Operations, Communications (a Shared Core Domain), and Customer Intelligence [future — Phase 3].
Core Architectural Principles:
- Event-Driven Decoupling: To prevent database deadlocks and tight coupling, contexts communicate primarily via asynchronous Domain Events (e.g.,
BookingConfirmed,IssueReported,DutyViolation). - Soft Cross-Schema References: The system strictly enforces hard foreign key constraints within a schema, but cross-schema relationships use soft ID referencing (e.g., the Commerce schema stores
service_leg_idas an integer without a hard database-level constraint to the Operations schema).
For the DDD patterns governing context interaction (CQRS, read-only views, saga orchestration), see domain-driven-design.md.
Context Map Diagram
For the full event registry with payloads, emitters, consumers, and trigger details, see event-catalog.md.
Context Map
The following map applies the DDD patterns defined in domain-driven-design.md §7 to the concrete bounded contexts. Each row specifies the upstream (data owner) and downstream (data consumer) context, the relationship type, and the primary synchronization mechanism.
| Upstream | Downstream | Relationship | Sync Mechanism | Notes |
|---|---|---|---|---|
| Backoffice | Commerce | Customer–Supplier | Domain events (PriceMatrixPublished, SeasonPricingFinalized, TourDeparturePublished) → Commerce projections | Backoffice owns master data (TourTemplate, TourDeparture, PricingRule). Commerce holds local read models shaped for checkout. |
| Backoffice | Operations | Customer–Supplier | Domain events → Operations projections | Operations derives ServiceLeg from Backoffice TourDeparture data (via TourDeparturePublished). Crew/fleet status changes trigger dispatch recalculation. |
| Commerce | Operations | Conformist | Read-only SQL views | Operations consumes passenger/booking data as-is for boarding. |
| Commerce | Backoffice | Event Notification | Domain events → Backoffice dashboards | Backoffice uses Commerce actuals for Soll/Ist reconciliation. |
| Operations | Backoffice | Event Notification | Domain events → Backoffice inspection scheduling | Field-reported maintenance escalation. |
| Communications | (all contexts) | Shared Kernel | Generic SendMessageCommand API | Provider-agnostic. Domain contexts fire triggers; Communications owns delivery. |
| Backoffice | Communications | Customer–Supplier | Templates + Scheduled triggers | Communications resolves and dispatches notification templates. |
| (all contexts) | Customer Intelligence | Event Notification | [future — Phase 3] Domain events → CI activity log | CI consumes all customer-relevant events for behavioral aggregation and recommendations. See ADR-021. |
| Customer Intelligence | Commerce, Communications, Backoffice | Event Notification | [future — Phase 3] Enrichment events → context projections | CI produces personalized offers, channel preferences, and profile enrichment signals. |
NOTE
No Anti-Corruption Layer (ACL) is required between Busflow's internal bounded contexts. They share a single TypeScript codebase (modular monolith) and a unified ubiquitous language. External legacy integrations are different: Kuschick/Satzart-style booking and ERP integrations require an ACL via dedicated connector/sidecar services so proprietary legacy data constructs do not leak into the Busflow domain model.
See adr-001-boarding-point-strategy.md for a concrete application of all three patterns.
Pricing: Authoring vs. Resolution
Busflow splits "pricing" across two contexts by design; no single context owns it. Teams usually trip over the two senses of the word.
| Sense | Context | What it owns |
|---|---|---|
| Rate-card authoring | Backoffice | Operator-authored selling strategy: base price derived from CostingSheet, margin_config, and the demographic/season/early-bird structure. Cost-derived, planning-side, immutable-append versioning. Lives on PriceMatrix. |
| Price resolution at sale | Commerce | What a specific buyer, in a specific channel, at a specific time actually pays: the TourOfferingPrice read-model, the price_matrix_id frozen on the ticket at checkout, and realized revenue in FinancialLedger. Demand-side, transactional, accumulating. |
So PriceMatrix stays in Backoffice, not Commerce. It is master-data authoring, couples tightly to CostingSheet (e.g. the recalculation→staleness cascade, ADR-006 §6), and serves as the Soll baseline for Soll/Ist reconciliation. Commerce does not own pricing; it consumes a validated read model (Customer–Supplier). The pricing input VOs (PricingRule, PricingConfig) already live on Backoffice TourTemplate, so keeping the output (PriceMatrix) there preserves cohesion.
NOTE
Open seam — [planned — Phase 2]. The static split above holds; demand-driven pricing stays open. Algorithmic yield (Level 3) and channel/commission economics (Level 2) follow signals that live in Commerce/Operations (occupancy, booking velocity, channel), so they pull genuinely toward Commerce. Pick one of two clean resolutions when you build Phase 2 yield:
- Single-authority — price authority stays in Backoffice; Commerce emits demand-signal events; a Backoffice yield service recomputes and republishes
PriceMatrixversions. - Resolution-layer — the base rate-card stays in Backoffice; a thin channel/yield modifier in Commerce layers on at resolution time (matches the "Commission on Retail" option).
See PD-1 (commission ↔ margin) and the Phase 2 Level 2/3 placeholder in ADR-006.
Cross-Boundary Soft FK Reference Map
The following table catalogs all soft foreign key references that cross schema boundaries. These are stored as plain ID columns (UUID or integer) without database-level FK constraints, as mandated by the database boundary rules.
| Source Context | Source Entity | FK Column | Target Context | Target Entity | Notes |
|---|---|---|---|---|---|
| Commerce | TourOffering | tour_departure_id | Backoffice | TourDeparture | Synced via TourDeparturePublished event |
| Commerce | TourOffering | tour_template_id | Backoffice | TourTemplate | Denormalized for display |
| Commerce | TourOfferingPrice | price_matrix_version_id | Backoffice | PriceMatrix | Synced via PriceMatrixPublished event |
| Commerce | Passenger | person_profile_id | Backoffice | PersonProfile | CRM person link |
| Commerce | Passenger | boarding_point_id | Backoffice | BoardingPoint | Pickup location |
| Commerce | Booking | booker_profile_id | Backoffice | PersonProfile | Billing authority (person who pays) [planned — Phase 1]. See ADR-037. |
| Commerce | Booking | reseller_id | Backoffice | Reseller | B2B channel |
| Commerce | Ancillary | catalog_item_id | Backoffice | AncillaryCatalogItem | Nullable; null for system-generated items |
| Commerce | SeatReservation | service_leg_id | Operations | ServiceLeg | Capacity hold per leg |
| Commerce | FinancialLedger | costing_sheet_id | Backoffice | CostingSheet | Soll baseline for Nachkalkulation |
| Operations | ServiceLeg | tour_departure_id | Backoffice | TourDeparture | Departure origin |
| Operations | LegAssignment | vehicle_id | Backoffice | Vehicle | Fleet dispatching |
| Operations | LegAssignment | crew_member_id | Backoffice | CrewMember | Crew dispatching |
| Operations | OnboardSale | ticket_id | Commerce | Ticket | Seat billing |
| Operations | BoardingEvent | ticket_id | Commerce | Ticket | QR validation |
| Communications | Conversation | booking_id | Commerce | Booking | Exclusive nullable FK |
| Communications | Conversation | service_leg_id | Operations | ServiceLeg | Exclusive nullable FK |
| Communications | Conversation | invoice_id | Commerce | Invoice | Exclusive nullable FK |
| Communications | Contact | person_profile_id | Backoffice | PersonProfile | CRM person link |
| Communications | Message | template_id | Backoffice | NotificationTemplate | Automated messages |
Multi-Tenancy Strategy
Every aggregate root carries a tenant_id (UUID) referencing the owning Operator. Hasura permission rules enforce row-level isolation by extracting x-hasura-tenant-id from the JWT and matching it against tenant_id. The system assigns users to specific tenants via a user_tenant_assignments table, preventing unauthorized cross-tenant data access. See domain-driven-design.md for enforcement rules.
Aggregate Model
Each bounded context defines aggregates — clusters of entities that the system modifies together in a single transaction. The aggregate root controls access to all children and enforces invariants.
Design heuristics:
- Transactional boundary — entities that must change together atomically belong in one aggregate.
- Lifecycle coupling — entities that share creation, state transitions, and deletion.
- Invariant protection — business rules that span multiple entities.
- Independent identity — entities with meaning outside their parent become separate aggregates.
- Collection size — unbounded child collections signal a separate aggregate.
FK policy: Hard FKs within aggregate boundaries; soft ID references for cross-aggregate lifecycle conflicts; hard FKs for stable context references. See ADR-036.
| Context | Aggregates | Roots |
|---|---|---|
| Backoffice | 15 | Operator, TourTemplate, TourDeparture, CostingSheet, PriceMatrix, CrewMember, Vehicle, Supplier, Allotment, BoardingPointLibrary, AncillaryCatalogItem, Reseller, PersonProfile, NotificationTemplate, CharterQuote |
| Commerce | 7 | TourOffering, Booking, CheckoutSession, SeatReservation, FinancialLedger, Invoice, LedgerPeriodLock |
| Operations | 6 | ServiceLeg, Incident, IssueReport, ExpenseReceipt, OnboardSale, BoardingEvent |
| Communications | 4 | Conversation, Message, ChannelAccount, Contact |
| Customer Intelligence | 3 | CustomerProfile, CustomerSegment, RecommendationModel |
Each spoke file below contains a full
## Aggregatessection with children, invariants, and domain events.
Entity Dictionaries (Spokes)
| Context | File | Summary |
|---|---|---|
| Backoffice | domain-backoffice.md | Master data, planning, CRM, Kalkulations-Engine (15 aggregates) |
| Commerce & Finance | domain-commerce.md | Sales, payments, capacity, ledger, tax (7 aggregates) |
| Operations | domain-operations.md | Fleet execution, dispatching, IoT, offline sync (6 aggregates) |
| Communications | domain-communications.md | Omnichannel inbox, contacts, automation (4 aggregates) |
| Customer Intelligence | domain-customer-intelligence.md | [TBD] — Phase 3 (3 conceptual aggregates) |