''' <summary>
''' A class to persist events to memory
''' </summary>
''' <typeparam name="TAggregate">
''' The class that provides the way of identifying the objects that this file
''' backed event store will store
''' </typeparam>
''' <remarks>
''' This is not optimised as there is no aggregate indexing going on so should only
''' be used in unit testing or demonstration code
''' </remarks>
Public Class MemoryBackedEventStore(Of TAggregate As IAggregateIdentity)
Implements IEventStore
Private m_eventsStore As New Dictionary(Of String, List(Of IEventContext))
Private m_eventVersion As New Dictionary(Of String, UInteger)
Private m_sequence As UInteger = 0
Public Function GetEvents(AggregateIdentifier As String) As IEnumerable(Of IEvent) Implements IEventStoreReader.GetEvents
Return GetEvents(AggregateIdentifier, 0)
End Function
Public Function GetEvents(AggregateIdentifier As String, StartingVersion As UInteger) As IEnumerable(Of IEvent) Implements IEventStoreReader.GetEvents
If (m_eventsStore.ContainsKey(AggregateIdentifier)) Then
Return m_eventsStore(AggregateIdentifier).Where(Function(o) o.Version >= StartingVersion).Select(Function(e) e.EventInstance)
Else
Return New List(Of IEvent)
End If
End Function
Public Function GetEventsWithContext(Aggregateidentifier As String) As IEnumerable(Of IEventContext) Implements IEventStoreReader.GetEventsWithContext
If (m_eventsStore.ContainsKey(Aggregateidentifier)) Then
Return m_eventsStore(Aggregateidentifier)
Else
Return New List(Of IEventContext)
End If
End Function
Public Sub SaveEvents(AggregateIdentifier As String, StartingVersion As UInteger, Events As IEnumerable(Of IEvent)) Implements IEventStoreWriter.SaveEvents
Dim currentVersion As UInteger = GetCurrentversionNumber(AggregateIdentifier)
If (currentVersion < StartingVersion) Then
'A gap will be left but this does no harm as the store is is immutable and forward only
currentVersion = StartingVersion
End If
If Not (m_eventsStore.ContainsKey(AggregateIdentifier)) Then
m_eventsStore.Add(AggregateIdentifier, New List(Of IEventContext))
End If
For Each thisEvent As IEvent In Events
'add the event
m_eventsStore(AggregateIdentifier).Add(AddContext(AggregateIdentifier, currentVersion, thisEvent))
'increment the version
currentVersion = currentVersion + 1
Next
'Set the max version number
m_eventVersion(AggregateIdentifier) = currentVersion
End Sub
Private Function GetCurrentversionNumber(ByVal Aggregateidentifier As String) As UInteger
If (Not m_eventVersion.ContainsKey(Aggregateidentifier)) Then
m_eventVersion.Add(Aggregateidentifier, 0)
End If
Return m_eventVersion(Aggregateidentifier)
End Function
''' <summary>
''' Add a context wrapper around this event
''' </summary>
''' <param name="AggregateIdentifier">
''' The unique key against which an event history is aggregated
''' </param>
''' <param name="Version">
''' The version of the event to save
''' </param>
''' <param name="eventToContextualise">
''' The actual event being written
''' </param>
Private Function AddContext(ByVal AggregateIdentifier As String, ByVal Version As UInteger, ByVal eventToContextualise As IEvent) As IEventContext
m_sequence = m_sequence + 1
Return ContextualisedEvent.Create(AggregateIdentifier, Version, m_sequence, eventToContextualise)
End Function
Private Class ContextualisedEvent
Implements IEventContext
ReadOnly m_aggregateidentifier As String
ReadOnly m_version As UInteger
ReadOnly m_sequence As UInteger
ReadOnly m_event As IEvent
Private m_timestamp As Date = Date.UtcNow
Private Sub New(ByVal AggregateIdentifierInit As String,
ByVal VersionInit As UInteger,
ByVal SequenceInit As UInteger,
ByVal EventInit As IEvent)
m_aggregateidentifier = AggregateIdentifierInit
m_version = VersionInit
m_sequence = SequenceInit
m_event = EventInit
End Sub
Public ReadOnly Property Commentary As String Implements IEventContext.Commentary
Get
'This context does not support comments yet
Return ""
End Get
End Property
Public ReadOnly Property SequenceNumber As UInteger Implements IEventContext.SequenceNumber
Get
Return m_sequence
End Get
End Property
Public ReadOnly Property Source As String Implements IEventContext.Source
Get
Return ""
End Get
End Property
Public ReadOnly Property Timestamp As Date Implements IEventContext.Timestamp
Get
Return m_timestamp
End Get
End Property
Public ReadOnly Property Who As String Implements IEventContext.Who
Get
Return ""
End Get
End Property
Public Function GetAggregateIdentifier() As String Implements IEventIdentity.GetAggregateIdentifier
Return m_aggregateidentifier
End Function
Public ReadOnly Property Version As UInteger Implements IEventIdentity.Version
Get
Return m_version
End Get
End Property
Public ReadOnly Property EventInstance As IEvent Implements IEventIdentity.EventInstance
Get
Return m_event
End Get
End Property
Public Shared Function Create(ByVal AggregateIdentifierInit As String,
ByVal VersionInit As UInteger,
ByVal SequenceInit As UInteger,
ByVal EventInit As IEvent) As ContextualisedEvent
Return New ContextualisedEvent(AggregateIdentifierInit,
VersionInit,
SequenceInit,
EventInit)
End Function
End Class
End Class