Entity
A domain object defined by its identity rather than its attributes.
An entity is a domain object that has a distinct identity persisting over time. Two entities can have the same attributes but remain different objects because their identities differ. Think of an order or a user: each has a unique ID that distinguishes it from all others.
This is the key difference between entities and value objects. A value object is defined by its attributes: two Money instances with the same amount and currency are interchangeable. An entity is defined by who it is, not what it contains. A User with ID abc-123 is always that specific user, even after changing their name or email.
In Go, entities are typically structs with an ID field and methods that enforce business rules. The important part is keeping the behavior inside the entity rather than scattering it across service functions.
type Training struct {
uuid string
userUUID string
time time.Time
status TrainingStatus
}
func (t *Training) Cancel() error {
if t.status != TrainingStatusScheduled {
return ErrTrainingNotScheduled
}
t.status = TrainingStatusCanceled
return nil
}
The entity owns its invariants. You can't set the status to an invalid value from outside because the fields are unexported. All state changes go through methods that validate the transition. This is a shift from treating structs as data containers with getters and setters.
Entities live inside aggregates. An aggregate groups an entity (the aggregate root) with other entities and value objects that must stay consistent together. The Repository Pattern loads and saves entire aggregates, not individual entities. This keeps transaction boundaries clean and prevents partial updates.
A common mistake is mapping entities one-to-one with database tables. Your domain model and your storage model are separate concerns. An entity might span multiple tables, or several entities might share one. The repository translates between them.
References
- Introduction to DDD Lite: When microservices in Go are not enough — Introduces tactical DDD in Go with practical refactoring examples. Shows how to model domain entities with encapsulated behavior instead of treating structs as data bags with getters and setters.
- Combining DDD, CQRS, and Clean Architecture in Go — Refactors a training service into a domain entity with methods like ApproveReschedule. Discusses entity discovery and why the noun-equals-entity approach falls short without proper DDD tools.
- Database Transactions in Go with Layered Architecture — Explains why thinking of database tables as entities leads to one-repository-per-table designs. Shows how grouping related entities into aggregates simplifies transaction handling.
- How to implement Clean Architecture in Go (Golang) — Places entities in the domain layer of Clean Architecture, separate from infrastructure concerns. The domain defines entities and business rules with no external dependencies.
- How to use basic CQRS in Go — Discusses returning created entities via REST APIs with CQRS. Shows the pattern of providing entity UUIDs upfront in commands instead of querying for the created entity.
- When using Microservices or Modular Monolith in Go can be just a detail? — Shows how domain entities live in the domain layer regardless of deployment model. The same entities work in both monolith and microservice architectures.
- The Distributed Monolith Trap (And How to Escape It) — Discusses how different bounded contexts can own separate models of the same entity, like the user entity, without coupling. Warns against splitting services based on database entities.
- Is Clean Architecture Overengineering? — Covers domain entities as part of the Clean Architecture domain layer, discussing when the separation is worth the effort and when it becomes overengineering.
- DDD: A Toolbox, Not a Religion — Discusses entities, aggregates, and other DDD building blocks as practical tools rather than rigid rules to follow.
- AMA #1: Clean Architecture, Learning, Event-Driven, Go — Discusses boundaries between application and domain layers, including when domain entities are needed versus when simple CRUD without domain logic is enough.
- Event-Driven Architecture: The Hard Parts — Warns against putting entire entities in events. Shows how a small event that mirrors a database entity can grow to 50 fields, making it hard to maintain and evolve.
- Synchronous vs Asynchronous Architecture — Discusses pitfalls of sharing entities between systems via events. Naive entity synchronization between old and new applications can lead to inconsistencies and out-of-order updates.