Skip to content

Architecture

Booking is a headless, API-first core with a family of optional submodules. The core ships only entities, the BookingManager service, events and plugin types - no UI, no payment, no contrib dependencies.

Layers

flowchart TB
    subgraph presentation["Presentation / consumers (submodules & 3rd party)"]
        UI[yoyaku_ui<br/>admin]
        CAL[yoyaku_calendar<br/>public picker]
        WFE[booking_webform<br/>element]
        ORCH[booking_orchestra<br/>workflow tasks]
        PAY[yoyaku_payment<br/>deposit / Commerce]
        API[booking_api<br/>remote HTTP client]
    end

    subgraph core["booking (core, headless)"]
        BM[BookingManager service]
        EV[Lifecycle events]
        CON[BookingConstraint plugins]
        QW[yoyaku_expire_holds<br/>queue worker + cron]
    end

    subgraph data["Entities"]
        R[Resource] --> S[Slot] --> C[Category] --> B[Booking]
    end

    presentation --> BM
    BM --> CON
    BM --> data
    BM --> EV
    EV --> presentation
    QW --> BM

The dependency direction is strict: consumers depend on the core; the core depends on nothing but Drupal. The engine never knows about webforms, payments, nodes or workflows - they react through the events and drive it through the API.

The booking on a resource page with a calendar is one surface, not the model. For the full space of booking use cases (search, appointments, events, carts, API), the seams that absorb each one, and the rule that the core never assumes a surface, see Use cases and surfaces.

Module map

Module Role Status
booking Core: entities, BookingManager, events, constraint plugin type, hold-expiry worker, multi-tenancy. Done
booking_node Bind a resource to a node + per-user resource management. Planned
yoyaku_ui Availability/calendar admin + booking dashboards. Planned
yoyaku_calendar Public JS slot-picker (block / field formatter) over an availability endpoint. Planned
booking_webform Webform element wrapping the slot-picker. Planned
booking_orchestra Orchestra task types: hold / confirm / release in a workflow. Planned
yoyaku_payment Deposit/price config on the category + "confirm on payment" contract. Planned
booking_recur Recurrence rules that materialise slots. Planned
booking_views Views integration. Planned
booking_api Remote HTTP client API (REST/JSON:API + OAuth + OpenAPI). Planned

Services

Service id Class Purpose
booking.booking_manager BookingManager The capacity engine and lifecycle API.
booking.yoyaku_constraint_manager BookingConstraintManager Discovers/runs constraint plugins.
booking.tenant_context TenantContext Resolves the active tenant.
logger.channel.booking - The module's log channel.

The BookingManager is injected with the entity type manager, the database connection (for transactions + row locks), the event dispatcher, the time service, and the constraint manager.

Design rules

  • OOP #[Hook] only - no .module file (D11.3+, D12-ready), matching the Orchestra house style.
  • Attribute-based entities (#[ContentEntityType], #[ConfigEntityType]), final classes, declare(strict_types=1).
  • Capacity is the lock target - see Capacity and holds.
  • Money and presentation stay out of the core.