polygene-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Niclas Hedhman <nic...@hedhman.org>
Subject [Proposal] EventSourcing support
Date Fri, 24 Jul 2015 15:54:52 GMT
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...


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.
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
-- 
Niclas Hedhman, Software Developer
http://zest.apache.org - New Energy for Java

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