Lesson 2: Tactical DDD
DDD PHP
Aggregate
An Aggregate is an entity or group of entities that is always kept in a consistent state. Aggregates are very explicitly present in the Command Model, as that is where change is initiated and business behaviour is placed.
Let's create our first Aggregate Product.
namespace App\Domain\Product;
use Ecotone\Modelling\Attribute\Aggregate;
use Ecotone\Modelling\Attribute\Identifier;
use Ecotone\Modelling\Attribute\CommandHandler;
use Ecotone\Modelling\Attribute\QueryHandler;
#[Aggregate]
class Product
{
#[Identifier]
private int $productId;
private int $cost;
private function __construct(int $productId, int $cost)
{
$this->productId = $productId;
$this->cost = $cost;
}
#[CommandHandler]
public static function register(RegisterProductCommand $command) : self
{
return new self($command->getProductId(), $command->getCost());
}
#[QueryHandler]
public function getCost(GetProductPriceQuery $query) : int
{
return $this->cost;
}
}Aggregate attribute marks class to be known as Aggregate
Identifier marks properties as identifiers of specific Aggregate instance. Each Aggregate must contains at least one identifier.
CommandHandler enables command handling on specific method just as we did in Lesson 1. If method is static, it's treated as a factory method and must return a new aggregate instance. Rule applies as long as we use State-Stored Aggregate instead of Event Sourcing Aggregate.
QueryHandler enables query handling on specific method just as we did in Lesson 1.
Now remove App\Domain\Product\ProductService as it contains handlers for the same command and query classes.
Before we will run our test scenario, we need to register Repository.
Repository
Repositories are used for retrieving and saving the aggregate to persistent storage. We will build an in-memory implementation for now.
Repository attribute marks class to be known to
Ecotoneas Repository.We need to implement some methods in order to allow
Ecotoneto retrieve and save Aggregate. Based on implemented interface,Ecotoneknowns, if Aggregate is state-stored or event sourced.canHandle tells which classes can be handled by this specific repository.
findBy return found aggregate instance or null. As there may be more, than single indentifier per aggregate, identifiers are array.
save saves an aggregate instance. You do not need to bother right what is
$metadataand$expectedVersion.
Let's run our testing command:
Have you noticed what we are missing here? Our Event Handler was not called, as we do not publish the ProductWasRegistered event anymore.
Event Publishing
In order to automatically publish events recorded within Aggregate, we need to add method annotated with AggregateEvents. This will tell Ecotone where to get the events from.
Ecotone comes with default implementation, that can be used as trait WithEvents.
Let's run our testing command:
Congratulations, we have just finished Lesson 2. In this lesson we have learnt how to make use of Aggregates and Repositories. Now we will learn about Converters and Metadata
Last updated
Was this helpful?