What is the difference between a "root aggregate" and "aggregate" in an event store?
I'm having trouble accurately defining these even after hours of searching. My understanding is that an aggregate is a table of IDs that group together events (collection) within the store often with object representations which is why there can also be an aggregate type.
Additionally, I've seen aggregate tables with version numbers which I find confusing under the premise that they are essentially group/container/aggregate IDs representing a collection of events.
So we have agreed that you are trying to make event streams to DDD Aggregate Root and Aggregate patterns.
There is a common pattern that each aggregate has its own stream. It has the name and identity of the aggregate root, although the name part is not a requirement.
All operations on the whole aggregate are represented by events, which are then written to this stream.
So if you have an aggregate root MyNamespace.Order
, which has multiple child value objects or entities MyNamespace.OrderLine
, all operation on lines are done by accessing aggregate root methods and all events are written to one stream, so for the Order with id 123 it would be:
Stream: MyName
The concept of entities is not represented on the event store side since you cannot easily define relations between different streams to read events from multiple streams in order to recompose the aggregate. Therefore, all events are written and read in the aggregate root stream, which then becomes the stream for the whole aggregate.
One important rule about aggregates is that aggregate is also the consistency boundary. This basically means that all operations on an aggregate must be done in one transaction.
It is important to keep the event version to handle concurrency. When you read an aggregate from the event store, you get the latest event version too. After performing the operation, you write new events back, checking if the latest event version is still the same. If the last event version does not match, you throw the concurrency exception since someone has already changed the aggregate and wrote changes to the store before you did and you got a conflict.
Concerning "tables" I am not quite sure what you mean. Certainly, you can model your event store in tables and there are different strategies on doing this. More often then not you would really prefer using specialised event store, like this one.
The DDD book and terms don't specifically address event sourcing (it was not yet A Thing).
SE Radio podcast 226 with Eric Evans covers this a bit.
An Aggregate is a set of related Entities and Value Objects that, in aggregate, model a single thing (or related set of things) which need to remain internally consistent
The Aggregate Root can be the root entity that one views the aggregate 'through' (e.g. the Invoice entity where you have it and a separate set of order lines)
It's unfortunate (but Naming is Hard) that people feel the need to overload the term Aggregate to also refer to a thing that performs an aggregation of events in a stream (even though the set of related events do together indeed represent the state of the Aggregate).
In the Implementing DDD book, the Functional Event Sourcing Appendix separates this out well IIRC - I tend to refer to this thing (the aggregation of the related events of an Aggregate for the purposes of making) as 'the folded state' (and also separate out the evolution of this fold state from the actual decision making)
Projections (in DDD-CQRS-ES parlance) refer to an abstract set of things hanging off the events which do appropriate things in response to new events (representing decisions people have made). A projection can maintain a Denormalized View ala your 'summary' (there is not only one). A stream has events. A decision-making process will fold those events to infer relevant context for the purposes of making a decision. A projection can have state (built from the well-ordered observation of a relevant subset of the events pertaining to a stream (or set thereof)) - it may be a blob that one keeps in a key-value store ready to be queried easily. It may be an in-memory total per stream that you build by reading from the start each time. It may a similar in-memory cache which is backed by some form of checkpoint (last event number one saw), together with a serialized form of what one summarised it as up to that point. It can be many of those. Or in degenerate cases, you may not have anything hooked up yet.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With