-
Notifications
You must be signed in to change notification settings - Fork 39
Views
When you process a command, the system might change its state. In an event-sourced system, all state changes are described by appending one or more events to the event stream.
In order to make that state available to be read, we have the concept of projections, which is a general term for whatever can be derived off of the event stream, or views which kind of insinuates that the purpose of this particular projection is to be seen by someone. :)
In a system based on the CQRS architectural principles, you will use projections to generate all the read models. Generally, you'll probably create a read model for each shape your data can take, in extreme cases maybe one specialized view per screen in your application.
With Cirqus, you can create views with an Event dispatcher whose only purpose in life is to do something with the events.
Cirqus has an event dispatcher implementation, the ViewManagerEventDispatcher
, which is capable of dispatching events to one or more views. It will manage its views asynchronously behind the scenes and make sure that each individual view automatically catches up with all events that might not have been processed by that view.
Other kinds of event dispatchers might send events off-site, perhaps into or out of the cloud, or perform various downstream integrations like e.g. send emails, put events on a company's event bus, etc.
The ViewManagerEventDispatcher
dispatches events to one or more views. Those views are managed by View managers. They basically define how the view is persisted, e.g. in Entity Framework or MongoDB.
This is what the whole cycle looks like:
- A new command is created.
- The command is sent to the Command Processor.
- The command processor executes the command.
- The execution causes one or more Events to be emitted.
- The batch of events is stored in the Event store.
- An Event dispatcher retrieves the new batch of events from the event store.
- The dispatcher uses Locators to decide which events should be sent to which view managers.
- The dispatcher sends the events to the view managers.
- The view manager handles the event.
- After handling a batch of events, the view manager persists its state.
As you can see, the first half of the steps above are the Command-part of CQRS while the second half are the Query-part of CQRS.
Not every view needs every event. How should the event dispatcher know to which view managers it should send its events? Cirqus has the concept of Locators to solve that problem. A locator defines which types of event it accepts and extracts IDs from those events. Every view needs a locator. You can read more about locators in Locators.
A view is an implementation of the IViewInstance<>
interface with one or more ISubscribeTo<>
implementations. The first interface is a generic interface which requires a locator. The ISubscribeTo<>
interface defines which events can be handled by a view and how they should be handled.
public class MyView : IViewInstance<InstancePerAggregateRootLocator>,
ISubscribeTo<MyExampleEvent>,
ISubscribeTo<AnotherExampleEvent>
{
public int MyProperty { get; set; }
public string SomeOtherProperty { get; set; }
public void Handle(IViewContext context, MyExampleEvent domainEvent)
{
MyProperty = domainEvent.SomePropertyOnTheEvent;
}
public void Handle(IViewContext context, AnotherExampleEvent domainEvent)
{
SomeOtherProperty = domainEvent.SomePropertyOnTheEvent;
}
public string Id { get; set; }
public long LastGlobalSequenceNumber { get; set; }
}
The properties Id
and LastGlobalSequenceNumber
are part of the IViewInstance
interface.
In the example above, the view will receive the events MyExampleEvent
and AnotherExampleEvent
. It will create a new instance of itself per instance of the aggregate root (see Aggregate roots) that emits these events. So if these two events were to be emitted by MyAggregateRoot
and you'd have 2 different instances of MyAggregateRoot
(so you work with 2 different IDs), then there would be 2 instances of MyView
stored in the persistence by the View managers.
Note that the instance of the view will not be created until it has emitted its first event. Also, there's currently no way to delete instances of a view. We recommend using soft deletes like explained in Aggregate roots.