polygene-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Kent SĂžlvsten <kent.soelvs...@gmail.com>
Subject Re: [Proposal] EventSourcing support
Date Sat, 25 Jul 2015 21:00:08 GMT
A few comments to an interesting post.


So, let's say that we introduce a Aggregate meta type, which is like an
Entity that can retrieve from an EventStore. It also has Identity as
entities does.

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.

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.

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.

// 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. 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.
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.

Still a few loose ends - but a good start.

/Kent




Den 24-07-2015 kl. 17:54 skrev Niclas Hedhman:
> Gang,
>
> Today I got caught up in EventSourcing a la Greg Young, and I had a look at
> "Event Store", https://geteventstore.com/
>
> Then I got a crazy idea, and somehow I like it a lot, and would like to
> discuss it further.
>
>
> Greg talks about the Event Store places "past tense actions/verbs" into
> streams by "aggregate ID" (DDD lingo). That is the only state to be held.
> He says that for query, one send those events to the "Read Model", run a
> "Projection" on them and the result of the projection is stored in first
> normalized form optimized for the queries, as many as needed and
> effectively tailored to the actual view of the queries data.
> But for the "Write Model", he seldom discuss how does the Write Model
> convert the event sequence to a current state, or an aspect of current
> state. As far as I understand it, the same projection algorithm mechanism
> is needed, but possibly a different set.
>
>
> 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...
>
>
> One can then extend with Projection Types which are simply interfaces with
> a single arbitrary method without in-arguments. The ProjectMixin is part of
> Zest, and it will be provided with the @Projections list (just like @Mixins)
> and matches method to one of the classes in the projection list.
>
> The @Projections list is Java8 Collector classes, and the ProjectMixin
> simply takes the EventState and feed those events into the provided
> collector.
>
> Additionally, the Collector implementation could be either a Composite or
> an Object and hence full injection support, and ability to use third-party
> Collector implementations..
>
> Do you still follow?? Let's look at some code stubs;
>
> // Zest code
> @Mixins( ProjectionMixin.class )
> public interface Aggregate<E> extends Identity
> {}
>
> public @interface Projections
> {
>     Class[] value();
> }
>
> @AppliesTo( ProjectionFilter.class )
> public class ProjectionMixin
>     implements InvocationHandler
> {
>     @State
>     private Stream<? extends Event> state;
>
>     private Collector collector;
>
>     public ProjectionMixin( @Structure Qi4j api,
>                             @This Aggregate me,
>                             @Invocation Method method  )
>     {
>         ModelDescriptor desc = spi.modelDescriptorFor( me );
>         // we need to look at the <E> of the Aggregate, the return
>         // type of the method and match those against the
>         // T and R of the Collector's generic types.
>         collector = findCollector( desc, method.getReturnType() );
>     }
>
>     public Object invoke( ... )
>     {
>         return state.collect( collector );
>     }
> }
>
>
> // User code
> public interface Order extends Aggregate<OrderEvent>
> {}
>
> @Projections( CurrentItemListProjection.class )
> public interface Items
> {
>     List<OrderItem> items();
> }
>
> public class CurrentItemListProjection extends Collector<OrderEvent,
> List<OrderItem>, List<OrderItem>>
> {
>     // impl details.
> }
>
> Possibly the simpler "reduce()" could also be supported, for instance the
> BinaryOperator variant, but for that it must be instantiated fresh on each
> invocation (not the one-time seen above)
>
> As you see above, the underlying support that is needed is the Stream<?> as
> a new type of state, just like the current StateHolder is backing the
> PropertyMixin. And that there is a EventStore concept backing that.
>
> I have left out the "write/update" case on purpose, just to get the
> discussion going. The write seems to be open to a couple of different
> strategies and would like to handle that separately.
>
>
> I am sure there are a lot of other interesting bits to be discovered here.
>
>
> Cheers


Mime
View raw message