Skip to main content

Entity Design

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion
  10. Appendix

Introduction

This document systematically explains entity design in the Sparrow framework, focusing on:

  • Design concepts and responsibilities of the BaseEntity base class
  • Constraints and contracts of the Entity interface
  • Entity identifier generation mechanism
  • Entity lifecycle management, state change methods, and timestamp handling
  • Complete implementation examples and best practices for Task entities
  • Validation rules, data integrity guarantees, and performance optimization suggestions
  • Extended capabilities for event sourcing and aggregate roots

Project Structure

Entity-related code is concentrated in the pkg/entity directory, adopting a layered design of "interface + base class + specific entity", combined with commands, domain events, and aggregate root interfaces, forming a clear DDD/CQRS and event sourcing architecture.

  • Entity Interface: Unifies constraints on entity identity, timestamp access, and update capabilities, ensuring consistent contracts for repositories and persistence layers.
  • BaseEntity Base Class: Provides standard fields and accessors for Id, CreatedAt, and UpdatedAt, unifying entity lifecycle management.
  • Task Entity: Takes tasks as the core business entity, demonstrating state machines, retry strategies, execution duration calculation, and expiration judgment business logic.
  • Aggregate Root Interface and Base Class: Provides version control, event collection and application, snapshot loading, and other event sourcing capabilities.
  • Domain Event and Base Event: Standardizes event metadata and payload processing.
  • Command and Base Command: Standardizes command identity and aggregate root binding.

Entity design follows the "core layer" concept of clean architecture, placing business entities in the innermost layer, providing stable contracts upward through interfaces and base classes, and implementing business rules downward through specific entities. In the event sourcing pattern, aggregate roots assume responsibilities for state changes and event management; commands drive aggregate roots to generate domain events; repositories are responsible for entity persistence; event storage and snapshots are used for state reconstruction and optimization.

BaseEntity Base Class and Entity Interface

  • Design Concepts
    • Unify entity identity and timestamp management to avoid duplicate implementations.
    • Initialize creation and update times through NewBaseEntity to ensure consistency.
  • Key Methods
    • Get/SetID: Read and write entity identity
    • Get/SetCreatedAt: Read-only creation time
    • Get/SetUpdatedAt: Update time synchronized with business changes
  • Lifecycle
    • Set CreatedAt and UpdatedAt when creating
    • Any state change should call SetUpdatedAt to maintain audit consistency
  • Status Enumeration: pending, running, completed, failed, cancelled
  • Key Methods
    • Start/Complete/Fail/Cancel: Drive state machine and timestamp updates
    • CanRetry: Retry determination based on Retries and MaxRetry
    • GetExecutionTime: Calculate execution duration (requires both times to exist)
    • IsExpired: Expiration check based on CreatedAt
  • Identifier Generation
    • NewTask internally calls generateTaskID, combining nanoseconds and milliseconds to ensure uniqueness under high concurrency
  • Timestamp Handling
    • All state changes update UpdatedAt, and Retries are increased on Fail
  • AggregateRoot Interface
    • Inherits Entity, extending aggregate type, aggregate ID, version control, and event sourcing capabilities
    • Provides reserved methods such as HandleCommand, ApplyEvent, LoadFromEvents, LoadFromSnapshot, Validate
  • BaseAggregateRoot Base Class
    • Embeds BaseEntity, maintains Version and uncommittedEvents list
    • AddEvent: Validates event ID/type/aggregate ID consistency, increments version, and updates UpdatedAt
    • MarkEventsAsCommitted: Clears uncommitted event list
    • HasUncommittedEvents: Helper judgment
  • Event and Snapshot
    • DomainEvent interface defines event metadata
    • BaseEvent provides common event structure and payload decoding tools
  • Command Interface and BaseCommand: Standardizes command ID and aggregate root ID binding

  • BaseEvent: Standardizes event metadata, provides payload decoding tool UnmarshalEvent

  • Session: Based on BaseEntity, provides expiration check, data reading/writing, and TTL update

  • Transaction: Implements Entity interface, used for Saga-coordinated transaction orchestration

  • Component Coupling

    • Entity layer depends on Entity interface and BaseEntity base class to ensure consistency
    • Aggregate root layer depends on DomainEvent interface and BaseEvent base class in event sourcing mode
    • Command layer interacts with aggregate roots, implementing non-intrusive state changes through events
  • External Dependencies

    • UUID tools for generating globally unique identifiers
    • JSON encoding/decoding for event payload transmission
  • Identifier Generation
    • Task uses nanosecond + millisecond combination to generate IDs, with high concurrency uniqueness; to further reduce conflict probability, a distributed ID generator (e.g., snowflake algorithm) or database auto-increment primary key strategy can be introduced at the upper layer
  • Timestamp Update
    • All state changes call SetUpdatedAt to ensure audit consistency; batch updates can be merged into a single update to reduce multiple writes
  • Event Sourcing
    • Use BaseAggregateRoot's AddEvent to automatically increment version and update timestamp; it is recommended to batch commit uncommitted events to reduce event storage pressure
  • Query and Sorting
    • Entity implementation of GetCreatedAt can be used for sorting and pagination; it is recommended to establish indexes for common queries in the repository layer to avoid full table scans
  • Memory and Serialization
    • Domain event payloads should use binary or compressed encoding to reduce network transmission and storage overhead

Troubleshooting Guide

  • Event ID/Type/Aggregate ID Validation Failure
    • AddEvent performs strict validation on event ID, type, and aggregate ID; if a panic occurs, check if event construction and aggregate root ID are consistent
  • Duplicate Event ID
    • AddEvent detects duplicate event IDs to avoid event stream contamination; ensure event IDs are unique
  • State Change Abnormality
    • Task's Fail increases Retries, and no automatic retry occurs when MaxRetry is reached; check CanRetry conditions and business processes
  • Expiration and Execution Duration
    • IsExpired is based on CreatedAt; GetExecutionTime requires both times to exist, otherwise returns empty

The entity design of the Sparrow framework is centered around interfaces and base classes, providing highly cohesive, low-coupling domain modeling capabilities through unified timestamp management, strict event sourcing, and aggregate root version control. The Task entity demonstrates a typical implementation of state machines and retry strategies, while Session and Transaction reflect different levels of business abstraction. Combined with templated aggregate root generation and event storage/snapshot mechanisms, developers can quickly build evolvable, observable domain models.

Appendix

  • Validation Rules and Templated Generation
    • README provides usage guidelines for aggregate root templates, supporting field-level validation rules and snapshot functionality, facilitating the generation of aggregate root code that meets business specifications
  • Best Practice Checklist
    • Clearly define entity responsibility boundaries, avoid mixing infrastructure code in entities
    • Use BaseEntity to unify timestamps, ensuring audit consistency
    • Implement business traceability through aggregate root's ApplyEvent and event storage
    • Establish indexes for high-frequency queries, and introduce snapshots to accelerate reconstruction when necessary
    • Use commands to drive aggregate roots, maintaining idempotent and traceable state changes