idempotency_guard

Macro idempotency_guard 

Source
macro_rules! idempotency_guard {
    ($events:expr, $( $pattern:pat $(if $guard:expr)? ),+ $(,)?) => { ... };
    ($events:expr, $( $pattern:pat $(if $guard:expr)? ),+,
     => $break_pattern:pat $(if $break_guard:expr)?) => { ... };
}
Expand description

Prevent duplicate event processing by checking for idempotent operations.

Guards against replaying the same mutation in event-sourced systems. Returns AlreadyApplied early if matching events are found, allowing the caller to skip redundant operations. Use break pattern to allow re-applying past operations.

§Parameters

  • $events: Event collection to search (usually chronologically reversed)
  • $pattern: Event patterns that indicate operation already applied
  • $break_pattern: Optional break pattern to stop searching

§Examples

use es_entity::{idempotency_guard, Idempotent};
pub enum UserEvent{
    Initialized {id: u64, name: String},
    NameUpdated {name: String}
}

pub struct User{
    events: Vec<UserEvent>
}

impl User{
    pub fn update_name(&mut self, new_name: impl Into<String>) -> Idempotent<()>{
        let name = new_name.into();
        idempotency_guard!(
            self.events.iter().rev(),
            UserEvent::NameUpdated { name: existing_name } if existing_name == &name
            // above line returns early if same name found
        );
        self.events.push(UserEvent::NameUpdated{name});
        Idempotent::Executed(())
    }
     
    pub fn update_name_with_break(&mut self, new_name: impl Into<String>) -> Idempotent<()>{
        let name = new_name.into();
        idempotency_guard!(
            self.events.iter().rev(),
            UserEvent::NameUpdated { name: existing_name } if existing_name == &name,
            => UserEvent::NameUpdated {..}
            // above line breaks iteration if same event found
       );
       self.events.push(UserEvent::NameUpdated{name});
       Idempotent::Executed(())     
    }
}
   
let mut user1 = User{ events: vec![] };
let mut user2 = User{ events: vec![] };
assert!(user1.update_name("Alice").did_execute());
// updating "ALice" again ignored because same event with same name exists
assert!(user1.update_name("Alice").was_already_applied());
     
assert!(user2.update_name_with_break("Alice").did_execute());
assert!(user2.update_name_with_break("Bob").did_execute());
// updating "ALice" again works because of early break condition
assert!(user2.update_name_with_break("Alice").did_execute());