Saga w mikroserwisach, w jakiej warstwie umieścic?

0

Cześć, w jakiej warstwie umieszczacie sagę w mikroserwisach? U mnie jest to fizyczny folder "/saga" jako event-handlery. To co mnie zastanawia, to czy to dobre miejsce, bo kazdy event-handler musze rejestrowac i wstrzykiwac serwis sagi i przekazywac mu event. Trochę smierdzi anty-patternem.

Pozdrawiam

0

Tak jak w komentarzach w sumie, saga to po prostu taki "proces manager". I zastanawiam sie w jakiej warstwie go umiescic, ja go umiescilem w event handlerze i zastanawiam sie czy to ok. Nasluchuje na N eventow i wsztrzykuje tam serwis ktory odpowiada co ma sie zdarzyc po evencie X, i tak jak ktos wspomnial, taka saga powinna tez wiedziec co zrobic w razie failu jakiejs uslugi.

0

Co to znaczy w event handlerze ? Saga powinna być startowana na jakiś event i subskrybować się na niego. Jeśli to dla przykładu OrderSaga powinna znajdować się w OrderService.

0
error91 napisał(a):

Co to znaczy w event handlerze ? Saga powinna być startowana na jakiś event i subskrybować się na niego. Jeśli to dla przykładu OrderSaga powinna znajdować się w OrderService.

No zwykle używa się handlera, żeby np. przemalować event na komendę i puścić ją do serwisu.

0

@._.:
Generalnie mam mechanizm Sagi, to znaczy nasłuchuję na wszystkie eventy i we zwrotce może polecieć komenda, która trafia do command busa i tutaj właśnie może być kilka rozwiązań, które mi wypadły do głowy.

  1. Taka saga jest dummy, czyli zwraca bezposrednio komende. Zdaczylo się X, wykonaj komende Y. I ta komenda to będzie cała logika tej sagi. Czyli, mniej więcej robimy coś takiego Saga(Event) --> SagaAggregateComman(Event). Ta komenda to agregat ktory lapie eventy i on juz wie, gdzie co zrobic (np. do jakiej kolejki Rabbita wyslac itp)
  2. Podejście nieco inne, cala logika bezposrednio w tej sadze, czyli Saga(Event) -> logika w sadze -> Command(x), mówiąc po polsku, do sagi leci event, robimy jakąś robotę (tak jak wyżej, jakaś wysyłka do kolejki albo coś) i na koniec zwracamy komende (jeśli byśmy chcieli znowu na to zareagować)

Teraz pytanie brzmi, czy te podejścia są ok? Jeśli tak, to waszym zdaniem, które lepsze?

1
Brunatny Rycerz napisał(a):

@._.:
Generalnie mam mechanizm Sagi, to znaczy nasłuchuję na wszystkie eventy i we zwrotce może polecieć komenda, która trafia do command busa i tutaj właśnie może być kilka rozwiązań, które mi wypadły do głowy.

  1. Taka saga jest dummy, czyli zwraca bezposrednio komende. Zdaczylo się X, wykonaj komende Y. I ta komenda to będzie cała logika tej sagi. Czyli, mniej więcej robimy coś takiego Saga(Event) --> SagaAggregateComman(Event). Ta komenda to agregat ktory lapie eventy i on juz wie, gdzie co zrobic (np. do jakiej kolejki Rabbita wyslac itp)
  2. Podejście nieco inne, cala logika bezposrednio w tej sadze, czyli Saga(Event) -> logika w sadze -> Command(x), mówiąc po polsku, do sagi leci event, robimy jakąś robotę (tak jak wyżej, jakaś wysyłka do kolejki albo coś) i na koniec zwracamy komende (jeśli byśmy chcieli znowu na to zareagować)

Teraz pytanie brzmi, czy te podejścia są ok? Jeśli tak, to waszym zdaniem, które lepsze?

Moim zdaniem podejście drugie jest lepsze bo cały proces masz w jednym miejscu. Polecam sposób jak to robi MassTransit w C#

public class OrderProcessSaga : MassTransitStateMachine<OrderState>
 {
       private string OperationId { get; set; }

       public OrderProcessSaga()
       {
           InstanceState(x => x.CurrentState);

           Event(() => OrderCreated, x => x.CorrelateById(context => context.Message.OrderId));
           Event(() => PaymentBegins, x => x.CorrelateById(context => context.Message.OrderId));
           Event(() => PaymentFails, x => x.CorrelateById(context => context.Message.OrderId));
           Event(() => PaymentSucceded, x => x.CorrelateById(context => context.Message.OrderId));
           Event(() => OrderSubmited, x => x.CorrelateById(context => context.Message.OrderId));

           Initially(
               When(OrderCreated)
                   .Then(context =>
                   {
                       context.Instance.OrderId = context.Data.OrderId;
                       if (context.TryGetPayload(out ConsumeContext<IOrderCreated> consumeContext))
                       {
                           var opId = consumeContext.Headers.Get("operationId", "");
                           if (!string.IsNullOrEmpty(opId))
                               OperationId = opId;
                       }
                   })
                   .TransitionTo(OrderRegistered));

           During(OrderRegistered,
               When(PaymentBegins)
                   .Then(context=> { context.Instance.PaymentId = context.Data.PaymentId; })
                   .TransitionTo(PaymentProcessBegin),
               When(PaymentFails)
                   .TransitionTo(PaymentProcessFails)
                   .Publish(context => new DeleteOrder{OrderId = context.Instance.OrderId})
                   .Finalize(),
               When(PaymentSucceded)
                   .TransitionTo(PaymentProcessSucceded)
                   .Publish(context => new OrderSubmitted{OrderId = context.Instance.OrderId})
                   .Finalize());
       }


       public State OrderRegistered { get; private set; }
       public State PaymentProcessBegin { get; private set; }
       public State PaymentProcessFails { get; private set; }
       public State PaymentProcessSucceded { get; private set; }
       public State OrderSubmitted { get; private set; }

       public Event<IOrderCreated> OrderCreated { get; private set; }
       public Event<IPaymentProcessBegin> PaymentBegins { get; private set; }
       public Event<IPaymentProcessFail> PaymentFails { get; private set; }
       public Event<IPaymentProcessSucceded> PaymentSucceded { get; private set; }
       public Event<IOrderSubmitted> OrderSubmited { get; private set; }
   }
}

0
  1. Taka saga jest dummy, czyli zwraca bezposrednio komende. Zdaczylo się X, wykonaj komende Y. I ta komenda to będzie cała logika tej sagi. Czyli, mniej więcej robimy coś takiego Saga(Event) --> SagaAggregateComman(Event). Ta komenda to agregat ktory lapie eventy i on juz wie, gdzie co zrobic (np. do jakiej kolejki Rabbita wyslac itp)

Tak też można, uważam, że chodzi ci o styl promowany przez Pat'a Helland'a. Gdzie proces długotrwały jest zaprojektowany jako zestaw współpracujących ze sobą Agregatów sygnalizujących swoją role w procesie eventami zwrotnymi, gdzie kilka tych agregatów może utrzymywać stan procesu (Saga).

  1. Podejście nieco inne, cala logika bezposrednio w tej sadze, czyli Saga(Event) -> logika w sadze -> Command(x), mówiąc po polsku, do sagi leci event, robimy jakąś robotę (tak jak wyżej, jakaś wysyłka do kolejki albo coś) i na koniec zwracamy komende (jeśli byśmy chcieli znowu na to zareagować)

Chyba najbardziej popularne podejście, sprawdza się przy mniejszych procesach.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.