JournaledGrain Basics
Ziarna dziennika pochodzą z JournaledGrain<TGrainState,TEventBase>klasy z następującymi parametrami typu:
- Obiekt
TGrainState
reprezentuje stan ziarna. Musi to być klasa z publicznym konstruktorem domyślnym. TEventBase
jest typowym supertypem dla wszystkich zdarzeń, które mogą być wywoływane dla tego ziarna, i może być dowolną klasą lub interfejsem.
Wszystkie obiekty stanu i zdarzeń powinny być serializowalne (ponieważ dostawcy spójności dzienników mogą wymagać ich utrwalania i/lub wysyłania ich w komunikatach powiadomień).
W przypadku ziarna, których zdarzenia są obiektami POC (zwykłe stare obiekty języka C#), JournaledGrain<TGrainState> można użyć jako skrótu dla elementu JournaledGrain<TGrainState,TEventBase>.
Odczytywanie stanu ziarna
Aby odczytać bieżący stan ziarna i określić jego numer wersji, JournaledGrain ma właściwości
GrainState State { get; }
int Version { get; }
Numer wersji jest zawsze równy całkowitej liczbie potwierdzonych zdarzeń, a stan jest wynikiem zastosowania wszystkich potwierdzonych zdarzeń do stanu początkowego. Początkowy stan, który ma wersję 0 (ponieważ nie zastosowano do niego zdarzeń), jest określany przez domyślny konstruktor klasy GrainState.
Ważne: aplikacja nigdy nie powinna bezpośrednio modyfikować obiektu zwróconego przez State
element . Jest przeznaczony tylko do czytania. Zamiast tego, gdy aplikacja chce zmodyfikować stan, musi to zrobić pośrednio przez wywoływanie zdarzeń.
Zgłaszanie zdarzeń
Wywoływanie zdarzeń odbywa się przez wywołanie RaiseEvent funkcji. Na przykład ziarno reprezentujące czat może wywołać element , PostedEvent
aby wskazać, że użytkownik przesłał wpis:
RaiseEvent(new PostedEvent()
{
Guid = guid,
User = user,
Text = text,
Timestamp = DateTime.UtcNow
});
Należy pamiętać, że RaiseEvent
rozpoczyna zapisywanie w celu uzyskania dostępu do magazynu, ale nie czeka na ukończenie zapisu. W przypadku wielu aplikacji ważne jest, aby poczekać, aż potwierdzimy, że zdarzenie zostało utrwalone. W takim przypadku zawsze następuje oczekiwanie na ConfirmEvents:
RaiseEvent(new DepositTransaction()
{
DepositAmount = amount,
Description = description
});
await ConfirmEvents();
Należy pamiętać, że nawet jeśli nie zostanie jawnie wywołane ConfirmEvents
, zdarzenia zostaną ostatecznie potwierdzone — nastąpi to automatycznie w tle.
Metody przejścia stanu
Środowisko uruchomieniowe automatycznie aktualizuje stan ziarna przy każdym wywoływaniu zdarzeń. Nie ma potrzeby, aby aplikacja jawnie aktualizowała stan po wystąpieniu zdarzenia. Jednak aplikacja nadal musi podać kod określający sposób aktualizowania stanu w odpowiedzi na zdarzenie. Można to zrobić na dwa sposoby.
(a) Klasa GrainState może zaimplementować co najmniej jedną Apply
metodę na obiekcie StateType
. Zazwyczaj jeden z nich tworzy wiele przeciążeń, a najbliższe dopasowanie jest wybierane dla typu środowiska uruchomieniowego zdarzenia:
class GrainState
{
Apply(E1 @event)
{
// code that updates the state
}
Apply(E2 @event)
{
// code that updates the state
}
}
(b) Ziarno może zastąpić TransitionState
funkcję:
protected override void TransitionState(
State state, EventType @event)
{
// code that updates the state
}
Zakłada się, że metody przejścia nie mają skutków ubocznych innych niż modyfikowanie obiektu stanu i powinny być deterministyczne (w przeciwnym razie efekty są nieprzewidywalne). Jeśli kod przejścia zgłasza wyjątek, ten wyjątek zostanie przechwycony i uwzględniony w ostrzeżeniu w dzienniku Orleans wystawionym przez dostawcę spójności dzienników.
Kiedy dokładnie środowisko uruchomieniowe wywołuje metody przejścia, zależy od wybranego dostawcy spójności dziennika i jego konfiguracji. Aplikacje nie powinny polegać na określonym chronometrażu, z wyjątkiem sytuacji, gdy jest to gwarantowane przez dostawcę spójności dzienników.
Niektórzy dostawcy, tacy jak Orleans.EventSourcing.LogStorage dostawca spójności dzienników, powtarzają sekwencję zdarzeń za każdym razem, gdy ziarno jest ładowane. W związku z tym, o ile obiekty zdarzeń mogą być nadal prawidłowo deserializowane z magazynu, można radykalnie zmodyfikować klasę GrainState
i metody przejścia. Jednak w przypadku innych dostawców, takich jak Orleans.EventSourcing.StateStorage dostawca spójności dzienników, tylko GrainState
obiekt jest utrwalany, dlatego deweloperzy muszą upewnić się, że może być poprawnie deserializowany podczas odczytu z magazynu.
Zgłaszanie wielu zdarzeń
Istnieje możliwość wykonania wielu wywołań przed RaiseEvent
wywołaniem metody ConfirmEvents
:
RaiseEvent(e1);
RaiseEvent(e2);
await ConfirmEvents();
Jednak może to spowodować dwa kolejne dostępy do magazynu i wiąże się z ryzykiem, że ziarno kończy się niepowodzeniem po zapisaniu tylko pierwszego zdarzenia. W związku z tym zazwyczaj lepiej jest zgłaszać wiele zdarzeń jednocześnie, używając polecenia
RaiseEvents(IEnumerable<EventType> events)
Gwarantuje to, że dana sekwencja zdarzeń jest zapisywana w magazynie niepodziealnie. Należy pamiętać, że ponieważ numer wersji zawsze odpowiada długości sekwencji zdarzeń, podniesienie liczby zdarzeń zwiększa liczbę wersji o więcej niż jedną naraz.
Pobieranie sekwencji zdarzeń
Następująca metoda z klasy bazowej JournaledGrain
umożliwia aplikacji pobranie określonego segmentu sekwencji wszystkich potwierdzonych zdarzeń:
Task<IReadOnlyList<EventType>> RetrieveConfirmedEvents(
int fromVersion,
int toVersion);
Nie jest to jednak obsługiwane przez wszystkich dostawców spójności dzienników. Jeśli nie jest obsługiwana lub jeśli określony segment sekwencji nie jest już dostępny, NotSupportedException zgłaszany jest element .
Aby pobrać wszystkie zdarzenia do najnowszej potwierdzonej wersji, należy wywołać polecenie
await RetrieveConfirmedEvents(0, Version);
Można pobrać tylko potwierdzone zdarzenia: zgłaszany jest wyjątek, jeśli toVersion
jest większy niż bieżąca wartość właściwości Version
.
Ponieważ potwierdzone wydarzenia nigdy się nie zmieniają, nie ma wyścigów, aby się martwić, nawet w obecności wielu wystąpień lub opóźnione potwierdzenie. Jednak w takich sytuacjach wartość właściwości Version
może być większa do czasu await
wznowienia niż w momencie RetrieveConfirmedEvents
wywołania, więc może być zalecane zapisanie jej wartości w zmiennej. Zobacz również sekcję dotyczącą gwarancji współbieżności.