Oh, also: This thing about handling envelopes with correlationId etc. separate from body (the actual domain event) leads to some conflicts.
In the Aggregate:
public void OpenAccount(Guid correlationId, Guid commandId, Guid accountId, Guid ownerId)
{
if (_opened) // or some other sort of validation
throw new InvalidOperationException(“Account already opened”);
ApplyEvent(new NewAccountOpened(correlationId, commandId, accountId, ownerId));
}
``
This is the pattern I’ve read is encouraged; Passing the members of the command as parameters.
Handled like this in abstract Aggregate class:
protected virtual void Apply(dynamic e) { }
public void ApplyEvent(object @event)
{
((dynamic)this).Apply(@event);
_uncommittedEvents.Add(@event);
this._version++;
}
``
With envelope, this pattern would not be followed, instead it would be like:
public void OpenAccount(Envelope envelope)
{
if (_opened) // or some other sort of validation
throw new InvalidOperationException(“Account already opened”);
var command = envelope.Body;
if (envelope.Body.GetType() == typeof(OpenNewAccount))
{
var @event = new NewAccountOpened(command.AccountId, command.OwnerId));
var eventEnvelope = Envelope.ForEvent(@event, envelope.CorrelationId, command.Id);
ApplyEvent(eventEnvelope);
}
else
throw new ArgumentException(“Wrong command type”);
}
``
and in the abstract aggregate base class:
public void ApplyEvent(Envelope envelope)
{
((dynamic)this).Apply(envelope.Body);
_uncommittedEvents.Add(envelope);
this._version++;
}
``
Then when saving to repository and calling the aggregate GetUncommittedEvents(), we would extract the body and the metadata separately from the envelope.
Is this how it is done then (more or less)?