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
- Immutability: Once instantiated, DTO properties cannot be modified. They represent an unalterable snapshot of intent.
- Strict Typing: Every property must have an explicit scalar, enum, or nested DTO type. Loose untyped values are rejected.
- 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: