Event Store using nosql performance considerations

It might be more of a general NOSQL infrastructure issue.

With the event finder, I save the event documents in ravendb.

There are several types of events in my domain model, basic example is below:

public abstract class Event
{
    public Guid AggregateId { get; set; }

    public Event(Guid aggregateId)
    {
        AggregateId = aggregateId;
    }
}

public class NewPersonEvent : Event
{
    public string Name { get; set; }

    public NewPersonEvent(Guid id, string name) : Base(id)
    {
        Name = name;
    }
}

public class NewOrderEvent : Event
{
    public DateTime OrderDate { get; set; }
    public double OrderCost { get; set;}


    public NewOrderEvent(Guid id, DateTime date, double cost) : Base(id)
    {
        OrderDate = date;
        OrderCost = cost;
    }
}

      

The event is saved as an event document regardless of what type of aggregate the event originates from.

This is the best approach to using the same document type in ravendb for all events. Or is there any advantage of grouping document types for each aggregate.

Instead of having only "Events" documents, you must have "PersonEvent" and "OrderEvent" documents instead.

What are the advantages and disadvantages of each of the two approaches, in particular, are there performance issues?

+3


source to share


1 answer


You are overriding the default tag name for events as ...

docStore.Conventions.FindTypeTagName = type => typeof(Event).IsAssignableFrom(type) ? DocumentConvention.DefaultTypeTagName(typeof(Event)) : DocumentConvention.DefaultTypeTagName(type);

      

Then whenever you execute a request for Event

, it just works and fetch all events. session.Query<Event>()

If so, you can shoot in the foot, because if you just want a subset of the events and you do session.Query<NewPersonEvent>()

, it will fetch all the events, since you overridden the tag convention at the beginning. You can still do it differently, but it won't be so straight forward (for example, with event type enumeration and enum filtering).

I would vote to NOT override RavenDB's default behavior, leave the different event types as their own document collections, and just use a multi-map index.

The Raven docs indicate that static indexes are preferred over dynamic indexes, so this shouldn't be a performance issue. docs :

If you disagree with the tag, but you can create more indexes for a subset of events, create map / decrement indexes to aggregate counters for each type of event, and more.



The index will be the index of the multicard and you have 2 options for you to choose.

Option 1

    public class Events_All : AbstractMultiMapIndexCreationTask
    {
        public Events_All()
        {
            AddMap<NewPersonEvent>(newPersonEvents =>
                from newPersonEvent in newPersonEvents
                select new
                {
                    Id   = newPersonEvent.AggregateId
                });

            AddMap<NewOrderEvent>(newOrderEvents =>
                from newOrderEvent in newOrderEvents
                select new
                {
                    Id   = newOrderEvent.AggregateId
                });
        }

    }

      

Option 2 (reflection)

public class Events_All2 : AbstractMultiMapIndexCreationTask
{
    public Events_All2()
    {
        AddMapForAll<Event>(events =>
            from @event in events
            select new
            {
                Id   = @event.AggregateId
            });
    }
}

      

Here is a sample test using packages RavenDB.Tests.Helpers

and Shouldly

NuGet. It uses the first approximate index, but you can also change it to use the second one.

    public class MultimapIndexTest : RavenTestBase
    {
        private readonly Random _random = new Random();

        [Fact]
        public void GetAll_HasBoth_Success()
        {
            //Arrange
            const string personName = "Jimmy";
            double randomCost = _random.Next();

            var event1 = new NewPersonEvent(Guid.NewGuid(), personName);
            var event2 = new NewOrderEvent(Guid.NewGuid(), DateTime.Now, randomCost);

            using (var docStore = NewDocumentStore())
            {
                docStore.ExecuteIndex(new Events_All());

                using (var sesion = docStore.OpenSession())
                {
                    sesion.Store(event1);
                    sesion.Store(event2);
                    sesion.SaveChanges();
                }

                docStore.WaitForStaleIndexesToComplete();

                //Act
                var events = GetAll(docStore).ToList();

                //Assert
                events.ShouldNotBeEmpty();
                events.Count().ShouldBe(2);

                var newPersonEvent = events.FirstOrDefault(x => x is NewPersonEvent) as NewPersonEvent;
                newPersonEvent.ShouldNotBe(null);
                newPersonEvent.Name.ShouldBe(personName);

                var newOrderEvent = events.FirstOrDefault(x => x is NewOrderEvent) as NewOrderEvent;
                newOrderEvent.ShouldNotBe(null);
                newOrderEvent.OrderCost.ShouldBe(randomCost);
            }
        }

        private IEnumerable<Event> GetAll(DocumentStore docStore)
        {
            using (var session = docStore.OpenSession())
            {
                return session.Query<Event, Events_All>();
            }
        }
    }

      

+1


source







All Articles