Skip to main content

Domain Model

Table of Contents

Introduction

This document systematically explains the DDD design and implementation of the Sparrow domain model, covering entity definitions, aggregate root design, domain event modeling, value objects and snapshot mechanisms, command processing, event sourcing, and projections. The document is centered around Go language generics and interfaces, combined with contracts from the repository and use case layers, providing practical suggestions and best practices to help architects and domain experts correctly delineate aggregate boundaries, encapsulate business rules, and implement high-cohesion, low-coupling domain models in complex business scenarios.

Project Structure

This project adopts a clean architecture layering, with the domain model located in the core layer, constructing a unified domain abstraction around entities, aggregate roots, domain events, commands, and snapshots; the use case layer provides interface contracts for event storage, repositories, and executors; the adapter layer interfaces with external systems through HTTP, message buses, event streams, etc.

Diagram Sources

  • pkg/entity/entity.go

  • pkg/entity/base_entity.go

  • pkg/entity/agg_root.go

  • pkg/entity/base_agg_root.go

  • pkg/entity/domain_event.go

  • pkg/entity/domain_base_event.go

  • pkg/entity/cmd.go

  • pkg/entity/snapshot.go

  • pkg/entity/paginated_result.go

  • pkg/usecase/eventstore.go

  • pkg/usecase/repo.go

  • pkg/usecase/executor.go

  • pkg/entity/task.go

  • pkg/entity/session.go

  • pkg/entity/saga.go

  • Entity and Base Entity

    • The entity interface defines a unified identity and timestamp access contract, while the base entity provides standard fields and constructors, ensuring all entities have consistent lifecycle management capabilities.
  • Aggregate Root and Base Aggregate Root

    • The aggregate root interface adds aggregate type, version control, command processing, event application and replay, snapshot loading, and other capabilities on top of the entity; the base aggregate root provides common implementations for version numbers, uncommitted event lists, event appending, and marking commits.
  • Domain Event and Base Event

    • The domain event interface defines event identity, type, aggregate association, version, and timestamp; the base event implements common capabilities such as JSON serialization, version setting, and payload decoding.
  • Command and Executor

    • The command interface defines command identity and target aggregate; the executor uniformly processes business intent, forming a standard "command -> aggregate root -> event" flow.
  • Snapshot and Pagination

    • The snapshot interface and generic snapshot implementation provide aggregate state persistence and reconstruction capabilities; the paginated result generic carries query return structures.
  • Use Case Layer Contracts

    • The event storage interface defines event persistence, query, and snapshot capabilities; the repository interface provides entity CRUD and conditional queries; the executor interface unifies command execution.

The diagram below shows the end-to-end flow from command to event storage and projection, reflecting the collaborative approach of event sourcing and CQRS:

Diagram Sources

Detailed Component Analysis

Entity and Base Entity

  • Design Points
    • Unified entity interface and base entity struct ensure all entities have consistent identity, creation, and update time management.
    • By embedding the base entity, duplicate implementation of ID and timestamp-related methods is avoided, improving consistency and maintainability.
  • Complexity and Performance
    • Access and setting operations are O(1), with memory usage linearly related to the number of fields.
  • Best Practices
    • Strictly distinguish between entities and value objects: use entities only when identity persistence is needed across changes; value objects should be immutable and used through replacement semantics.

Diagram Sources

  • pkg/entity/entity.go

  • pkg/entity/base_entity.go

  • Design Points

    • The aggregate root interface adds aggregate type, version control, command processing, event application and replay, snapshot loading, and business validation responsibilities on top of the entity.
    • The base aggregate root provides common capabilities such as version number auto-increment, uncommitted event list management, event validity checking, and update time synchronization.
  • Event Sourcing and Version Control

    • Each new event increments the aggregate version and synchronizes the update time; consistency checks for event ID, type, and aggregate ID ensure event stream integrity.
  • Snapshot Optimization

    • For aggregates with long histories, snapshots can be used to reduce the number of replayed events and improve reconstruction performance.

Diagram Sources

  • pkg/entity/agg_root.go

  • pkg/entity/base_agg_root.go

  • Design Points

    • The domain event interface defines event identity, type, aggregate association, version, and timestamp; the base event implements common capabilities such as JSON serialization, version setting, and payload decoding.
    • Event payload supports generic decoding, facilitating reuse of event carriers between different aggregates.
  • Event Consistency

    • Non-empty and consistency checks for event ID, type, and aggregate ID prevent event stream contamination and erroneous replay.

Diagram Sources

  • pkg/entity/domain_event.go

  • pkg/entity/domain_base_event.go

  • Design Points

    • The command interface defines command identity and target aggregate ID; the base command struct provides common fields and accessors.
    • The executor uniformly receives any action, delegates to the aggregate root to process the command and generate domain events, forming a clear command processing pipeline.
  • Collaboration with Aggregate Root

    • The executor calls the aggregate root's command processing method, the aggregate root applies the event and returns uncommitted events, which are then persisted and published.

Diagram Sources

  • pkg/entity/cmd.go

  • pkg/usecase/executor.go

  • Snapshot Interface

    • Provides aggregate ID, type, version, creation time, and state serialization/deserialization capabilities; generic snapshot implementation simplifies state carrying.
  • Paginated Result

    • Generic paginated result carries datasets, totals, page numbers, and sizes, facilitating standardized output for repositories and query results.

Diagram Sources

  • pkg/entity/snapshot.go

  • pkg/entity/paginated_result.go

  • Event Storage

    • Basic capabilities: save events, read events, load aggregates; Extended capabilities: query by version/type/time range, paginated query, aggregate version query; Snapshot capabilities: save and read latest snapshot; Batch capabilities: batch save events.
  • Repository

    • Provides generic repository interface covering save, query, update, delete, batch operations, pagination, conditional query, counting, and random sampling.
  • Executor

    • Unified command execution entry, shielding command type differences, focusing on business intent and event generation.

Diagram Sources

  • pkg/usecase/eventstore.go

  • pkg/usecase/repo.go

  • pkg/usecase/executor.go

  • Task Entity

    • Contains type, status, payload, result, error, priority, retry count, and time window fields; provides start, complete, fail, cancel, and retry judgment behaviors.
  • Session Entity

    • Contains data dictionary and expiration time; provides expiration check, renewal, and data read/write/delete behaviors.
  • Transaction Entity

    • Transaction steps define handlers and compensation information; transaction entity contains step index, status, and timestamps; provides factory functions and ID generation strategies.

Diagram Sources

  • pkg/entity/task.go

  • pkg/entity/session.go

  • pkg/entity/saga.go

  • pkg/entity/base_entity.go

  • Process Description

    • The client submits a command, the executor delegates to the aggregate root to process the command, the aggregate root generates domain events based on business rules and applies them to its own state, then persists the events and publishes them to the event bus/stream.
  • Key Points

    • Event version and aggregate version are incremented synchronously, and the uncommitted event list is used for batch publishing and idempotent processing.
    • LoadFromEvents and LoadFromSnapshot support aggregate reconstruction and performance optimization.

Diagram Sources

  • pkg/usecase/executor.go

  • pkg/entity/agg_root.go

  • pkg/usecase/eventstore.go

  • Aggregate Root and Event Store

    • The aggregate root persists events and loads aggregates through the event store interface, and the event store implementation can be based on a database or event stream platform.
  • Aggregate Root and Repository

    • The repository provides entity-level persistence capabilities, suitable for non-event sourcing scenarios or regular persistence of value objects/entities.
  • Executor and Command

    • The executor uniformly parses and delegates commands, and the aggregate root is responsible for business rules and event generation.

Diagram Sources

  • pkg/entity/cmd.go

  • pkg/usecase/executor.go

  • pkg/entity/agg_root.go

  • pkg/usecase/eventstore.go

  • pkg/usecase/repo.go

  • Event Store Selection

    • Database-based event storage is suitable for scenarios requiring strong consistency and complex queries; event stream-based storage is suitable for high throughput and eventual consistency scenarios.
  • Snapshot Strategy

    • Regularly generate snapshots for aggregates with long histories to reduce the number of replayed events and improve reconstruction performance.
  • Batch Operations

    • Use batch event saving and paginated queries to reduce network and storage overhead.
  • Version Control and Concurrency

    • Avoid concurrent write conflicts through expected version parameters and optimistic locking mechanisms.

Troubleshooting Guide

  • Event ID/Type/Aggregate ID Validation Failure
    • Phenomenon: Empty event ID, empty event type, empty event aggregate ID or inconsistency with aggregate root, duplicate event ID.
    • Handling: Check the mapping from command to event and the aggregate root event appending logic to ensure event metadata is complete and unique.
  • Version Mismatch
    • Phenomenon: Error reporting version inconsistency when saving events.
    • Handling: Read the latest aggregate version before executing the command, and use the expected version parameter for saving.
  • Event Replay Exception
    • Phenomenon: Failed to reconstruct aggregate state from event stream.
    • Handling: Confirm event order and ApplyEvent implementation are correct, and introduce snapshots to accelerate reconstruction if necessary.

Sparrow's domain model is centered around interfaces and base implementations, through mechanisms such as aggregate roots, domain events, commands, and snapshots, to fully support event sourcing and CQRS architecture. Combined with generics and unified contracts, it ensures both cohesion and testability of domain logic while providing flexible extension space. It is recommended in actual projects:

  • Clearly define aggregate boundaries and invariants to ensure business rules are centralized within aggregate roots.
  • Reasonably use snapshots and event replay to balance performance and consistency.
  • Isolate technical implementation differences through executor and event storage interfaces to improve maintainability.

Appendix