Status quo
- I created an aggregate, let's call it
Foo
. It has two entities within itself, let's call themFoo
&Bar
. - You can mutate things by calling the aggregate's public methods. E.g.
$foo->doSomething()
. I.e. the aggregate itself has an repository injection. Looks this way
class Foo
{
/**
* @var FooRepositoryInterface
*/
private $fooRepository;
/**
* @var BarRepositoryInterface
*/
private $barRepository;
/**
* @var FooEntity|null
*/
private $fooEntity;
/**
* @var BarEntity|null
*/
private $barEntity;
public function __construct(
FooRepositoryInterface $fooRepository,
BarRepositoryInterface $barRepository,
?FooEntity $fooEntity,
?BarEntity $barEntity
) {
$this->fooRepository = $fooRepository;
$this->barRepository = $barRepository;
$this->fooEntity = $fooEntity;
$this->barEntity = $barEntity;
}
public function doSomething()
{
// check if the entities are not nulls or nulls. Depends on what the method does
// create/mutate entities and persist them
// assign entities to $this
}
public function getSomethingA(string $bla)
{
return $this->foo->getSomething();
}
public function getSomethingB()
{
return $this->bar->getSomething();
}
}
I.e. the entire persistence "logic" is within an aggregate. As there are dependencies on the repositories, makes sense to erect such aggregates via factories. E.g.
class FooFactory {
/**
* @var FooRepositoryInterface
*/
private $fooRepository;
/**
* @var BarRepositoryInterface
*/
private $barRepository;
public function __construct(
FooRepositoryInterface $fooRepository,
BarRepositoryInterface $barRepository
) {
$this->fooRepository = $fooRepository;
$this->barRepository = $barRepository;
}
public function createForFooBaring(): Foo
{
return new Foo($this->fooRepository, $this->barRepository, null, null);
}
}
And then in a use-case e.g. or a service, just inject the factory and you are good to mutate things.
class UseCase {
private $fooFactory;
public function __construct(FooFactory $fooFactory)
{
$this->fooFactory = $fooFactory;
}
public function execute(Input $input): Output
{
$foo = $this->fooFactory->createForFooBaring();
$foo->doSomething($input->getSomething()); // that's it. The data is transactionally (if it's a must) persisted. You read the business logic here, not technical implementation of inserts or updates. Awesome!
}
}
Problem
Now, let's assume I want to find an aggregate root in the use-case above. Smells like it's right about time to inject a repository here, find entities and use a factory to establish an existing aggregate.
But. First of all, the factory itself has all of the dependencies needed, yet if we do something in the factory like
public function findByEmail(string $email): ?Foo
{
$fooEntity = $this->fooRepository->findByEmail($email);
// let's assume here we found it
$barEntity = $this->barRepository->findBySomeForeignKey($fooEntity->getBarFk());
// let's assume here we found it
return new Foo($this->fooRepository, $this->barRepository, $fooEntity, $barEntity);
}
Nice. But. It's no longer a factory sorta. Could rename it to say... uhm... FooManager/Service e.g. and then it fits an idea.
Hence the question. How do you normally find an aggregate in DDD?