diff --git a/src/saga.rs b/src/saga.rs index 114f5d5..27e6195 100644 --- a/src/saga.rs +++ b/src/saga.rs @@ -131,6 +131,25 @@ impl<'a, AR, A> Saga<'a, AR, A> { Saga { react: new_react } } + + /// Merges two sagas into one. + /// Creates a new instance of a Saga by merging two sagas of type `AR`, `A` and `AR`, `A2` into a new saga of type `AR`, `Sum` + pub fn merge(self, saga2: Saga<'a, AR, A2>) -> Saga<'a, AR, Sum> { + let new_react = Box::new(move |ar: &AR| { + let a: Vec> = (self.react)(ar) + .into_iter() + .map(|a: A| Sum::Second(a)) + .collect(); + let a2: Vec> = (saga2.react)(ar) + .into_iter() + .map(|a2: A2| Sum::First(a2)) + .collect(); + + a.into_iter().chain(a2).collect() + }); + + Saga { react: new_react } + } } /// Formalizes the `Action Computation` algorithm for the `saga` to handle events/action_results, and produce new commands/actions. diff --git a/tests/saga_test.rs b/tests/saga_test.rs index d96e778..2f08f83 100644 --- a/tests/saga_test.rs +++ b/tests/saga_test.rs @@ -30,6 +30,30 @@ fn order_saga<'a>() -> Saga<'a, OrderEvent, ShipmentCommand> { } } +fn order_saga_2<'a>() -> Saga<'a, Event, ShipmentCommand> { + Saga { + react: Box::new(|event| match event { + Event::OrderCreated(evt) => { + vec![ShipmentCommand::Create(CreateShipmentCommand { + shipment_id: evt.order_id, + order_id: evt.order_id, + customer_name: evt.customer_name.to_owned(), + items: evt.items.to_owned(), + })] + } + Event::OrderUpdated(_) => { + vec![] + } + Event::OrderCancelled(_) => { + vec![] + } + Event::ShipmentCreated(_) => { + vec![] + } + }), + } +} + fn shipment_saga<'a>() -> Saga<'a, ShipmentEvent, OrderCommand> { Saga { react: Box::new(|event| match event { @@ -43,15 +67,44 @@ fn shipment_saga<'a>() -> Saga<'a, ShipmentEvent, OrderCommand> { } } +fn shipment_saga_2<'a>() -> Saga<'a, Event, OrderCommand> { + Saga { + react: Box::new(|event| match event { + Event::ShipmentCreated(evt) => { + vec![OrderCommand::Update(UpdateOrderCommand { + order_id: evt.order_id, + new_items: evt.items.to_owned(), + })] + } + + Event::OrderCreated(_) => { + vec![] + } + Event::OrderUpdated(_) => { + vec![] + } + Event::OrderCancelled(_) => { + vec![] + } + }), + } +} + #[test] fn test() { let order_saga: Saga = order_saga(); let order_saga2: Saga = crate::order_saga(); + let order_saga_2: Saga = crate::order_saga_2(); let shipment_saga: Saga = shipment_saga(); + let shipment_saga_2: Saga = crate::shipment_saga_2(); let combined_saga = order_saga2 .combine(shipment_saga) .map_action(&sum_to_command) .map_action_result(&event_from_sum); + let merged_saga = order_saga_2 + .merge(shipment_saga_2) + .map_action(&sum_to_command); + let order_created_event = OrderEvent::Created(OrderCreatedEvent { order_id: 1, customer_name: "John Doe".to_string(), @@ -72,6 +125,7 @@ fn test() { customer_name: "John Doe".to_string(), items: vec!["Item 1".to_string(), "Item 2".to_string()], }); + let combined_commands = combined_saga.compute_new_actions(&order_created_event2); assert_eq!( combined_commands, @@ -82,4 +136,15 @@ fn test() { items: vec!["Item 1".to_string(), "Item 2".to_string()], })] ); + + let merged_commands = merged_saga.compute_new_actions(&order_created_event2); + assert_eq!( + merged_commands, + [Command::ShipmentCreate(CreateShipmentCommand { + shipment_id: 1, + order_id: 1, + customer_name: "John Doe".to_string(), + items: vec!["Item 1".to_string(), "Item 2".to_string()], + })] + ); }