I have a question about the inverse relationship between aggregate root's (AR). When the relationship between two aggregate root's has been determined, i.e., after some operation an 1-1 or 1-N relation has been set or altered. How does the inverse relation know about it's existens when you are only allowed to change one aggregate root.
My problem is that the other aggregate root uses business logic that is depending on the inverse relationship between the aggregate roots.
The code beneath is an example, thus maybe the used names can be a little strange, but the important part is the relationship between the AR's. For the example I use two aggregate roots: Person and Organization and employment as an business process. The problem is that the behavoir set only one side of the relation. Hereby the example:
class Organization
{
// parameter is a value object representing the Person AR.
public function startEmployment(Person $person)
{
if (in_array($person, $this->employees)) {
throw new Exception("Person is already an employee");
}
$this->employees[] = $person;
}
}
With the example above I can change a single AR and the business logic is on the right place. But when I look at the other AR, thus Person, I discover some troublesome area's. For example, when the business requirement is: Person may not change living location when being employed (could think of a better example).
class Person
{
public function changeLivingLocation(Location $location)
{
// what information and where do i get it from?
if (...) {
throw new Exception("May not change living location");
}
$this->livingLocation = $location;
}
}
The comment already describes the problem. Where do i get the information from? The AR Organization contains all the knowlegde about employments. The simplest solution is to query the Organization table, but then I'm introducing a infrastructure layer in the domain layer. This goes against the clean architecture principles (or other DDD examples I have seen). I could introduce a domain service that performs the business logic, but then within the domain service I could query the Organization repository/service. Although, the still there is something from the infrastructure layer mentioned within the domain layer.
Question(s):
// Update after reading first answer After reading the answer and evaluation the idea that a repository interface exists within the domain layer I moved it to a domain service. At least, for how I see a domain service now. I can up with the following implementation the policy evaluates the business logic which ij tu n is based on a repository count .
class Person
{
public function changeLivingLocation(MayChangeLocation $policy, Location $location)
{
if ($policy->evaluate ($this)) {
throw new Exception("May not change living location");
}
$this->livingLocation = $location;
}
}
class MayChangeLocation // effective the domain service
{
public function __construct(RepositoryInterface $repository) {
$this->repository = $repository;
}
public function evaluate (Person $person)
{
$organizations = $this->repository->getOrganzationsEmployesPerson($person->getId());
// here real business logic is applied
if (count($organizations) > 0) {
return false;
}
return true;
}
}
Just curious about review comments :)
The simplest solution is to query the Organization table, but then I'm introducing a infrastructure layer in the domain layer.
That's what you should do, not through the infrastructure layer, but through a query in the domain layer.
You can pass the repository to load the Organization
AR in the Person
one, or use a service, or a Query
in CQRS terms.