2012-12-11 14 views
10

Czasami opracowuję mały projekt wykorzystujący wzór CQRS i Event Sourcing. Mam problem strukturalny i nie jestem świadomy, jakie rozwiązanie należy podjąć, aby go rozwiązać.CQRS/Event Sourcing, jak uzyskać spójne dane do stosowania reguł biznesowych?

Wyobraź sobie następujący przykład: Polecenie jest wysyłane z informacją, że klient banku zdeponował pewną kwotę pieniędzy (Depozytowa Polecenie). W procedurze obsługi komend/Entity/Aggregate (nie jest ważne dla dyskusji) należy zastosować regułę biznesową; Jeśli klient jest jednym z 10 największych graczy, który ma więcej pieniędzy na koncie, wygrać nagrodę.

Pytanie brzmi: jak mogę uzyskać aktualne, spójne dane, aby wiedzieć, czy klient po złożeniu depozytu znajduje się w top 10%.

  • Nie mogę użyć składnicy zdarzeń, ponieważ nie można utworzyć takiego zapytania;
  • Nie jestem pewien, czy mogę korzystać z modelu odczytu, ponieważ nie jest on w 100% zgodny z , który jest aktualny.

Jak to zrobić, w przypadkach, gdy potrzebne są dane z bazy danych do zastosowania reguły biznesowej? Jeśli nie zwracam uwagi na aktualne dane, natrafiam na możliwości przyznania nagrody dwóm różnym klientom:

Czekam na Twoją opinię.

Odpowiedz

6

Wszelkie informacje, które kruszywo wymaga do podjęcia decyzji biznesowych, powinny być przechowywane jako część stanu kruszywa. W związku z tym, po odebraniu polecenia, aby wpłacić pieniądze na konto klienta, powinieneś już mieć stan bieżący/aktualizacyjny dla tego klienta, który może zawierać bieżące saldo dla każdego z jego kont.

Sugerowałbym również, że agregat nigdy nie powinien iść do modelu odczytu w celu pobrania informacji. W zależności od tego, co próbujesz osiągnąć, możesz wzbogacić komendę o dodatkowe szczegóły z modelu odczytu (gdzie stan nie jest krytyczny), ale agregat sam powinien wyciągnąć swój własny znany stan.


EDIT

Po ponownym przeczytaniu pytanie, zdaję sobie sprawę, że mówisz o śledzenie stanu na wielu agregatów. To wchodzi w królestwo sagi. Możesz stworzyć sagę, która śledzi próg wymagany do uzyskania 10% najlepszych wyników. Tak więc, gdy klient dokonuje depozytu, saga może śledzić, gdzie umieszcza je w rankingu. Jeśli klient przekroczył nagłówek, możesz opublikować polecenie z sagi, aby wskazać, że spełnia on wymagane kryteria.

W twoim przypadku Twoja saga może śledzić całkowitą kwotę wszystkich depozytów, więc po dokonaniu depozytu można podjąć decyzję, czy klient znajduje się obecnie w top 10%. Inne pytania, które możesz zadać sobie samemu ... jeśli klient wpłaci X $ kwoty pieniędzy, a następnie natychmiast rozszerzy $ Y, aby wrócić z powrotem do threashold; co powinno się stać? Itp


Bardzo surowy agregat/metody handle saga ...

public class Client : Aggregate 
{ 
    public void Handle(DepositMoney command) 
    { 
     // What if the account is not known? Has insufficient funds? Is locked? etc... 
     // Track the minimum amount of state required to make whatever choice is required. 
     var account = State.Accounts[command.AccountId]; 

     // Balance here would reflect a point in time, and should not be directly persisted to the read model; 
     // use an atomic update to increment the balance for the read-model in your denormalizer. 
     Raise(new MoneyDeposited { Amount = command.Amount, Balance = account.Balance + command.Amount }); 
    } 

    public void Handle(ElevateClientStatus command) 
    { 
     // you are now a VIP... raise event to update state accordingly... 
    } 
} 

public class TopClientSaga : Saga 
{ 
    public void Handle(MoneyDeposited e) 
    { 
     // Increment the total deposits... sagas need to be thread-safe (i.e., locked while state is changing). 
     State.TotalDeposits += e.Amount; 

     //TODO: Check if client is already a VIP; if yes, nothing needs to happen... 

     // Depositing money itself changes the 10% threshold; what happens to clients that are no longer in the top 10%? 
     if (e.Balance > State.TotalDeposits * 0.10) 
     { 
      // you are a top 10% client... publish some command to do whatever needs to be done. 
      Publish(new ElevateClientStatus { ClientId = e.ClientId, ... }); 
     } 
    } 

    // handle withdrawls, money tranfers etc? 
} 
+1

Dziękuję za odpowiedź Calgary, masz mój punkt widzenia. Muszę przeczytać więcej o wzorze Saga, a następnie odpowiedzieć sformułowaną opinią. –

+0

, więc sugerujesz, że mam sagę, która jest tworzona i usuwana z aplikacją, jak singleton. ta saga śledzi całkowitą kwotę zdeponowanych pieniędzy. każdy klient, który wpłacił pieniądze, dodaje sumę do całkowitej sumy sagi. do przechowywania całkowitej kwoty używam magazynu zdarzeń? Pytam, ponieważ po uruchomieniu aplikacji muszę załadować bieżącą całkowitą ilość. to ma sens? dzięki –

+0

@JP - saga będzie zarządzać własnym stanem. W pewnym momencie utworzysz 'Oddział' lub' 'Bank', który może uruchomić sagę do śledzenia depozytów ogółem. Za każdym razem, gdy dokonywany jest depozyt lub inne powiązane zdarzenie, stan sagi byłby odzyskiwany (idealnie buforowany w pamięci), tak, że zawsze zna on sumę bieżącą. Jeśli twoja aplikacja musi znać sumę z innych powodów, to być może suma staje się własnością stanu skupienia 'bank' lub' branch'. Twoje agregaty/sagi są autorytetem dla wszystkich danych, nie chcesz iść do modelu odczytu dla tego typu informacji. Ma sens? –

Powiązane problemy