Skip to content

Events for an aggregate are read without any specific order #409

@totemcaf

Description

@totemcaf

Describe the bug
When events are applied to an aggregate to materialize it, the events should be applied in version order to maintain the event history.

The events.AggregateStore use an eh.EventStore to read events and then applies them in the same order that were received from the event store.

The query to read the documents from the event collection provides a filter and no sort option, so the documents are returned in the "natural order" witch as explain by documentation: "This ordering is an internal implementation feature, and you should not rely on any particular ordering of the documents." (according to https://www.mongodb.com/docs/manual/reference/method/cursor.sort/#return-in-natural-order)

func (s *EventStore) Load(ctx context.Context, id uuid.UUID) ([]eh.Event, error) {
    cursor, err := s.events.Find(ctx, bson.M{"aggregate_id": id})
    ...
}

and

func (s *EventStore) LoadFrom(ctx context.Context, id uuid.UUID, version int) ([]eh.Event, error) {
    cursor, err := s.events.Find(ctx, bson.M{"aggregate_id": id, "version": bson.M{"$gte": version}})
    ...
}

To Reproduce

I couldn't reproduce it yet. I think that the current implementation of MongoDB store adds new documents in insertion order, and to insert in a different order you should delete some old documents.

Steps to reproduce the behavior (perhaps):

  1. Insert a number of events for several aggregates
  2. Remove the events of one the aggregate
  3. Insert new events for existent aggregates
  4. Read events for existent aggregates (one aggregate at a time)

It is possible that the events read for some the aggregates were not in the desired order (the version order).

Expected behavior
The events applied to an aggregate by the events.AggregateStore should be in order of the version number.

Possible solutions

There are two possible solutions to this problem, depending on which component should be responsible of the order:

  1. The eh.EventStore should guarantee the order of events
  2. The events.AggregateStore should guarantee the order of applications of events

In the first case, we can simple add a Sort option to the query to ensure the documents are read in the required order.
This uses computing power of the database and should be implemented in all the event store implementations.

In the second case, the events.AggregateStore should order the events before applying them. Because the version number of events is an integer between a and b (a the min version read, and b the max), and b - a should be the exact number of events, we can quickly move the events into an array using the version - a as index.
This uses computing power of the application, but the algorithm is smart enough.

I want to hear some comments and I can provide a PR to solve this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions