# Aggregates

The aggregate ensures that all the changes to the objects within the aggregate are performed in a consistent and transactional manner.<br>

Key characteristics of an Aggregate are:

1. **Consistency Boundary**: The aggregate defines a clear boundary for transactional consistency. All invariants and business rules within an aggregate must be satisfied as a whole, ensuring that the domain remains in a valid state at all times.
2. **Encapsulation**: The internal structure and state of the aggregate's objects are hidden from the outside world. External entities can only interact with the aggregate through its root.
3. **Lifecycle**: Aggregates are typically long-lived, persisting over multiple transactions. They should be designed with consideration for their lifecycle management.
4. **Isolation**: Aggregates should not directly reference or hold references to other aggregates. Instead, they can reference other aggregates by their unique identifiers (IDs).

{% hint style="info" %}
**Eiffel.Modelling.Abstractions** NuGet package contains base aggregate class. By using abstract Aggregate class you can define your aggregates.
{% endhint %}

```csharp
/// <summary>
/// Aggregate implementation
/// </summary>
/// <typeparam name="TIdentity">The type of unique business identifier</typeparam>
/// <typeparam name="TKey">The type of database primary key</typeparam>
public abstract class Aggregate<TIdentity, TKey> : EntityBase<TKey>, IAggregate
   where TIdentity : ValueObject<TIdentity>
   where TKey : struct, IEquatable<TKey>
{
    /// <summary>
    /// Domain events collection
    /// </summary>
    public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents.ToList();
    
    /// <summary>
    /// Business identifier
    /// </summary>
    public abstract TIdentity Identity { get; internal set; }
    
    private readonly List<IDomainEvent> _domainEvents;
    /// <summary>
    /// CTOR
    /// </summary>
    public Aggregate()
    {
        _domainEvents = new List<IDomainEvent>();
    }
    
    /// <summary>
    /// Raises/adds domain event
    /// </summary>
    /// <typeparam name="TEvent">The type of the class representing domain event</typeparam>
    /// <param name="domainEvent">Domain event</param>
    protected void Raise<TEvent>(TEvent domainEvent)
        where TEvent : IDomainEvent
    {
        _domainEvents.Add(domainEvent);
    }
    
    /// <summary>
    /// Clears all domain events
    /// </summary>
    public void ClearEvents()
    {
        _domainEvents.Clear();
    }
}
```

Each Aggregate is likely to contain more than one function and these functions are business needs demanded by domain experts/stakeholders. Each function usually requires notifying other members of the system that an event has occurred. These events are called [Domain Events](https://docs.eiffel.dev/principles/domain-driven-design/domain-events). Each Aggregate is responsible for keeping the events occurring within itself.\
\
Each Aggregate must have a uniquely defined identity type and primary key type required for the database. You can find the identity details specific to the aggregates in the [Business Identifiers](https://docs.eiffel.dev/principles/domain-driven-design/business-identifiers) section.\
\
\&#xNAN;*Example Booking Aggregate with BookingCancelled domain event.*

```csharp
public class Booking : Aggregate<BookingId, Guid>
{
    // Business identifier
    public override BookingId Identifier { get; private set; }

    // Status enum
    public BookingStatus Status { get; private set; }

    // ...
    // ...
    void Cancel(string reason)
    {
        Status = BookingStatus.Cancelled;
        Raise(new BookingCancelled(BookingId, reason));
    }
}
```
