Why is DomainEvent not an interface?

Why is DomainEvent not an interface and instead a concrete class?
This makes overriding certain functionality (e.g. the automatic
generation of id via EventBase) significantly difficult. I've got an
app that I'm having to jump through lots of constructor hoops in order
to achieve what I need and use the axon framework.

Hi,

to give you a straight answer: it is a concrete class because it evolved to be such way. And I didn’t want to break the API too much in the minor version upgrades.
I am thinking about making it an interface in 2.0, but the problem is that I expect very specific behavior on it. Ideas are welcome, though.

There are, however, some changes in the 1.2 version that should help you out. There is an SPI option to include your own identifier generation strategy. See http://www.axonframework.org/docs/1.2/performance-tuning.html#d4e1688.

Cheers,

Allard

Hi,

One way to do it is to keep the concrete event type, the
way it currently is, to fulfill the needs of Axon framework
itself, but modify it to be a generic type and let it carry a
POJO payload that is, in effect, the real event from the
perspective of the Axon users. I mean something like:
AxonEvent<PojoPayload> In other words, no DomainEvnet
interface at all.

This way, at the borders of Axon, the POJO object can be
detached/attached from/to the AxonEvent (wrapper) and reused
without exposing any artifacts of Axon (including any interfaces)
to the systems outside of the borders of Axon.

This is the way the Java CDI and Spring Integration
(among others) has adopted.

Cheers
Esfand

Hi all,

these remarks have given me a very refreshing perspective on the role of the Event in the Axon framework. So before I start elaborating on that and run the risk of confusing you: thanks for that! I’m thinking about ways to use this to improve the API in the 2.0 version.

I’ll elaborate a bit on my thoughts. Please reply with any comments or questions that you may have. In the end, I want to make sure I’m providing solutions/features that you will find useful.

— warning, thought dump ahead :wink: ----

Separating the “payload” out from the event can be a very powerful thing. In that case, the (POJO) payload is the actual event for what the application is concerned. Obviously, the framework needs a bit more. That can be included in the DomainEvent, which -as Esfand states- is a container for the payload and the headers. The “envelope”, as you will.

In an aggregate, you should still be able to “apply(new MyPayloadStyleEvent())”. Since in most cases the aggregate identifier is also an important part of the payload, you’ll have to manually add it to you payload. Axon cannot (and should not) interfere with that.

This approach solves a problem (read: challenge) I have been facing for quite a while: refactorability of you aggregate boundaries when using event sourcing. Currently, it is not trivial to refactor the aggregate boundaries when using event sourcing. It’s possible, but is takes a little custom tool to make it work. By separating the payload from the “axon event”, it should become easier.

This is the structure of a DomainEvent I have in mind:

DomainEvent (concrete Axon class)

  • Identifier (unique identifier to be able to detect similar versus same events)
  • Aggregate Identifier (the ID of the aggregate generating the event)
  • Sequence number (the version of the aggregate)
  • Headers (container for key value pairs holding non-functional information)
  • Payload (your pojo)

An example of such an event in (XStream-style) serialized form could be:

TimeStamp2010...... User IP192.168.0.1 AuthenticationMyUserName abc123 tr321 chair 120 etc....

Note that the orderId is mentioned twice: once as aggregateIdentifier, once as “orderId” in the payload. They both serve a different purpose. Axon uses the aggregateidentifier to decide which events to load when an aggregate needs to be initialized. The orderId has a functional meaning to your application.

The event store will store the payload separately from the rest of the information. The payload is stored in serialized form, as well as the headers. The id, aggregateIdentifier, aggregate type, sequence numbers, etc. are stored separately, and used by the event store to perform selection and ordering logic. The payload revision field can be used to do more strict selection of which upcasters to invoke.

If, in the order example, you were to decide that these events should no longer be applied on an Order, but on a Transaction aggregate instead, you can easily “relocate” the events to another aggregate, just by providing the “transactionId” as the new aggregate identifier. The sequence numbers will be changed accordingly. But the payload doesn’t need to change.

@EventHandler methods in your application could be registered to your payload type. Axon will then automatically extract it and provide the payload from the event. If you need information from a header, you can provide an extra parameter and annotate it with @Header(name = "User IP), in which case the value of that header key will be passed as parameter. Alternatively, you can add a parameter of type “Headers” or maybe even the “DomainEvent”.

If you’re still following me, cool! I’m very eager to know what you think of this API change.

Cheers,

Allard

Hi Allard,

Your design is really brilliant! It doesn't leave anything,
I can think of, to be desired.

Providing an id to the framework is no price to pay to
get into such a win-win situation. Can that be eliminated by having
an annotation such as @AggregateIdentifier on the right
field of the payload? (With the possibility of alternate
definition externally, e.g. in an XML file?)

I assume switching of the aggregate-id will be via
an offline utility which accepts the user intent as
input and reshuffles the event-stores' events starting
from the beginning of the time.

A few wish items:

1) Please allow the Axon application plug its own
   serialization/deserialization if it has any.
   Considering better knowledge of the application
   about its data it may be able to provide a more
   efficient operator. Also it may save the application
   a couple of deserialization/serialization cyles at
   the borders of Axon. (Assuming the following wish is
   also comes through)

2) Event at the expense of involving the application,
   please reduce the number of times an event has to
   serialized/deserialized during its life cycle within
   the Axon world. Also at the end, give an option
   to the application to get the raw serialized form of
   the payload's data (instead of a java object). So
   the application can pass that verbatim to the next
   neighboring messaging system, if there is any.

3) If the Axon framework, for its own benefit, needs to
   keep some data-fields along with the applications
   payload, if possible, please make it friendly (i.e. as
   much as compatible/similar as possible) to the
   native AMQP and JMS requirements. I'm just
   talking abstractly here. For example let's say AMQP
   broker expects the user to provide a unique UUID
   for each message. If Axon also happened to have a
   needs to define and keep an identifier for each event,
   and if making the id being a UUID doesn't hurt the
   performance, then please select the UUID as the type
   of the internal identifier.

Thanks for providing a fantastic messaging framework and
for constantly enhancing it. I can't wait to see the
Axon 2 with your new event-format design.

Regards,
Esfand

Nice idea.

I currently use an almost 1:1 conversion from "Event" (Axon) to
"Message" (pure POJO) as I don't like to transport Axon specific stuff
to the query server. This way the query side has no dependencies to
Axon at all. Usage of such "payload" style events would eleminate the
need for "Event 2 Message" converters.

Hi Esfand,

it’s actually your design. I just found a way to make it solve some issues in Axon. :wink:

About your remarks, here is my reply:

  1. Serialization is already pluggable. Although Axon defaults to the XStream(Event)Serializer, you can easily provide your own serializer implementation.

  2. A while ago, I was playing with this idea. I’ve built some custom code for a project where I kept a WeakHashMap from Event to Serialized form. As long as the Event is serialized, the serialized form is available. When serializing an event (e.g. when sending it over AMQP), you can use the existing serialized form. That should save a few cpu cycles.
    The idea behind letting the Serializer do this, is that you might want to use different serialization forms for different communication methods.
    The concept of the DomainEvent as an “envelope” also allows the serialization to be postponed until the moment it is really requested. The same, of course, counts for the header information.

  3. The “AMQP” design was my reason to make the “meta-data” more explicit in the Event. However, it never sprung to mind to also make the “payload” more explicit. I think this new design is definitely a step foreward.

Thanks for your heads up.
Cheers,

Allard

Hi all,

I like very much the payload concept on the event side.
I found very useful the header part of the envelope, and the possibility to inject headers as arguments on the event handler.
I would suggest to include an interface for a generic header provider being able to enrich event headers with custom values as actually happens with audit interceptor on the command bus.
Best regards and many thanks for this precious work.
Domenico

Payload issue aside (see below :), for me, my biggest issue isn't that
there is a concrete Event class, it's that the other interfaces in
Axon all use a concrete implementation on their public API
(DomainEvent). This makes it very difficult to do anything outside
what is perscribed in the concrete implementation and still use all
the abstract implementations of repository, event store, etc. The
same is true of the non generic DomainEventStream.

For my needs, all I need is an interface that is used by the public
api's for the event sourced aggregate, repository, and the collection
they use (currently DomainEventStream), and that would fufill my
needs. IMNSHO any framework should be using interfaces on the public
API to allow users of the framework to write their own implementation
when the framework does not meet their needs (80/20 rule).

Now in regards to the whole idea of the payload, I've seen event
implementations that do this (last time I looked at Jonathan Oliver's
Event Store, it was doing something similar). I guess the question
is, are you going to have the event be a pojo, or have it hold
serialized data? Different applications might have different needs,
so you might want to have a common interface for both, then provide
abstract implementations and let the application devs decide which
path fits their needs the best. At the end of the day, events must be
applied, and stored, so as long as those needs can be met easily (and
in my case performantly :slight_smile:

I can only say I totally agree.

In the renewed API, I will make DomainEvent an interface. I’ve been using an SPI structure for identifier generation (using Java’s ServiceLoader) and will probably do the same for DomainEvent implementations. EventFactory.getInstance().newDomainEvent(…) style. This allows applications to provide their own DomainEvent implementations which might better suit special needs.

The default DomainEvent implementation will contain lazy deserialization logic. I noticed that many events loaded from the Event Store to initialize an aggregate aren’t really used. By doing lazy deserialization, we spend less time on things we don’t need. If you want to serialize the Event using the same serializer that deserialized it, it will skip deserialization and return the “cached” serialized form instead.

How does that sound?

If you have any more thoughts about this, let me know. This is very valuable info for me.

Cheers,

Allard

"I noticed that many events loaded from the Event Store to initialize
an aggregate aren't really used. "

Can you provide an example of what you mean here?

Thanks,

Chad

Imagine an aggregate -where have we seen this one before- Order. There is a big chance you’d want to be able to set or modify an address on that order, using appropriate Commands. There will also be an AddressChangedEvent for that Order. But if the address is not part of any decision in a command handler method, it will not be included as state. If it’s not included as state, you don’t need an EventHandler in your aggregate for the AddressChangedEvent. But when loading the aggregate from the Event Store, the event is (currently) loaded and deserialized.

In 2.0, I want to keep loaded events in its serialized form, until the “getPayload” method on it is called. If there is not handler, that method won’t be called, and the event is not deserialized.

Hope that clarifies it a bit.

Cheers,

Allard

Hi,

In 2.0, I want to keep loaded events in its serialized form, until the
"getPayload" method on it is called.

To make it more flexible, there can be another method,
let's say "getPayloadRaw" which would return the payload
in its serialized form.

/Esfand

I’ve actually just spent my morning thinking about how to design this stuff. This is what I came up with:

I don’t want the Event (interfaces) themselves to be serialization-aware. I think it’s not the Event’s concern. So the Event interface (as well as DomainEvent, etc) will have methods to get access to the payload, headers, etc. The implementations, however, may very well be aware that they come from a serialized form. The EventStore, for example, might return a SerializedDomainEvent instance, which maintains a reference to its serialized form and the serializer to deserialize it upon request.

There are some optimizations possible, here. For example, the instance returned by the Event Store might be sent over AMQP to another machine. We don’t want to deserialize it, and then serialize it back to the same shape again. Therefore, an Event could (also) implement a SerializationAware interface. This interface provides methods to investigate whether the Event contains its own serialized data. A serializer could verify that serialized form and use that if appropriate. This saves us one deserialize->serialize cycle.

In the mean time, Serializers could maintain a WeakHashMap of Events and their serialized form to prevent duplicate serialization on the same event. This could be useful when the same serializer is used to store Events in the Event Store and send them over (e.g.) AMQP.

All this will still provide the freedom to use different serializers for long-term storage and inter-machine communication, while preventing duplicate serialization steps when a single serializer is used for both.

Let me know what you think. All comment is welcome!
Cheers,

Allard

Hi Allard,

Just to be clear, during this post, let's say Axon has a
published interface named EventWrapper. The EventWrapper
is a generic interface parametrized on an interface of
type (Java) Object (which hereon I will call it Payload)

An instance of the Payload class is the actual event from the
user's point of view.
The Payload class is a serializable (tagged by Java's Serializable
interface) and immutable Value Object. (I'm not so sure everyone
agrees with the immutable bit, but I think it helps to keep Axon
high-octane) Expecting anything more from the Payload class (e.g. the
event-id) should be via Axon-defined Java annotations with the
possibility
of defining them externally (for example via an XML file). In
particular
expecting the Payload to implement any kind of interfaces (other
than Serializable)is going to make the class unusable outside of Axon
and therefore hurt the integration

The EventWrapper interface provides some methods to get/set the
payload
and a number of methods such as getHeaderItem to get/set the metadata.
The implementation of EventWrapper is private and just an internal
concern of the Axon framework and not exposed to the Axon applications
(otherwise it makes upgrading the Axon framework in future real
tough).
In particular the way the EventWrapper instances are serialized is
also
hidden from the applications (with the exception of the Payload VO
part
if serialized via an applicaiton-defined algorithm)
Regarding the payload part, It is by default serialized/deserialized
by
an Axon provided algorithm unless the Axon's algorithm is overridden
explicitly by the Axon application.

The EventWrapper interface will provide a getPayloadSerialized which
will return a byte-array containing the serialized form of the
payload.
Obviously, if the event is serialized by the application's algorithm,
the application can de-serialize it, otherwise the application can do
nothing with the byte-array other than possibly feeding it back to the
Axon later. (I'm not sure the feeding back part is necessary and even
if it is necessary it is a source of complication for Axon or not)

Am I on the right track so far? If not please correct me.

If so, then we can continue as to how the Axon-application can
override
the default algorithm of Axon for serialization/deserialization.

Is there any other important detail that I've missed to notice
and therefore havn't mentioned above?

/Esfnad

Hi Esfand,

that’s almost completely like I see it. With a few minor differences though. I’ve spent some time today designing/refactoring the domain classes in the trunk. Nothing is committed yet. It’s quite a big chunk to refactor.

There are two interfaces: Event and DomainEvent (extends Event). Let’s stick to Event for a while.
The Event exposes methods to get the payload (any object), as well as the MetaData (read-only Map of String to Object). Besides that, there are some methods required by different Axon-specific mechanisms (fond mostly on the DomainEvent interface).

Axon’s API’s will use the Event and DomainEvent interfaces everywhere. The exceptions to this are annotation support and the apply() method in the aggregate. They both allow you to pass the payload only. In Event Handlers, you might sometimes require some data from the headers. There will be an annotation to handle that.
For example

@EventHandler
void handleMyEvent(MyEvent event, @Header(“userId”) String userId);

Where MyEvent is an application-specific POJO, and the userId is taken from the meta-data under key “userId”.

Now, let’s put serialization into play. There are two different areas where serialization is needed: storage and messaging. Storage is done by the EventStore, messaging by EventBus connectors that forward events as messages on e.g. AMQP.

The EventStore will take the message apart and use a serializer to persist the payload. The rest of the information is stored separately. The JPAEventStore will store this information in table columns. When an event is loaded, the EventStore will instantiate a DomainEvent implementation which contains some optimizations to prevent unnecessary deserialization.

A similar approach is taken with the messaging. Since most messaging systems have a concept of body and headers, a connector could serialize the Event’s payload (the application’s event) as the message body and the meta-data as the message headers. Axon-specific information, such as the Event Identifier can be added to the message headers. If there is an Axon-connector listening on the other side, it knows how to fetch the identifier from the headers and construct a new Event with meta-data and payload.

The serialization mechanism can be chosen on a per-situation basis. This means that you can choose a different serializer for storage, where upcasting is important to allow refactoring, and for communication with a third party, where the format is often specified up-front.

This approach means that Axon’s Event implementation itself will never need to be serialized using the Serializer, only the payload and maybe the meta-data values.

The lazy deserialization approach is just an implementation detail of the Event. Nobody needs to know ;-).

Cheers,

Allard

High load thinking here... It stinks of smoke :wink:
Hi Allard what I read is really cool and definitively message oriented, which IMHO is a good thing.
Just a dumb question: Why don't do the same on the command side?

Seems like an excellent approach!
Thanks a lot for your explanations.

A minor comment on the naming: The name "Event" is sooo
overused. It is also used by the Java proper. The same
interface is used by Java CDI too. So, it makes it likely
to have both Event interfaces imported into the same class.
Besides, although it is the Event from the framework's vantage
point, it is in essence the EventWrapper or EventEnvelope etc
from the user's point of view. Customer's come first, right?
:slight_smile:

/ Esfand

Lol
It seems I've missed the other post:
https://groups.google.com/forum/m/#!topic/axonframework/I_XGBscFCHo

Cheers
Domenico

I really like where this is going! I’ve been wanting to make a
protobuf-serializer for a while and this "your own serialized payload
in an envelope" thing would be a perfect match for that. No need to
make the axon classes work with the protobuf compiler in some magic
way. I just keep my domain events like I want them and put them inside
the axon envelope.