Anatomy of a Message

In Axon, all communication between components is done with explicit messages, represented by the Message interface. A Message consists of a Payload, which is an application-specific object that represents the actual functional message, and Meta Data, which is a key-value pair describing the context of the message.

Each of Message's sub-interfaces represent a specific type of message, and defines additional information that describes that Message. Unlike any Meta Data, this additional information defines information required for correct processing of that type of message.

Messages are immutable. That means that, to add a Meta Data element, you effectively create a new Message instance, with an additional (or other) Meta Data element. To still be able to consider two Java-instances of a message as representing the same conceptual Message, each message has an identifier. Changing Meta Data of a message will not change this identifier.

Meta Data

Meta Data of messages typically describe the context in which a Message was generated. For example, Meta Data might contain information about the message that caused this Message to be generated (e.g. Command handlers generate Events based on an incoming Command).

In Axon, Meta Data is represented as a Map of String to Object. While you are free to add any type of object as the Meta Data value, we strongly recommend sticking to primitives and Strings, or at most simple objects. Meta Data does not come with the same flexibility as the payload, when it comes to structural changes.

Unlike a regular Map<String,Object>, MetaData in Axon is immutable. Mutating methods will create and return a new instance, instead of modifying an existing one.

MetaData metaData = MetaData.with("myKey", 42) // 1
.and("otherKey", "some value"); // 2
  1. Creates a MetaData instance with the indicated key-value pair

  2. Add an additional key-value pair, returning a new instance of MetaData.

Meta Data in message works similarly:

EventMessage eventMessage =
GenericEventMessage.asEventMessage("myPayload") // 1
.withMetaData(singletonMap("myKey", 42)) // 2
.andMetaData(singletonMap("otherKey", "some value")); // 2
  1. Create an EventMessage with "myPayload" as payload

  2. withMetaData replaces any Meta Data in the message with the given Map. In this case

    java.util.Collections.singletonMap() is used to define a single entry.

  3. andMetaData adds the entries from the given Map to the Message's meta data. Existing entries with equal keys are overwritten.

    Note - about MetaData implementing Map<String, Object> MetaData also implements Map<String, Object>, meaning that instead of passing a singletonMap (or any other type of Map), you can also use MetaData.with(key, value). As MetaData is immutable, all mutating operations of Map will throw an UnsupportedOperationException.

Message-specific data

Certain types of messages provide extra information, on top of what the Message provides, itself. For example, an EventMessage (an interface extending Message) also provides a timestamp, representing the time at which the event was generated. The QueryMessage carries, besides Payload and Meta Data, also a description of the type of response that the requesting component expects.