Your First Events

We now need to design the aggregate events that will form as the basis of your domain. Some of events that occur in this system could be:

  • Bank account has been opened.
  • Money has been sent to a bank account.
  • Bank fees for the sending of money have been deducted from bank account.
  • Money has been received by a bank account.

Lets model these events accordingly.

The event that represents a bank account being opened

//Walkthrough.Domain/Model/Account/Events/AccountOpenedEvent.cs
public class AccountOpenedEvent : AggregateEvent<Account,AccountId>
{
public Money OpeningBalance { get; }
public AccountOpenedEvent(Money openingBalance)
{
OpeningBalance = openingBalance;
}
}

The event that represents a bank account having sent money

//Walkthrough.Domain/Model/Account/Events/MoneySentEvent.cs
public class MoneySentEvent : AggregateEvent<Account,AccountId>
{
public Transaction Transaction { get; }
public MoneySentEvent(Transaction transaction)
{
Transaction = transaction;
}
}

The event that represents a bank account deducting bank fees

//Walkthrough.Domain/Model/Account/Events/FeesDeductedEvent.cs
public class FeesDeductedEvent : AggregateEvent<Account,AccountId>
{
public Money Amount { get; }
public FeesDeductedEvent(Money amount)
{
Amount = amount;
}
}

The event that represents a bank account receiving money

//Walkthrough.Domain/Model/Account/Events/MoneyReceivedEvent.cs
public class MoneyReceivedEvent : AggregateEvent<Account,AccountId>
{
public Transaction Transaction { get; }
public MoneyReceivedEvent(Transaction transaction)
{
Transaction = transaction;
}
}

We need to add each aggregate event applier method to the aggregate state as an IApply<>.

AccountState becomes:

//Walkthrough.Domain/Model/Account/AccountState.cs
public class AccountState : AggregateState<Account, AccountId>,
IApply<AccountOpenedEvent>,
IApply<MoneySentEvent>,
IApply<FeesDeductedEvent>,
IApply<MoneyReceivedEvent>
{
public Money Balance { get; }
public void Apply(AccountOpenedEvent aggregateEvent)
{
Balance = aggregateEvent.OpeningBalance;
}
public void Apply(MoneySentEvent aggregateEvent)
{
Balance -= aggregateEvent.Transaction.Amount;
}
public void Apply(FeesDeductedEvent aggregateEvent)
{
Balance -= aggregateEvent.Amount;
}
public void Apply(MoneyReceivedEvent aggregateEvent)
{
Balance += aggregateEvent.Transaction.Amount;
}
}

Notice how events are treated as facts. The only domain logic here is how to apply the event to the aggregate state. If you have if-else statements in your state model, reconsider your modelling of events and state.

Head over to the next section on specifications.

Next →