Domain Architecture

Deep dive into the architectural principles, CQRS segregation, tactical modeling patterns, and event choreography governing Obelaw Domains.

Domain Architecture Specification

Building large-scale enterprise software requires an architectural pattern that scales with organizational complexity. Obelaw implements a decoupled, business-driven Domain-Driven Design (DDD) architecture that shields core enterprise logic from framework churn, database mutations, and delivery protocols.

This specification details the tactical and strategic architectural patterns that govern every bounded context within the platform.


1. Architectural Foundations

The architecture balances formal Domain-Driven Design principles with pragmatic operational ergonomics:

flowchart TD
    subgraph Presentation["1. Presentation & Delivery Layer"]
        Web["Web Controllers"]
        Admin["Admin Panels (Filament)"]
        API["REST / GraphQL Endpoints"]
        Console["Console Commands"]
        Queue["Background Job Workers"]
    end

    subgraph Boundaries["2. Context Boundary & Gateways"]
        DTO["Type-Safe DTOs<br/>Validated Payloads"]
        Gateway["Unified Domain Gateway<br/>Context Service Access"]
    end

    subgraph Core["3. Tactical Domain Core"]
        CQRS["CQRS Command & Query Segregation"]
        subgraph WriteSide["Command (Write) Pipeline"]
            Actions["Single-Purpose Actions"]
            Invariants["Domain Invariants ('The Law')"]
            Aggregates["Aggregate Roots & State Machines"]
        end
        subgraph ReadSide["Query (Read) Pipeline"]
            Queries["Fluent Query Pipelines"]
            Projections["Read Models & Criteria Builders"]
        end
        Events["Domain Event Bus<br/>State Change Broadcasts"]
    end

    subgraph Infrastructure["4. Persistence & Infrastructure"]
        Storage["Relational DB Storage<br/>Audit Trails & Ledgers"]
        Cache["Distributed Cache & Indexes"]
        External["External Third-Party Ports"]
    end

    Presentation -->|"Encapsulated in DTOs"| DTO
    DTO --> Gateway
    Gateway --> CQRS
    CQRS --> WriteSide
    CQRS --> ReadSide
    WriteSide -->|"Enforces Rules"| Invariants
    WriteSide -->|"Mutates State"| Aggregates
    Aggregates -->|"Publishes Events"| Events
    Aggregates -.->|"State Persistence"| Storage
    ReadSide --> Cache
    ReadSide --> Storage

2. CQRS Pattern: Write Actions vs. Read Queries

A fundamental rule in Obelaw is the strict segregation of read and write responsibilities. Blending mutations with data querying produces fragile code with hidden side-effects.

Command Pipeline: Single-Purpose Actions

All domain state modifications occur exclusively through Actions:

  • One Unit of Work: An action represents a single, cohesive business transaction (e.g., IssueInvoice, TransferStock, DisqualifyLead).
  • Validated Input: Actions accept strongly-typed, immutable Data Transfer Objects (DTOs). They never accept raw HTTP requests or loose associative arrays.
  • Invariant Enforcement: Before mutating state, the action evaluates all non-negotiable business rules (“The Law”). If an invariant fails, execution immediately halts, throwing a domain exception.
  • Event Emission: Upon successfully committing changes, the action emits one or more domain events to notify the rest of the enterprise.

Query Pipeline: Fluent Criteria Builders

Data retrieval occurs through dedicated Queries:

  • Zero Side-Effects: Queries are strictly read-only and never alter domain state, fire mutation events, or perform database writes.
  • Chained Fluidity: Query methods accept specific filtering, scoping, and sorting parameters, returning intermediate query builders until materialized.
  • Optimized Representations: Queries can bypass complex aggregate rehydration to assemble flat, efficient read models optimized for data tables, dashboards, and reporting views.

3. Data Transfer Objects (DTOs) as Boundary Shields

Data entering a bounded context must be validated and sanitized before domain logic executes. DTOs serve as defensive shields at context boundaries:

flowchart LR
    subgraph ExternalWorld["External Request Boundary"]
        Payload["Untrusted Payload<br/>JSON / Form Data / CLI Arguments"]
    end

    subgraph ValidationBoundary["Boundary Verification"]
        DTO["Immutable DTO Factory<br/>Type Casting · Required Field Verification · Range Checks"]
    end

    subgraph DomainHeart["Domain Execution"]
        Action["Domain Action Execution<br/>Guaranteed Type Safety"]
    end

    Payload -->|"Raw Data"| DTO
    DTO -->|"Validated & Sealed Instance"| Action

Invariant Rules for DTOs

  1. Immutability: Once instantiated, DTO properties cannot be modified. They represent an unalterable snapshot of intent.
  2. Strict Typing: Every property must have an explicit scalar, enum, or nested DTO type. Loose untyped values are rejected.
  3. Context Insulation: DTOs do not contain database query methods or infrastructure dependencies. They are pure data carriers.

4. Aggregates and Invariant Enforcement (“The Law”)

In Domain-Driven Design, an Aggregate is a cluster of domain objects treated as a single unit for data changes. The Aggregate Root is the sole external access point.

stateDiagram-v2
    [*] --> Draft: Initialize Aggregate
    Draft --> Validating: Submit for Approval
    Validating --> Draft: Invariant Check Failed (Validation Error)
    Validating --> Approved: Invariant Check Passed
    Approved --> Active: Activate Entity
    Active --> Suspended: Administrative Hold
    Suspended --> Active: Release Hold
    Active --> Terminated: Sunsetting / Decommissioning
    Terminated --> [*]

The Invariant Mandate

Business rules within an aggregate are non-negotiable laws. Examples across domains include:

  • Accounting: Journal voucher debits must equal credits with zero variance.
  • Inventory: Stock in a physical bin cannot drop below zero.
  • Sales: A closed order’s committed prices cannot be retroactively altered without issuing a formal credit note.
  • PIM: A product cannot syndication to commercial channels until its completeness score reaches 100%.

Invariants must hold true before a transaction begins and immediately after it finishes.


5. Domain Event Choreography & Anti-Corruption Layers

Cross-domain collaboration relies on asynchronous domain events rather than direct cross-database relationships:

sequenceDiagram
    autonumber
    participant OMS as Sales / OMS
    participant Bus as Domain Event Bus
    participant ACL as Accounting ACL
    participant FMS as Accounting / FMS

    OMS->>OMS: Finalize Order (Action)
    OMS->>Bus: Emit: SalesOrderApproved
    Bus-->>ACL: Route Event to Subscribers
    ACL->>ACL: Translate OrderDTO -> JournalVoucherDTO
    ACL->>FMS: Execute: PostReceivableJournal (Action)
    FMS->>FMS: Assert Zero-Sum Invariant
    FMS->>Bus: Emit: JournalVoucherPosted

The Anti-Corruption Layer (ACL)

When a domain consumes an event from an upstream domain, it passes the payload through an Anti-Corruption Layer (ACL):

  • Translates upstream nomenclature into local ubiquitous language.
  • Shields internal aggregate structures from changes in upstream event schemas.
  • Maps external identifiers to local polymorphic references (Owner Type + Owner Alias).

6. Continuous Auditability & Reversals

In enterprise architecture, destructive mutations (UPDATE and DELETE queries that erase historical context) violate operational compliance:

  • Append-Only Event Logs: All state transitions record actor identities, origin channels, timestamps, and before/after state diffs.
  • Compensating Transactions: When an operation must be rolled back after commitment, the system generates an explicit compensating entry (e.g., an opposite journal voucher, a stock restock entry, or a credit note) rather than deleting historical records.
  • Audit Trails: Every aggregate root maintains an unbroken audit trajectory that can be inspected for forensic and statutory compliance.

7. Next Steps

Review how these architectural patterns are applied across specific domain blueprints:

Our Premium Sponsors

Obelaw is proudly open-source. Continued development, bug fixes, and community support are made possible by the generosity of our sponsors.

Sponsor Obelaw