From b97e50f985d846553202628f5785fd8184b96ac7 Mon Sep 17 00:00:00 2001 From: Ivan Dugalic Date: Tue, 28 Jan 2025 17:15:26 +0100 Subject: [PATCH] `combine` depricated on saga and view, in faviour of `merge` --- src/saga.rs | 6 ++ src/view.rs | 6 +- tests/aggregate_combined_test.rs | 37 ++++--- ...st.rs => materialized_view_merged_test.rs} | 25 ++--- ...ed_test.rs => saga_manager_merged_test.rs} | 36 ++++--- tests/saga_test.rs | 34 +------ tests/view_test.rs | 98 +++++-------------- 7 files changed, 98 insertions(+), 144 deletions(-) rename tests/{materialized_view_combined_test.rs => materialized_view_merged_test.rs} (92%) rename tests/{saga_manager_combined_test.rs => saga_manager_merged_test.rs} (72%) diff --git a/src/saga.rs b/src/saga.rs index 27e6195..4f8c319 100644 --- a/src/saga.rs +++ b/src/saga.rs @@ -117,6 +117,10 @@ impl<'a, AR, A> Saga<'a, AR, A> { /// Combines two sagas into one. /// Creates a new instance of a Saga by combining two sagas of type `AR`, `A` and `AR2`, `A2` into a new saga of type `Sum`, `Sum` + #[deprecated( + since = "0.8.0", + note = "Use the `merge` function instead. This ensures all your sagas can subscribe to all `Event`/`E` in the system." + )] pub fn combine(self, saga2: Saga<'a, AR2, A2>) -> Saga<'a, Sum, Sum> { let new_react = Box::new(move |ar: &Sum| match ar { Sum::First(ar) => { @@ -134,6 +138,8 @@ impl<'a, AR, A> Saga<'a, AR, A> { /// 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` + /// Similar to `combine`, but the event type is the same for both sagas. + /// This ensures all your sagas can subscribe to all `Event`/`E` in the system. 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) diff --git a/src/view.rs b/src/view.rs index 34c1b78..220475f 100644 --- a/src/view.rs +++ b/src/view.rs @@ -128,6 +128,10 @@ impl<'a, S, E> View<'a, S, E> { /// Combines two views into one. /// Creates a new instance of a View by combining two views of type `S`, `E` and `S2`, `E2` into a new view of type `(S, S2)`, `Sum` /// Combines two views that operate on different event types (`E`` and `E2``) into a new view operating on `Sum` + #[deprecated( + since = "0.8.0", + note = "Use the `merge` function instead. This ensures all your views can subscribe to all `Event`/`E` in the system." + )] pub fn combine(self, view2: View<'a, S2, E2>) -> View<'a, (S, S2), Sum> where S: Clone, @@ -161,7 +165,7 @@ impl<'a, S, E> View<'a, S, E> { /// Merges two views into one. /// Creates a new instance of a View by merging two views of type `S`, `E` and `S2`, `E` into a new view of type `(S, S2)`, `E` /// Similar to `combine`, but the event type is the same for both views. - /// Composes two views that operate on the same/shared event type (`E`) into a new view operating on `E` + /// This ensures all your views can subscribe to all `Event`/`E` in the system. pub fn merge(self, view2: View<'a, S2, E>) -> View<'a, (S, S2), E> where S: Clone, diff --git a/tests/aggregate_combined_test.rs b/tests/aggregate_combined_test.rs index fc4f711..c1a25ec 100644 --- a/tests/aggregate_combined_test.rs +++ b/tests/aggregate_combined_test.rs @@ -211,10 +211,10 @@ fn shipment_decider<'a>() -> Decider<'a, ShipmentCommand, ShipmentState, Shipmen } } -fn order_saga<'a>() -> Saga<'a, OrderEvent, ShipmentCommand> { +fn order_saga<'a>() -> Saga<'a, Event, ShipmentCommand> { Saga { react: Box::new(|event| match event { - OrderEvent::Created(evt) => { + Event::OrderCreated(evt) => { vec![ShipmentCommand::Create(CreateShipmentCommand { shipment_id: evt.order_id, order_id: evt.order_id, @@ -222,25 +222,38 @@ fn order_saga<'a>() -> Saga<'a, OrderEvent, ShipmentCommand> { items: evt.items.to_owned(), })] } - OrderEvent::Updated(_) => { + Event::OrderUpdated(_) => { vec![] } - OrderEvent::Cancelled(_) => { + Event::OrderCancelled(_) => { + vec![] + } + Event::ShipmentCreated(_) => { vec![] } }), } } -fn shipment_saga<'a>() -> Saga<'a, ShipmentEvent, OrderCommand> { +fn shipment_saga<'a>() -> Saga<'a, Event, OrderCommand> { Saga { react: Box::new(|event| match event { - ShipmentEvent::Created(evt) => { - vec![OrderCommand::Update(api::UpdateOrderCommand { + 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![] + } }), } } @@ -367,9 +380,8 @@ async fn orchestrated_event_sourced_aggregate_test() { .map_command(&command_from_sum) // Decider> .map_event(&event_from_sum, &sum_to_event); // Decider let combined_saga = order_saga() - .combine(shipment_saga()) - .map_action(&sum_to_command) - .map_action_result(&event_from_sum); + .merge(shipment_saga()) + .map_action(&sum_to_command); let repository = InMemoryEventRepository::new(); let aggregate = Arc::new(EventSourcedOrchestratingAggregate::new( repository, @@ -700,9 +712,8 @@ async fn state_stored_combined_test() { .map_event(&event_from_sum, &sum_to_event); // Decider let combined_saga = order_saga() - .combine(shipment_saga()) - .map_action(&sum_to_command) - .map_action_result(&event_from_sum); + .merge(shipment_saga()) + .map_action(&sum_to_command); let repository = InMemoryStateRepository::new(); let aggregate = Arc::new(StateStoredOrchestratingAggregate::new( diff --git a/tests/materialized_view_combined_test.rs b/tests/materialized_view_merged_test.rs similarity index 92% rename from tests/materialized_view_combined_test.rs rename to tests/materialized_view_merged_test.rs index fe80960..87ee6ff 100644 --- a/tests/materialized_view_combined_test.rs +++ b/tests/materialized_view_merged_test.rs @@ -7,30 +7,30 @@ use fmodel_rust::view::View; use fmodel_rust::Identifier; use crate::api::{ - OrderCancelledEvent, OrderCreatedEvent, OrderEvent, OrderUpdatedEvent, OrderViewState, - ShipmentEvent, ShipmentViewState, + OrderCancelledEvent, OrderCreatedEvent, OrderUpdatedEvent, OrderViewState, ShipmentViewState, }; -use crate::application::{event_from_sum, Event, MaterializedViewError}; +use crate::application::{Event, MaterializedViewError}; mod api; mod application; -fn order_view<'a>() -> View<'a, OrderViewState, OrderEvent> { +fn order_view<'a>() -> View<'a, OrderViewState, Event> { View { evolve: Box::new(|state, event| { let mut new_state = state.clone(); match event { - OrderEvent::Created(evt) => { + Event::OrderCreated(evt) => { new_state.order_id = evt.order_id; new_state.customer_name = evt.customer_name.to_owned(); new_state.items = evt.items.to_owned(); } - OrderEvent::Updated(evt) => { + Event::OrderUpdated(evt) => { new_state.items = evt.updated_items.to_owned(); } - OrderEvent::Cancelled(_) => { + Event::OrderCancelled(_) => { new_state.is_cancelled = true; } + Event::ShipmentCreated(_) => {} } new_state }), @@ -43,17 +43,20 @@ fn order_view<'a>() -> View<'a, OrderViewState, OrderEvent> { } } -fn shipment_view<'a>() -> View<'a, ShipmentViewState, ShipmentEvent> { +fn shipment_view<'a>() -> View<'a, ShipmentViewState, Event> { View { evolve: Box::new(|state, event| { let mut new_state = state.clone(); match event { - ShipmentEvent::Created(evt) => { + Event::ShipmentCreated(evt) => { new_state.shipment_id = evt.shipment_id; new_state.order_id = evt.order_id; new_state.customer_name = evt.customer_name.to_owned(); new_state.items = evt.items.to_owned(); } + Event::OrderCreated(_) => {} + Event::OrderUpdated(_) => {} + Event::OrderCancelled(_) => {} } new_state }), @@ -108,9 +111,7 @@ impl ViewStateRepository() -> Saga<'a, OrderEvent, ShipmentCommand> { +fn order_saga<'a>() -> Saga<'a, Event, ShipmentCommand> { Saga { react: Box::new(|event| match event { - OrderEvent::Created(evt) => { + Event::OrderCreated(evt) => { vec![ShipmentCommand::Create(CreateShipmentCommand { shipment_id: evt.order_id, order_id: evt.order_id, @@ -21,29 +20,41 @@ fn order_saga<'a>() -> Saga<'a, OrderEvent, ShipmentCommand> { items: evt.items.to_owned(), })] } - OrderEvent::Updated(_) => { + Event::OrderUpdated(_) => { vec![] } - OrderEvent::Cancelled(_) => { + Event::OrderCancelled(_) => { + vec![] + } + Event::ShipmentCreated(_) => { vec![] } }), } } -fn shipment_saga<'a>() -> Saga<'a, ShipmentEvent, OrderCommand> { +fn shipment_saga<'a>() -> Saga<'a, Event, OrderCommand> { Saga { react: Box::new(|event| match event { - ShipmentEvent::Created(evt) => { + 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![] + } }), } } - /// Simple action publisher that just returns the action/command. /// It is used for testing. In real life, it would publish the action/command to some external system. or to an aggregate that is able to handel the action/command. struct SimpleActionPublisher; @@ -71,9 +82,8 @@ async fn test() { let saga_manager = SagaManager::new( SimpleActionPublisher::new(), shipment_saga() - .combine(order_saga()) - .map_action(&sum_to_command2) - .map_action_result(&event_from_sum2), + .merge(order_saga()) + .map_action(&sum_to_command2), ); let result = saga_manager.handle(&order_created_event).await; assert!(result.is_ok()); diff --git a/tests/saga_test.rs b/tests/saga_test.rs index 2f08f83..a86433a 100644 --- a/tests/saga_test.rs +++ b/tests/saga_test.rs @@ -2,9 +2,9 @@ use fmodel_rust::saga::{ActionComputation, Saga}; use crate::api::{ CreateShipmentCommand, OrderCommand, OrderCreatedEvent, OrderEvent, ShipmentCommand, - ShipmentEvent, UpdateOrderCommand, + UpdateOrderCommand, }; -use crate::application::{event_from_sum, sum_to_command, Command, Event}; +use crate::application::{sum_to_command, Command, Event}; mod api; mod application; @@ -54,19 +54,6 @@ fn order_saga_2<'a>() -> Saga<'a, Event, ShipmentCommand> { } } -fn shipment_saga<'a>() -> Saga<'a, ShipmentEvent, OrderCommand> { - Saga { - react: Box::new(|event| match event { - ShipmentEvent::Created(evt) => { - vec![OrderCommand::Update(UpdateOrderCommand { - order_id: evt.order_id, - new_items: evt.items.to_owned(), - })] - } - }), - } -} - fn shipment_saga_2<'a>() -> Saga<'a, Event, OrderCommand> { Saga { react: Box::new(|event| match event { @@ -93,14 +80,8 @@ fn shipment_saga_2<'a>() -> Saga<'a, Event, OrderCommand> { #[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); @@ -126,17 +107,6 @@ fn test() { 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, - [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()], - })] - ); - let merged_commands = merged_saga.compute_new_actions(&order_created_event2); assert_eq!( merged_commands, diff --git a/tests/view_test.rs b/tests/view_test.rs index 9169b56..30ef3e3 100644 --- a/tests/view_test.rs +++ b/tests/view_test.rs @@ -1,32 +1,32 @@ -use api::OrderView2State; use fmodel_rust::view::{View, ViewStateComputation}; use crate::api::{ - OrderCancelledEvent, OrderCreatedEvent, OrderEvent, OrderUpdatedEvent, OrderViewState, - ShipmentCreatedEvent, ShipmentEvent, ShipmentViewState, + OrderCancelledEvent, OrderCreatedEvent, OrderUpdatedEvent, OrderViewState, + ShipmentCreatedEvent, ShipmentViewState, }; -use crate::application::{event_from_sum, Event}; +use crate::application::Event; mod api; mod application; -fn order_view<'a>() -> View<'a, OrderViewState, OrderEvent> { +fn order_view<'a>() -> View<'a, OrderViewState, Event> { View { evolve: Box::new(|state, event| { let mut new_state = state.clone(); match event { - OrderEvent::Created(evt) => { + Event::OrderCreated(evt) => { new_state.order_id = evt.order_id; new_state.customer_name = evt.customer_name.to_owned(); new_state.items = evt.items.to_owned(); } - OrderEvent::Updated(evt) => { + Event::OrderUpdated(evt) => { new_state.items = evt.updated_items.to_owned(); } - OrderEvent::Cancelled(_) => { + Event::OrderCancelled(_) => { new_state.is_cancelled = true; } + Event::ShipmentCreated(_) => {} } new_state }), @@ -39,17 +39,20 @@ fn order_view<'a>() -> View<'a, OrderViewState, OrderEvent> { } } -fn shipment_view<'a>() -> View<'a, ShipmentViewState, ShipmentEvent> { +fn shipment_view<'a>() -> View<'a, ShipmentViewState, Event> { View { evolve: Box::new(|state, event| { let mut new_state = state.clone(); match event { - ShipmentEvent::Created(evt) => { + Event::ShipmentCreated(evt) => { new_state.shipment_id = evt.shipment_id; new_state.order_id = evt.order_id; new_state.customer_name = evt.customer_name.to_owned(); new_state.items = evt.items.to_owned(); } + Event::OrderCreated(_) => {} + Event::OrderUpdated(_) => {} + Event::OrderCancelled(_) => {} } new_state }), @@ -62,47 +65,15 @@ fn shipment_view<'a>() -> View<'a, ShipmentViewState, ShipmentEvent> { } } -fn order_view_second<'a>() -> View<'a, OrderView2State, OrderEvent> { - View { - evolve: Box::new(|state, event| { - let mut new_state = state.clone(); - match event { - OrderEvent::Created(evt) => { - new_state.order_id = evt.order_id; - new_state.customer_name = evt.customer_name.to_owned(); - new_state.items = evt.items.to_owned(); - } - OrderEvent::Updated(evt) => { - new_state.items = evt.updated_items.to_owned(); - } - OrderEvent::Cancelled(_) => { - new_state.is_cancelled = true; - } - } - new_state - }), - initial_state: Box::new(|| OrderView2State { - order_id: 0, - customer_name: "".to_string(), - items: Vec::new(), - is_cancelled: false, - }), - } -} - #[test] fn test() { - let order_view: View = order_view(); - let order_view2: View = crate::order_view(); - let order_view3: View = crate::order_view(); - let order_view_second: View = order_view_second(); - let shipment_view: View = shipment_view(); - let combined_view = order_view2 - .combine(shipment_view) - .map_event(&event_from_sum); - let merged_view = order_view3.merge(order_view_second); + let order_view: View = order_view(); + let order_view2: View = crate::order_view(); + let shipment_view: View = shipment_view(); - let order_created_event = OrderEvent::Created(OrderCreatedEvent { + let merged_view = order_view2.merge(shipment_view); + + let order_created_event = Event::OrderCreated(OrderCreatedEvent { order_id: 1, customer_name: "John Doe".to_string(), items: vec!["Item 1".to_string(), "Item 2".to_string()], @@ -124,9 +95,9 @@ fn test() { items: vec!["Item 1".to_string(), "Item 2".to_string()], }); - let new_combined_state2 = combined_view.compute_new_state(None, &[&order_created_event2]); + let new_merged_state2 = merged_view.compute_new_state(None, &[&order_created_event2]); assert_eq!( - new_combined_state2, + new_merged_state2, ( OrderViewState { order_id: 1, @@ -143,34 +114,15 @@ fn test() { ) ); - let new_combined_state_2 = merged_view.compute_new_state(None, &[&order_created_event]); - assert_eq!( - new_combined_state_2, - ( - OrderViewState { - order_id: 1, - customer_name: "John Doe".to_string(), - items: vec!["Item 1".to_string(), "Item 2".to_string()], - is_cancelled: false, - }, - OrderView2State { - order_id: 1, - customer_name: "John Doe".to_string(), - items: vec!["Item 1".to_string(), "Item 2".to_string()], - is_cancelled: false, - } - ) - ); - let shipment_created_event2 = Event::ShipmentCreated(ShipmentCreatedEvent { shipment_id: 1, order_id: 1, customer_name: "John Doe".to_string(), items: vec!["Item 1".to_string(), "Item 2".to_string()], }); - let new_combined_state3 = combined_view.compute_new_state(None, &[&shipment_created_event2]); + let new_merged_state3 = merged_view.compute_new_state(None, &[&shipment_created_event2]); assert_eq!( - new_combined_state3, + new_merged_state3, ( OrderViewState { order_id: 0, @@ -187,7 +139,7 @@ fn test() { ) ); - let order_updated_event = OrderEvent::Updated(OrderUpdatedEvent { + let order_updated_event = Event::OrderUpdated(OrderUpdatedEvent { order_id: 1, updated_items: vec![ "Item 11".to_string(), @@ -210,7 +162,7 @@ fn test() { } ); - let order_canceled_event = OrderEvent::Cancelled(OrderCancelledEvent { order_id: 1 }); + let order_canceled_event = Event::OrderCancelled(OrderCancelledEvent { order_id: 1 }); let new_state = order_view.compute_new_state(Some(new_state), &[&order_canceled_event]); assert_eq!( new_state,