SES - Podstawowe interfejsy

Dzisiaj zrobiłem kilka zmian, choć nie wiele, bo miałem na głowie inną pilną robotę. Wieczorkiem usiadłem i w sumie nie wszystko co wcześniej napisałem mi się podoba. Jutro zatem duże zmiany.

Mimo to jest kilka rzeczy, które w miarę się krystalizują i prawdopodobnie w tej postaci zostaną zachowane.

IEventStore

Podstawowy interface całej biblioteki, czyli IEventStore.

public interface IEventStore
{
    Task<IReadOnlyEventStream> Load(
        Guid streamId,
        bool pessimisticLock,
        CancellationToken cancellationToken = default(CancellationToken));

    Task SaveChanges(
        Guid streamId,
        int expectedVersion,
        IEventStream stream,
        CancellationToken cancellationToken = default(CancellationToken));
}

Definiuje tylko dwie metody:

Oczywiście pod nimi kryje się troszkę logiki takiej jak serializacja/deserializacja, konwersja do nowszej wersji, rozwiązywanie konfliktów w przypadku wykrycia kolizji wersji, itd.

IEvent (i IMemento)

Zdarzenie marker, ale ułatwia kilka rzeczy. Po pierwsze w łatwy sposób mogę posługiwać się snapshotem, gdzie jeśli istnieje to zawsze będzie pierwszym elementem w liście odczytanych zdarzeń, bo IMemento dziedziczy po IEvent.

Dzięki temu mogę zrobić tak:

var events = await _settings.Persistor.Load(streamId, pessimisticLock);
var snapshot = events[0] as IMemento;
var currentVersion = snapshot?.Version + events.Count ?? events.Count;
return new ReadOnlyEventStream(events, currentVersion);

Słowem wyjaśnienia. Ładujemy zdarzenia. Sprawdzamy, czy pierwszy element to IMemento. Jeśli tak to znaczy, że mamy załadowany snapshot, a zdarzenia są tylko te, które mają wersję od niego starszą. Jeśli nie jest to IMemento to znaczy, że mamy załadowane wszystkie zdarzenia dla danego streamId.

Takie podejście przekłada się także na to jak będzie wyglądała implementacja IAggregate, ale o tym innym razem.

IReadOnlyEventStream

Definuje obiekt, w którym dostaniemy w całości załadowane dane dla danego streamId.

public interface IReadOnlyEventStream
{
    IReadOnlyList<IEvent> Events { get; } // Lista zdarzeń
    int CurrentVersion { get; } // Bieżąca wersja w bazie danych
}

IEventStream

W odróżnieniu od poprzedniego interfejsu IEventStream definiuje nam obiekt, który wysyłamy do eventstore nowe zdarzenia, aby zapisał je do bazy danych.

public interface IEventStream
{
    Guid CommitId { get; } // Identyfikator zmiany
    IEvent[] Events { get; } // Lista nowych zdarzeń
    IDictionary<string, object> Metadata { get; set; } // Metadane, związane z zapisywanymi zdarzeniami
}

PesimissticLock

Pewnie intryguje Was ten parametr. I słusznie. W sumie to się zastanawiam czy nie powinienem zmienić, aby miał domyślną wartość na false. A skąd w ogóle taki pomysł, aby coś takiego umieszczać w EventStore? O tym w kolejnym wpisie. :)

Komentarze