Let's say I have three aggregates in the same bounded context:
PhoneAggregate
ServiceCenterAggregate
ServiceWorkAggregate
ServiceWorkAggregate
references both PhoneAggregate
and ServiceCenterAggregate
by id.
Now when new ServiceWorkAggregate
is created, a POST request is sent:
{
"phone_id": uuid,
"servicecenter_id": uuid
}
I can't just create a new aggregate using those IDs, I need to perform some checks: at least confirm that those UUIDs are valid entity IDs, maybe also perform some business logic checks involving current state of selected PhoneAggregate
and ServiceCenterAggregate
. What is the best way to do this?
ServiceWorkAggregate
application service or domain service include repositories for all three aggregates.phone_id
and servicecenter_id
fields inside ServiceWorkAggregate
use value objects Repairable
and ServiceLocation
, which would be immutable copies of those aggregates, fetched from the same table etc. Then I would need only one repository in my servicework application service.A lot of developpers think that the domain layer of an application must be designed as a single normalized data model, like an RDBMS schema. This is not true, and is the cause of many suffering.
The fact that you have 3 different contexts does not mean that the ServiceWork
context is not able to manipulate data from the other two concepts. It only means that this context is not able to authoritatively change these concepts states. Put in other words, ServiceWorkRepository
can read data about the Phone
context, but it cannot create, delete or change a phone's state.
Also, you don't need the same information regarding a phone's state when you are changing a phone's context, or when you are changing a service work's context. This allows you to have two different models for the same concept, depending on the current context. Let's call them ServiceWorkContext.PhoneEntity
and PhoneContext.PhoneEntity
. These entities can map to the same table/columns in the database, but they can have different level of details: flattened relationships, fewer columns read, and do not allow changing the state.
This concept is called polysemic domain modeling.
In other words, you have the following classes:
MyApp.Domain.PhoneContext.PhoneAggregate
represents the phone's state when you want to create or update a phone, and validates business rules regarding these use casses.
MyApp.Domain.ServiceWorkContext.PhoneEntity
represents a simplified readonly copy of the phone's state, used when trying to create / update a service work. For instance it can contain a single Country property (which maps to Phone->Owner->Address->Country->Name) to be compared with the ServiceCenter's country, if you have a business rule stating that the phone's country must match the service center's. In that case, you may not need to read Phone.Number from the database for instance.