# Aggregate Introduction

{% hint style="info" %}
Works with: **Laravel**, **Symfony**, and **Standalone PHP**
{% endhint %}

## The Problem

Business rules are enforced in multiple places — a validation here, a check there. When rules change, you update three files and miss a fourth. There's no single source of truth for what an Order or User can do, and no guarantee that business invariants are always protected.

## How Ecotone Solves It

Ecotone's **Aggregates** encapsulate business rules in a single place. Commands are routed directly to the aggregate, which protects its own invariants. Ecotone handles loading and saving — you write business logic, not infrastructure code.

***

This chapter will cover the basics on how to implement an [Aggregate](https://docs.ecotone.tech/message-driven-php-introduction#aggregates).\
We will be using Command Handlers in this section, so ensure reading [External Command Handler](https://docs.ecotone.tech/modelling/command-handling/external-command-handlers) section first, to understand how Command are sent and handled.

## Aggregate Command Handlers

Working with Aggregate Command Handlers is the same as with [External Command Handlers](https://docs.ecotone.tech/modelling/command-handling/external-command-handlers).\
We mark given method with `Command Handler` attribute and Ecotone will register it as Command Handler.

In most common scenarios, Command Handlers are used as boilerplate code, which fetch the aggregate, execute it and then save it.

```php
$product = $this->repository->getById($command->id());
$product->changePrice($command->getPriceAmount());
$this->repository->save($product);
```

This is non-business code that is often duplicated wit each of the Command Handler we introduce.\
Ecotone wants to shift the developer focus on the business part of the system, this is why this is abstracted away in form of Aggregate.

```php
 #[Aggregate]
class Product
{
    #[Identifier]
    private string $productId;
```

By providing `Identifier` attribute on top of property in your Aggregate, we state that this is identifier of this Aggregate (Entity in Symfony/Doctrine world, Model in Laravel world).\
This is then used by Ecotone to fetch your aggregate automatically.

However Aggregates need to be [fetched from repository](https://docs.ecotone.tech/modelling/command-handling/repository) in order to be executed.\
When we will send an Command, Ecotone will use property with same name from the Command instance to fetch the Aggregate.

```php
class ChangePriceCommand
{
    private string $productId; // same property name as Aggregate's Identifier
    private Money $priceAmount;
```

{% hint style="success" %}
You may read more about Identifier Mapping and more advanced scenarios in [related section](https://docs.ecotone.tech/modelling/command-handling/identifier-mapping).
{% endhint %}

When identifier is resolved, Ecotone use `repository` to fetch the aggregate and then call the method and then save it. So basically do all the boilerplate for you.

{% hint style="success" %}
To implement repository reference to [this section](https://docs.ecotone.tech/modelling/command-handling/repository).\
You may use inbuilt repositories, so you don't need to implement your own.\
Ecotone provides [`Event Sourcing Repository`](https://docs.ecotone.tech/modelling/event-sourcing), [`Document Store Repository`](https://docs.ecotone.tech/messaging/document-store#storing-aggregates-in-your-document-store), integration with [Doctrine ORM](https://docs.ecotone.tech/modules/symfony/doctrine-orm) or [Eloquent](https://docs.ecotone.tech/modules/laravel/eloquent).
{% endhint %}

## State-Stored Aggregate

An Aggregate is a regular object, which contains state and methods to alter that state. It can be described as Entity, which carry set of behaviours.\
When creating the Aggregate object, you are creating the *Aggregate Root*.

```php
 #[Aggregate] // 1
class Product
{
    #[Identifier] // 2
    private string $productId;

    private string $name;

    private integer $priceAmount;
    
    private function __construct(string $orderId, string $name, int $priceAmount)
    {
        $this->productId = $orderId;
        $this->name = $name;
        $this->priceAmount = $priceAmount;
    }

    #[CommandHandler]  //3
    public static function register(RegisterProductCommand $command) : self
    {
        return new self(
            $command->getProductId(),
            $command->getName(),
            $command->getPriceAmount()
        );
    }
    
    #[CommandHandler] // 4
    public function changePrice(ChangePriceCommand $command) : void
    {
        $this->priceAmount = $command->getPriceAmount();
    }
}
```

1. `Aggregate` tells Ecotone, that this class should be registered as Aggregate Root.
2. `Identifier` is the external reference point to Aggregate.

   This field tells Ecotone to which Aggregate a given Command is targeted.
3. `CommandHandler` defined on static method acts as *factory method*. Given command it should return *new instance* of specific aggregate, in that case new Product.
4. `CommandHandler` defined on non static class method is place where you would make changes to existing aggregate, fetched from repository.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ecotone.tech/modelling/command-handling/state-stored-aggregate.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
