Google Sign-In Required

Use your company Google account to access the BetterFleet private content.

Back to private home

BetterFleet Support Private
Skip to content
BetterFleet Dev Wiki
0031 - Adopt DDD, Hexagonal Architecture, and CQRS for Python Domain Services
Initializing search
    bf-dev
    • Home
    • Process
    • Products
    • Reference
    • Decisions
    • Work
    • Operations
    bf-dev
    • Home
      • Process Handbook
      • BetterFleet Workflow Map
      • Product Development System
      • Product Engineering Workflow
        • Process Workflows
        • Work Intake and Weekly Planning
        • Product Engineering Workflow in Linear
        • Product Engineering Delivery
        • Agent Guidance
        • Workflow
        • Skills
        • Skill Sources
        • Process Guides
        • GitLab Feature Flags
        • In-App Docs Authoring
        • Release Notes
        • Process Templates
        • Release Plan: <title>
      • Process Publishing
      • Product overview
        • General Reference
          • Core Domain Training
          • System Topology
          • Two-Axis Ontology Model
          • Ontology Primer
          • Worked Example
          • Evidence, Ownership, and Lineage
          • Energy Management
          • Standards and Protocol Map
          • Charging, Roaming, and Commercial Model
          • Charge Planning and Operations
          • Cross-Cutting Domains
          • Domain Coverage Matrix
        • BetterFleet Product Ontology
        • Core Operations Data Ontology
        • BetterFleet R&D Plan
        • Index
        • Architecture
        • Manage Product Capabilities
        • Manage Data and State
        • Manage Service Interaction Flows
        • Manage Reference
        • Manage Internal Application Diagrams
          • Manage Authorization And Permissions
          • bf-manage-core Auth and Authorization Model
          • Manage Authorization and Permissions
          • bf-manage-web Auth and Permission Model
          • Manage Service Catalog
          • bf-depot-sim
          • bf-digital-twin (Manage Role)
          • bf-fleet-health
          • bf-manage-connect
          • bf-manage-core
          • bf-manage-incidents
          • bf-manage-roaming
          • bf-manage-sitepwrmon
          • bf-manage-web
          • bf-schedule-creator (Manage Role)
          • bf-support-microsite
          • bf-telematics
        • Index
        • Architecture
        • Plan Reference
        • Plan Internal Application Diagrams
        • Plan Migration and Flags
        • Plan Simulation Request Lifecycle
          • Plan Service Catalog
          • bf-bnl-schedule-analysis-compute
          • bf-bnl-settings
          • bf-bnl-ui
          • bf-digital-twin (Plan Role)
          • bf-route-modelling
          • bf-schedule-creator (Plan Role)
      • Where to Ask Product Questions
      • Reference
        • Platform Reference
        • Platform Architecture
        • Script Runtime Model
        • Compose Profiles and Modes
        • Repository Map
        • Monolithic Git Transition FAQ
        • Monolithic Git Sizing
        • CI and Release Integration
        • Shared Reference
        • Shared Infrastructure Architecture
        • Secrets and Env Strategy
        • Vendors and Local Dependencies
        • System Reference
        • Cloud Data Dependencies
        • Ports and URLs
        • Service Matrix
          • API Docs
          • OCPI API Docs
          • OCPP API Docs
          • OSCP API Docs
          • VDV API Docs
          • Yard State API Docs
        • System Design
        • System Design: BBA Microgrid Controller Generic Packet Translation
        • System Design: Depot Simulation
        • System Design: IoT Sensor Packet
        • System Design: Microgrid Energy Orchestration
          • System Design: OCPP Profile 3 And ISO 15118 PKI
          • Architecture: BetterFleet OCPP Profile 3 and ISO 15118 PKI
          • Specification: BetterFleet OCPP Profile 3 and ISO 15118 Certificate Lifecycle Management
          • System Design: On-Prem Control
          • Challenge
          • Specification: BetterFleet On-Prem Continuity Control
          • System Design: OSCP
          • OSCP Protocol Documentation
          • Depot Sim Testing Requirements
          • System Design: OSCP Flexibility Provider Domain
      • Decisions
        • Architecture Decision Records
        • 0001 - Record architecture decisions
        • 0002 - Cognito for Authentication and Authorisation
        • 0003 - AWS Amplify for Authentication
        • 0004 - DynamoDB for default database
        • 0005 - Data Persistence
        • 0006 - Trunk-Based Development
        • 0007 - Generalised principle for automation
        • 0008 - Naming Repositories, Services, and URLs
        • 0009 - Use Timezone Aware DateTimes and UTC
        • 0010 - Use semantic release
        • 0011 - Centralized feature flag repository
        • 0012 - Use Named Exports in Storybook
        • 0013 - RESTful TITLE GraphQL
        • 0014 - Service Granularity
        • 0015 - Async/co-routine exception handling pattern
        • 0016 - Logging & log levels
        • 0017 - Instantiated Models
        • 0018 - Repository Pattern for Database Access
        • 0019 - Use of Design Tokens in TypeScript React Application
        • 0020 - API backwards compatibility and versioning
        • 0021 - Alembic Migration strategy
        • 0022 - Consistent react-hook-form usage
        • 0023 - Domain Event-Driven Architecture
        • 0024 - Domain Event Bus Tech Stack
        • 0025 - No enum types in DB table columns
        • 0026 - In-Memory Ormar Stores for Repository testing
        • 0027 - Storing Tab State in Query and Local Storage
        • 0028 - Adopt OpenTelemetry Semantic Conventions for Structured Logging
        • 0029 - Adopt RFC 9457 for HTTP Error Responses
        • 0030 - Use GitLab registry and Terraform state for ECS services
        • 0031 - Adopt DDD, Hexagonal Architecture, and CQRS for Python Domain Services
          • Date
          • Status
          • Context
          • Decision
          • Consequences
      • Work
        • Active Work
          • Work: Bba Microgrid Controller
          • Implementation Specification: BBA Microgrid Controller
          • BBA Microgrid Controller Deliverables (Stories)
          • Work: BFDev Monolithic Git
          • Challenge
          • Specification: BFDev Monolithic Git v2
          • BFDev Monolithic Git v2 Stories
          • Work: Complex Circuit Load Balancing
          • Implementation Specification: Complex Circuit Load Balancing
          • Complex Circuit Load Balancing Deliverables (Stories)
            • COR-10 and COR-11 Consolidation Review
          • Work: Dispatch Reliability and Reconciliation
          • Challenge
          • Specification: Dispatch Reliability and Reconciliation
          • Dispatch Reliability and Reconciliation (Unit User Stories)
            • Dispatch populated vehicle cards grey surface snapshot
            • Dispatch Visual Review
          • Work: Enable Scheduled Managed Charger Access
          • Challenge: Enable Scheduled Managed Charger Access
          • Specification Exploration Dossier: Enable Scheduled Managed Charger Access
          • Specification Review: Enable Scheduled Managed Charger Access
          • Specification: Enable Scheduled Managed Charger Access
          • Work: Guided Cut-Off and Release Orchestration
          • Specification: Guided Cut-Off and Release Orchestration
          • Guided Cut-Off and Release Orchestration (Unit User Stories)
          • Work: Production Deployment Validation
          • Challenge
          • Work: Scheduled Report Parity
          • Specification: Scheduled Report Parity
          • Work: Telematics
          • Telematics EventBridge Path
          • Telematics Ingress Architecture
          • Specification: Telematics Migration into bf-manage-core with 5-Minute Freshness and Health Visibility
          • Telematics Core Migration MVP (Implementation-Time BDD)
          • Work: Vector Derms
          • Implementation Specification: Vector DERMS
          • Vector DERMS Deliverables (Stories)
          • Work: Visiting Vehicle Charging Visibility
          • Specification: Visiting Vehicle Charging Visibility
          • Visiting Vehicle Charging Visibility (Unit User Stories)
          • Work: Workspace Owned Stripe Roaming
          • Specification: Workspace-Owned Stripe Credentials for Roaming Payments
        • Backlog Work
          • Work: Microgrid
          • Microgrid Backlog Stories
          • Work: Mobile Ops Companion
          • Challenge
          • Specification: Mobile Operations Companion v1
          • Mobile Operations Companion Deliverables (Stories)
          • Work: Oscp
          • OSCP Backlog Stories
        • Archived Work
          • Work: Code Canonical Orchestration
          • Challenge
          • Specification: Product Engineering Workflow
          • Product Engineering Workflow Deliverables (Unit User Stories)
          • Work: Release Notes Automation
          • Release Plan: Release Notes Automation
          • Release Notes Automation Backlog Stories
      • Operations
      • Onboarding Runbook
        • Operations Runbooks
        • Production Hotfix Release
        • Staging Hotfix Release
        • Manage Staging Release Validation
        • Terraform Plan Dry Runs
        • Operations Tooling
        • Code Indexing
        • Operations Evidence
        • Database Restoration Test Report
      • Daily Operations Runbook
      • Testing Guide
      • Troubleshooting
    • Date
    • Status
    • Context
    • Decision
    • Consequences
    1. Home
    2. Decisions
    3. Adrs
    decisions general

    0031 - Adopt DDD, Hexagonal Architecture, and CQRS for Python Domain Services¶

    Date¶

    2026-05-11

    Status¶

    Proposed

    Context¶

    BetterFleet Python services increasingly need a consistent structure for domain behaviour, persistence, API boundaries, events, and tests.

    bf-manage-core already contains detailed DDD and hexagonal architecture guidance in docs/DDD/, including a target layering model with domain, service, ports, adapters, API, CQRS command/query split, unit of work, repositories, domain events, event store, outbox, and projections. The local bf-python-ddd-architecture skill also encodes the same rules for implementation and review work.

    The current cross-product ADR set records related decisions such as repository-based persistence, domain events, event bus direction, service granularity, database choices, and repository testing. It does not yet record the wider architectural direction that links those decisions together.

    Without a cross-product ADR, new services and substantial refactors can drift into inconsistent layering, direct ORM access from business logic, thin domain models, router-heavy workflows, or mixed command/query paths.

    Decision¶

    We will adopt Domain-Driven Design, hexagonal architecture, and CQRS as the default architectural direction for new Python domain services and substantial Python domain refactors.

    For this decision:

    • Domain code owns aggregates, entities, value objects, domain events, invariants, and state transitions.
    • Domain code must not depend on FastAPI, Pydantic API DTOs, ORM models, database sessions, adapter implementations, or infrastructure clients.
    • Service command code coordinates use cases through repository and unit-of-work ports. Business rules stay in domain objects.
    • Service query code reads through query ports and returns read models or DTOs, not aggregates.
    • Ports define task-focused contracts for repositories, queries, unit of work, event stores, outboxes, event buses, and external lookups.
    • Adapters implement ports and contain persistence, ORM, external API, event bus, and infrastructure details.
    • API routers stay thin: they handle HTTP concerns, auth wiring, request/response DTOs, dependency injection, and translation to service calls.
    • Commands and queries are separated. Commands mutate aggregates through command-side repositories or unit-of-work ports. Queries use read-optimized query ports or projections.
    • State-sourced aggregates are the default persistence pattern.
    • Event sourcing is used selectively where audit history, replay, dispute handling, or time-ordered domain behaviour justify the extra cost.
    • State-sourced aggregates may emit domain events for projections and downstream reactions.
    • Reliable asynchronous publication should use an outbox-style flow rather than direct publication from routers or domain objects.
    • Cross-context interaction should use domain events, anti-corruption adapters, or explicit ports rather than direct imports of another context's aggregates or persistence models.
    • Tests should follow the same boundaries: domain tests for invariants, service tests with fake ports/adapters, adapter tests for persistence mapping, API tests for HTTP translation, and event-handler tests for idempotency and projection behaviour.

    This ADR sets the cross-product direction. Detailed naming, placement, import, event, and testing rules remain in bf-manage-core/docs/DDD/ and the bf-python-ddd-architecture skill.

    Legacy services are expected to migrate incrementally. This decision does not require broad rewrites without a product or technical reason.

    Consequences¶

    New Python domain work has a clearer default structure and a shared review standard.

    Domain behaviour becomes easier to test without databases, HTTP frameworks, or external services.

    Persistence, API, and infrastructure choices can change with less impact on domain and service code.

    CQRS lets read paths use simpler, read-optimized models while command paths keep invariants in aggregates.

    Event-driven work aligns with 0023 - Domain Event-Driven Architecture and 0024 - Domain Event Bus Tech Stack, while repository work aligns with 0018 - Repository Pattern for Database Access.

    The approach adds more explicit files, ports, adapters, and tests than simple CRUD code. Small scripts, prototypes, or low-risk glue code may not need the full structure.

    Teams need to be deliberate when applying this to existing services. Incremental migration should keep production paths stable and avoid mixed patterns inside one aggregate boundary.

    If this ADR is accepted, future Python service designs and reviews should treat unexplained violations of these boundaries as architecture issues.

    Made with Material for MkDocs
    BFDev Docs Assistant
    New conversation?
    Ask one focused question at a time, this helps the assistant provide accurate answers about what's been implemented in BetterFleet.