IEventListener<TEvent>interface. Event can be any plain object that implements the IEvent interface (which itself is empty in its definition).
DomainEventlistener will also receive events of type
DomainAggregateEventand all types derived from it). Single event type can be handled by any amount of event handlers, which are registered using the dependency container (their order of invocation is not guaranteed in any way).
IEventMessage<T>). Event message is an envelope wrapping the event with additional metadata that may not be primarily important for the domain (i.e. they are not a part of the event’s definition on its own), but still might be needed elsewhere (especially when consuming the events outside of the domain model core). This allows to keep the event type definitions simple and clean of things unrelated to their purpose from the domain perspective.
IEventMetadataProviderin the DI container. These providers are invoked by the
EventMessageFactorywhen the event messages get constructed (i.e. typically before saving to event store or sending to other connected systems).
EventVersionAttributespecifying its new version (which makes it possible to preserve its original name). An example:
IEventUpgrade(Revo auto-discovers these upon startup).
IEnumerable) of all original aggregate's event messages and then returns an upgraded stream of event messages. You can easily implement this transformation using C#
EventMessageUpgradeExtensionsclass making the transformation even easier, e.g.:
IEventListener<TEvent>) via the event bus is the basic way of processing events that is implemented by the framework. As such, it is completely synchronous – meaning that all the listeners are invoked sequentially, one-by-one and without guaranteed order, in a synchronous manner. Because of their nature, synchronous event listeners posess no delivery guarantees and should rarely be used in actual application code (possible valid use cases include notifications that only have transient effects).
IAsyncListener<TEvent>for the specific event (base) type and registered using the dependency container. Events asynchronously delivered via this interface are a bit different in their nature when compared to the events delivered using the regular (synchronous) event pipeline.
IEventSequencer<TEvent>). When the async processing of an event begins, the event dispatcher calls all of these registered sequencers. An event sequencer primarily defines two properties – which event sequence(s) it wants to use for the event (which in turn implies the event queue it will be pushed to) and what sequence number(s) will it assign to that event. Based on that information, the event dispatcher subsequently creates corresponding event queues (if it does not exist already) and pushes the event to them. Later when an event backlog worker gets to process any of the queues, it iterates through all the events queued in it, ordered by their sequence number, and passes them to corresponding async event listener(s). When the backlog worker is done, it removes the events that were successfully processed from that queue. If processing of any of the events fails, it stays in the queue until the problem gets resolved and event successfully processed.
IAsyncEventSequencersignals that it wants to attempt synchronous dispatch by implementing
ShouldAttemptSynchronousDispatch, the events added to its queue that would normally get scheduled for later processing in the background get directly passed to the event processor instead - that is, in a blocking (synchronous) manner, possibly on the same thread. If any of the async event listeners fail, they still retain the same properties of other asynchronous events – i.e. their processing gets retried later either upon the restart of the system or at the next processing of the same queue.
EntityEventToPocoProjectors<,>along with projection row versioning (see later chapter on read models and projections).