Message Internals

The Message object is the envelope in which events are stored and dispatched to consumers. It contains an event object and headers. The headers are meant for non-domain-event-specific information. EventSauce uses a number of headers internally. All of the internal headers are available as constants on the EventSauce\EventSourcing\Header interface:

constant value description
Header::EVENT_ID __event_id ID of the event (optional but recommended)
Header::EVENT_TYPE __event_type type of the event
Header::TIME_OF_RECORDING __time_of_recording when the event was recorded
Header::AGGREGATE_ROOT_ID __aggregate_root_id the aggregate root id
Header::AGGREGATE_ROOT_ID_TYPE __aggregate_root_id_type the type of aggregate root id
Header::AGGREGATE_ROOT_VERSION __aggregate_root_version the aggregate version (1-based sequence)

Message Decorator

If you want to add more headers you can use Message Decorators.

Message Serialization

For persistence and dispatching, messages are serialized. The MessageSerializer interface represents this boundary. By default the ConstructingMessageSerializer is used, which serializes the event to the following format (shown JSON encoded):

{
    "headers": {
        "__aggregate_root_id": "1234-1234-1234-1234"
    },
    "payload": {
        "key": "value"
    }
}

If needed you can create a custom implementation of the MessageSerializer to adapt to existing serialization formats in your application or in systems you interact with.

Class name inflection

By default, the ContructionMessageSerializer uses the DotSeparatedSnakeCaseInflector to convert event and id class names to a string when storing an event. When reconstructing the event from the repository it than used the class again to construct the get the FQN from the string.

For example:

Domain\BankAccount\DomainEvents\TransactionRecorded::class becomes Domain.bank_account.domain_events.transaction_recorded in the database.

This couples the event’s implementation details to the event storage, making it hard to refactor the Name or namespace of the event.

The ExplicitlyMappedClassNameInflector can be used to declare a map from event to string (added in 1.3.0).

new ConstructingMessageSerializer(
    new \EventSauce\EventSourcing\ExplicitlyMappedClassNameInflector([
        // Map event types to a specified event type
        TransactionRecorded::class => 'transactions.transaction_recoded',
        
        // Use an array to map multiple event types to a class name.
        // The first entry is used to map the class to, the others are
        // Used to map previously emit event types to the specified class
        OrderHasShipped::class => ['orders.order_has_shipped', 'old.event_name'],
    ])
)

This method allows us to freely rename or move event classes. A downside for this method, is that you need to be sure the class name is configured in lookup array.

Frank de Jonge

EventSauce is a project by Frank de Jonge.