Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

Event Streams on Azure - Wrapping Events in Identity and Context

5.00/5 (1 vote)
9 Feb 2016CPOL1 min read 6.1K  
How to wrap the business meaningful events in an event store with identity and context information, stored according to the backing technology

This article is an entry in our Microsoft Azure IoT Contest. Articles in this section are not required to be full articles so care should be taken when voting.

As per posts passim, the event instance holds the business meaningful data about the event that occurred. These properties can be read by projections in order to build up some view of the current state of an aggregate.

However, there are also aspects we need to store for any given event that do not have a business meaningful aspect: the identity of the event which uniquely identifies the particular instance and the context of the event which is additional logging or status information we want to store for diagnostic purposes only.

In order to keep the event processing as pure as possible, I want to keep this extra information outside the event data itself (therefore making it unavailable to a projection so developers don't inadvertently use context information in a business process).

To do this, I introduce an interface that defines how an event is wrapped in its identity:

VB.NET
''' <summary>
''' Interface to allow unique identification of an event
''' </summary>
''' <typeparam name="TAggregate">
''' The type which identifies the aggregation
''' </typeparam>
''' <remarks>
''' These are the infrastructure elements of an event that do not have any business meaning.
''' </remarks>
Public Interface IEventIdentity(Of TAggregate As CQRSAzure.EventSourcing.IAggregationIdentifier)

    ''' <summary>
    ''' Get the identifier by which this events aggregate is uniquely known
    ''' </summary>
    ''' <remarks>
    ''' Most implementations use a GUID for this but if you have a known unique identifier 
    ''' then that can be used instead - e.g. ISBN, CUSIP, VIN etc.
    ''' </remarks>
    Function GetAggregateIdentifier() As String

    ''' <summary>
    ''' The event sequence - this is the order in which the events occurred for the aggregate
    ''' </summary>
    ReadOnly Property Sequence As UInteger

    ''' <summary>
    ''' The event that is identified by this event identity
    ''' </summary>
    ReadOnly Property EventInstance As IEvent(Of TAggregate)

End Interface

This in turn can be wrapped inside a interface that provides the additional context information:

VB.NET
''' <summary>
''' Additional context information about an event 
''' </summary>
''' <remarks>
''' Different domains often require additional context information about events that occurred
''' By having a separate context interface you can segregate these from the actual event itself
''' </remarks>
Public Interface IEventContext
    Inherits IEventInstance

    ''' <summary>
    ''' Which user caused the event to occur
    ''' </summary>
    ''' <remarks>
    ''' This can be empty in the case of timer or state triggered events
    ''' </remarks>
    ReadOnly Property Who As String

    ''' <summary>
    ''' The time at which this event occurred
    ''' </summary>
    ''' <remarks>
    ''' This should be stored as UTC or have timezone information
    ''' </remarks>
    ReadOnly Property Timestamp As Date

    ''' <summary>
    ''' The source from whence this event originated
    ''' </summary>
    ReadOnly Property Source As String

    ''' <summary>
    ''' Sequence for holding events in a queue or queue-like storage
    ''' </summary>
    ReadOnly Property SequenceNumber As Long

    ''' <summary>
    ''' Any additional comments attached to the event for audit purposes for example
    ''' </summary>
    ReadOnly Property Commentary As String

End Interface

Public Interface IEventContext(Of TAggregationKey)
    Inherits IEventContext
    Inherits IEventInstance(Of TAggregationKey)

End Interface

The reason that these are kept as interfaces is that the actual implementation of the backing store for the event may affect how the instance and context data are stored - for example in an SQL database, you would probably have the identity and context information as separate fields in the table(s) whereas for a file based solution, the identity part might be derived from the filename the event is stored in and the offset in that file could be the event sequence number.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)