get-eventually / eventually-rs

Event Sourcing for Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Move Optimistic Locking using Versions in AggregateRoot

ar3s3ru opened this issue · comments

Optimistic Locking through Versioning is a core functionality of Event Sourcing, because it allows:

  • Concurrency update conflicts detection
  • Idempotency

Given the simplistic design of an Aggregate -- which only updates the State and handles Commands -- versioning should be part of the AggregateRoot structure.

pub struct AggregateRoot<T> {
    id: T::Id,
    state: T::State,
    version: u64,
    // Other stuff
}

version contains the current version of the AggregateRoot, and it should be used by the Repository when appending new events.

The EventStore interface should change as such:

pub trait EventStore {
    // As usual...

    // The version to use for appending events must be specified during
    // the function call: this way, callers can adhere to idempotency and OCC
    // that will be handled by the underlying store implementations.
    fn append(&mut self, id: Self::SourceId, version: u64, events: Vec<Self::Event>) -> ...
}

This might now mean that a difference must be made between new Domain Events (EventStore::Event) and Events from the EventStream (committed events).

In short, this is how the EventStore interface might look like:

pub struct PersistedEvent<T> {
    version: u64,
    sequence_number: u64,
    committed_at: DateTime<Utc>, // Still unsure if needed
    event: T,
}

pub trait EventStore {
    // Same as usual ...

    fn stream(&self, id: Self::SourceId, from: u64) ->
        BoxFuture<Result<BoxStream<Result<PersistedEvent<Self::Event>, Self::Error>>, Self::Error>>
}

Very complicated type, let's break it down:

BoxFuture< // Asynchronous operation, requires a Future
    Result< // Creating a Stream might fail (e.g. connection lost, etc.) 
        BoxStream< // The actual iterable EventStream
            Result< // Since we're streaming from the network,
                    // there might be some errors when streaming an element.
                PersistedEvent<Self::Event>, // The persisted representation of the Event.
                Self::Error // The error in case the network fails
            >
        >,
        Self::Error // Same as above
    >
>