Skip to content
This repository has been archived by the owner on May 29, 2024. It is now read-only.

Locators

Brett edited this page Jun 22, 2019 · 4 revisions

When using ViewManagerEventDispatcher as Event dispatcher, all views get all events, once, and in the correct order.

This means that views managed by each IViewManager does not need to care about anything other than updating view instances from the events.

The first thing to think about when you model your views, is how to partition the event stream - since the view manager gets all events from the global event stream, and it might be interested in having multiple view instances, it must partition the event stream somehow.

When you model a view, you implement the generic IViewInstance<> interface, closing it with the type of view locator you want to use.

View locator

The view locator is the thing that partitions the event stream, i.e. splits it into a number of logically separate streams. One natural way to do that is by aggregate root ID, which can be done with the built-in InstancePerAggregateRootLocator like this:

public class MyView : IViewInstance<InstancePerAggregateRootLocator> { ... }

which will cause and instance of MyView to be created/updated for each aggregate root ID encountered as the events are streaming.

Another (pretty boring) built-in view locator is the GlobalInstanceLocator, which does not partition the event stream - it just passes the event stream to one single view instance, which then gets to handle all events.

View locator responsibility

The way the event stream is partitioned, is by having the view locator return a "partition key" for each event. In fact, it's possible to return multiple keys as the view locator abstract method has the following signature:

protected abstract IEnumerable<string> GetViewIds(IViewContext context, DomainEvent e);

So, as you can see, the view locator can return any number of IDs for each domain event. Each Id is used to create/update their respective IViewInstances. As an example on how this can be done, check out the implementation from InstancePerAggregateRootLocator:

protected override IEnumerable<string> GetViewIds(IViewContext context, DomainEvent e)
{
    return new[] {e.Meta[DomainEvent.MetadataKeys.AggregateRootId]};
}

which causes the event stream to be partitioned by aggregate root ID.

Custom view locator

If the built-in locators wouldn't suffice, it's quite easy to create your own view locator. To create one, create a subclass of HandlerViewLocator and implement one or more IGetViewIdsFor<> interfaces.

IGetViewIdsFor<> is a generic interface that defines that the view locator should receive a certain type of events. E.g. if you implement IGetViewIdsFor<MyDomainEvent>, the locator would receive all the events of type MyDomainEvent. For each implementation you have to define the method IEnumerable<string> GetViewIds(IViewContext context, MyDomainEvent e) that extracts the partition/viewinstance ID from the event. The IViewContext object has a few methods available that might help you with this. It contains methods to load an aggregate root and a dictionary of items that are registered in the context by the view manager.

Example

private class RoomLocator : HandlerViewLocator,
        IGetViewIdsFor<RoomAddedEvent>,
        IGetViewIdsFor<RoomRemovedEvent>
    {
        public IEnumerable<string> GetViewIds(IViewContext context, RoomAddedEvent e)
        {
            yield return e.RoomId;
        }

        public IEnumerable<string> GetViewIds(IViewContext context, RoomRemovedEvent e)
        {
            yield return e.RoomId;
        }
    }

The view that works with this locator will then look like this:

public class RoomView : IViewInstance<RoomLocator>,
        ISubscribeTo<RoomAddedEvent>,
        ISubscribeTo<RoomRemovedEvent>
{
    public void Handle(IViewContext context, RoomAddedEvent domainEvent)
    {
        // do something with the event and/or the context
    }

    public void Handle(IViewContext context, RoomRemovedEvent domainEvent)
    {
        // do something with the event and/or the context
    }

    public string Id { get; set; }
    public long LastGlobalSequenceNumber { get; set; }	
}
Clone this wiki locally