polygene-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Niclas Hedhman <nic...@hedhman.org>
Subject Re: [Proposal] EventSourcing support
Date Sun, 26 Jul 2015 06:17:24 GMT
On Sun, Jul 26, 2015 at 5:00 AM, Kent SĂžlvsten <kent.soelvsten@gmail.com>

I explicitly asked to keep the "Write" case out of the discussion, but
obviously that was not possible from the first line ;-)

> In DDD lingo an Aggregate is a web of connected interdependent entities
> with a common aggregate root - only the aggregate root is queryable and
> all updates should go through the aggregate root, which is responsible
> for preserving consistency.  The only associations to an aggregate form
> the outside must go to the aggregate root (allowing it to ensure
> consistency). In EventSourcing a Domain Event should only touch a single
> aggregate root - so if we modify multiple aggregates in a UOW that means
> multiple (Domain) Events.

Yes. And?

> I think using the aggregate name is correct - but we might simply let it
> be a special Entity - and might in the future consider restricting
> associations and querying/indexing according to the above.

GutFeeling(tm) says that it is not an entity.In Event Sourcing, the
Aggregate is the transactional, concurrency, consistency and distribution
boundary. Making the UnitOfWork responsible to handle these concerns
on-top-of, seems wrong to me. This is why I wanted to leave the "wrte" case
out for a little while.

>> Keep the above in mind, because I haven't figured out whether what I am
>> writing below applies to Read Model, Write Model or both...

> I think mostly the Read Model. For the Write model i imagine something
> more like an EntityComposite with no indexer/querybuilder - in principle
> loaded from an eventstore - constraints being very important.
> We might be able to use existing entitystores for saving snapshots if
> necessary.

I haven't analyzed what Snapshotting would encompass, but it wouldn't
surprise me if it would be straight forward to leverage the EntityComposite
mechanism within the Aggregate Root, for snapshot reasons. However, it
seems that Greg Young's "Event Store" does snapshotting in the DB itself,
but I am not totally sure (I couldn't get it (3.0.5) to run for more than a
few minutes before starting to crash on me), so I think such support is
probably inside the actual Zest EcentStore extension implementation.

>> // User code
>> public interface Order extends Aggregate<OrderEvent> {}
>> public class CurrentItemListProjection extends Collector<OrderEvent,
>> List<OrderItem>, List<OrderItem>>
>> {
>>     // impl details.
>> }
> In general the may be multiple types of events affecting the same
> aggregate (OrderCreatedEvent, ItemAddedEvent, OrderPayedEvent,
> OrderShippedEvent, OrderCancelledEvent).
> Each should be handlede specifically - so I think a shared
> BaseOrderEvent is not of much value.

Good Point! I somehow managed to forget that DomainEvents should be
strongly typed, and as you mention, it makes a lot more sense that Zest is
capable to direct the event straight to its respective handler, and not
happen in user code. That said, the Collector is Java8 Stream API, and it
defines the three Generics, and I think it makes sense to leverage that
bit. Some re-think needed to figure out how to make this as smooth as

> This might be solved with multiple
> Projections (each projection updates a single read model based on a
> specific type of event).  But then
> Aggregate<OrderEvent>
> would not make much sense. So maybe another solution for that small part
> may have to be sorted out.

Yes, Agree. After all, I haven't spent weeks of analysis and
experimentation, just read some details around Greg's "Event Store"
(horrible name) and a few things clicked in my brain.

> Another complication is that in general a read model may span multiple
> aggregates. I think a read model for a single aggregate is the most
> common case - so specific handling that case is in order.

Ah! Good call. Of course, all of the above is to support "read" in the
"Write Model", which is what I have always wondered how it should be done.
Greg told me long time ago that, "just put in a SQL table with
AggregateRootID and SeralizedDomainEvent columns" and "have the state
resolver inside your object", which felt awkward to me.

> Still a few loose ends - but a good start.

You made the most important contribution in that "One Aggregate Root
receives/contains 0..m DomainEvents", so the Aggregate<E> is not the right
abstraction. Perhaps the write/update case must be considered before
finding the best abstraction.

I would like to keep CQRS out of the picture, even though Greg says "You
can do CQRS without ES, but EventSourcing requires CQRS.", mainly because I
think CQRS is somewhat incompatible with HATEOAS, which to me seems much
more relevant for web apps.

Now, WRITES.... Event Sourcing is effectively saying that the Aggregate
Root produces a DomainEvent, appends it to its State and publishes it
through some 'global' mechanism. Other destinations should be able to pick
that up. One could then claim that the EventStore extension could just as
well listen on the same listener mechanism as everything else. And the
'transactional' aspect becomes a concern for the event distribution
mechanism. Further, in a distributed environment, there is a need to
support consumer groups (as Kafka calls it).

So then, should the Aggregate Root actually append a Domain Event to itself
and then publish it, or should it actually only publish it and depend on
that the state update is coming back to it "somehow"?? IF SO, then another
weird question rise up; What if someone else publishes a DomainEvent that
the Aggregate Root is supposed to manage? Is that OK? If not, how can that
be enforced? Private Domain Events?

module.aggregates( OrderAggregate.class )
      .events( OrderCreated.class, OrderLineAddedEvent.class, ... )
      .scope( EventScope.aggregate );

It doesn't "click" yet for me...

Let's ponder about "replay of past events"...

EventSourcing prides itself that one can change the algorithm, replay the
events and hence retro-actively fix bugs in the past. BUT, what happens if
the new algorithm emits new events, but in past time? Should those be
injected into history? Or vice versa, what if the bug was that too many
events were emitted, should those now be removed? Or is it that ONLY the
State Resolver is allowed to be modified, and that the replay is
effectively "never" happening as a global thing, but just internally in the
Aggregate Root whenever its state is loaded??

I bet that this has been discussed endlessly on various forums, and I
should probably go digging.


  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message