Why Entities Matter in DDD: Understanding Identity, Uniqueness, and Aggregate Roots
This article explains the core DDD concepts of entities, value objects, and aggregate roots in Go, detailing how unique identifiers differ from database primary keys, when to use value objects versus primitive types, and best practices for generating and handling IDs within aggregates.
In this second installment of the DDD series, the author explores the concepts of entities, value objects, and aggregate roots, focusing on their role in Go applications.
What Is an Entity?
"An entity is a unique thing that can change over a long period of time. Even if its state changes dramatically, it remains the same entity because it shares the same identity." – from IDDD
Unlike plain data objects that merely map to database tables, an entity carries business behavior and a stable identity that persists across state changes.
Data Object vs. Entity
Data objects (often structs used with ORM tools like Gorm) reflect database columns and are typically "anemic"—they lack behavior. Entities, on the other hand, encapsulate both data and domain logic, making the code more expressive and maintainable.
Unique Identifier
While a database primary key (e.g., id) uniquely identifies a row, the business identifier (e.g., order_id) uniquely identifies the domain entity. These two concepts are distinct; the business identifier should be generated and managed at the domain level.
Representing the Unique Identifier
Two main approaches exist:
Use a value object to wrap the identifier, ensuring immutability and type safety.
Use a primitive type (e.g., int64 or string) when the identifier carries no additional behavior.
When the identifier encodes business rules (e.g., timestamp + business type + random code), a value object is preferred to prevent misuse.
Generating the Unique Identifier
Identifiers can be:
Provided directly by the user (e.g., scanning an ISBN for a book).
Generated by the system, either via a custom generator or database auto‑increment. In the latter case, the identifier should be created before persisting the entity to avoid issues with domain events or map storage.
Typical implementation involves a NextIdentity method on the repository interface, which is called in factories or application services before constructing the entity.
Aggregate Root
An aggregate root is the entry point for a cluster of related entities and value objects. Designing small aggregates helps maintain consistency boundaries and avoids the pitfalls of large, monolithic aggregates.
When an aggregate references another, it should do so via the external aggregate’s global identifier rather than holding a direct object reference.
Conclusion
Entities must have a unique identifier (value object or primitive), generate that identifier before entity creation, and encapsulate rich business behavior. Understanding the distinction between entities and data objects, and applying proper aggregate design, leads to clearer, safer code.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
