Domain Layer
Entity
Object with unique identitity (e.g. Customer and Order)
Represents the business object in the problem domain
Have business rules associated
Mutable (from the methods)
Notes
Similar entities can exist on multiple different Bounded Context, but those entities must be contextualized with the BC.
If you need data from an entity which is located on another BC, you might need to:
use anti corruption layer (ACL)
use shared kernel
use integration event (e.g. pubsub or other form of messaging)
Example
Here's an example of SavingsAccount
class which is an entity. Even if the same customer has 2 savings account with the account type, they're still two different savings account which have different account number.
Value Object
Object which represents a simple value (e.g. balance of savings account)
Doesn't have unique identity
Doesn't contain business logic
Should be immutable
Note
Can contains method for non business logic
Example
Aggregate
Aggregate is a way to group related entities and value objects
Each aggregate must have one aggregate root, the aggregate root must be entity
The aggregate root is the only one accessible from outside
Here are a few guidelines that can help you decide whether multiple entities should be grouped into a single aggregate:
Cohesion: The entities in an aggregate should be closely related and should form a single, cohesive concept in the domain.
Consistency: The entities in an aggregate should have a consistency boundary, meaning that their invariants must be maintained within a single transaction.
Bounded Context: The entities in an aggregate should belong to the same bounded context, meaning that they should be part of the same sphere of knowledge in the domain.
Access Patterns: The entities in an aggregate should be accessed together frequently, and it should be common to load all of them in a single query.
Note
If there's some data from another entity from another aggregate (but same BC) that you might need, you can refer to the other entity outside the aggregate by copying the entity id instead the whole entity
Loading the whole aggregate from the database can be more expensive in terms of performance, especially if the aggregate is large or contains a lot of data. However, in DDD the aggregate is considered as a consistency boundary, and it's recommended to load the whole aggregate in a single transaction, this helps to maintain the consistency of the aggregate's invariants, and to avoid inconsistencies between aggregate's parts. Other option is to use lazy loading.
How we group aggregates might change over time, it might be a good idea to split aggregates if it's getting too big
Domain Service
Business logic which doesn't naturally fit into an entity goes here
Usually business logic which involves a multiple domain object (entities)
Note
Domain service operations can be represented by method (inside object) or function, using function is great if you're not planning to make the service stateful
Example
Factory
To create entities and value object consistently
Purely a domain model, different with repository, factory has no infra concern at all
Note
Usually it's needed when the construction is complex and involves creation of other objects, if it's simple then you might not need it
It violates some of the abstraction on the domain object, so use it only when needed
Can also be responsible for creating objects when retreiving the data from data storage layer
Use it when needed, you MIGHT NOT need it if:
construction is fairly simple
construction doesn't involve creation of other objects and all the needed data can be easily passed via object constructor
client is interested with the construction implementation strategy
the class is the sole type, there's no hierarchy (parent-child class relation) involved, which means there's no need to make a constructor with construction type options
Repository
Allows aggregate roots insertion, retrieval, modification, and deletion at storage
Abstraction over the data storage (infrastructure layer)
Note
Usually only the interface is at the domain layer (the interface is considered purely a domain model)
The implementations are at the infrastructure layer
Might have cache layer
Last updated