use std::ops::Deref; use async_trait::async_trait; use uuid::Uuid; use crate::store::StoreEvent; use crate::Aggregate; /// This trait is used to implement an [`EventHandler`]. An event handler is intended to be an entity /// which can create, update and delete a read side and perform side effects. /// /// The main purpose of an [`EventHandler`] is to have an eventually persistent processor. #[async_trait] pub trait EventHandler: Sync where A: Aggregate, { /// Handle an event and perform an action. This action could be over a read model or a side-effect. /// All the errors should be handled from within the [`EventHandler`] and shouldn't panic. async fn handle(&self, event: &StoreEvent); /// Perform a deletion of a resource using the given aggregate_id. async fn delete(&self, _aggregate_id: Uuid) {} /// The name of the event handler. By default, this is the type name of the event handler, /// but it can be overridden to provide a custom name. This name is used as /// part of tracing spans, to identify the event handler being run. fn name(&self) -> &'static str { std::any::type_name::() } } #[async_trait] impl EventHandler for T where A: Aggregate, A::Event: Send + Sync, Q: EventHandler, T: Deref + Clone + Send + Sync, { /// Deref call to [`EventHandler::handle`]. async fn handle(&self, event: &StoreEvent) { self.deref().handle(event).await; } /// Deref call to [`EventHandler::handle`]. async fn delete(&self, aggregate_id: Uuid) { self.deref().delete(aggregate_id).await; } /// Deref call to [`EventHandler::handle`]. fn name(&self) -> &'static str { self.deref().name() } } /// This trait is used to implement a [`TransactionalEventHandler`]. A transactional event handler is /// intended to be an entity which can create, update and delete a read side. No side effects must be /// performed inside of this kind on handler. /// /// An `handle` operation will result in a _deadlock_ if the implementation of this trait is used to /// apply an event on an [`Aggregate`]. #[async_trait] pub trait TransactionalEventHandler: Sync where A: Aggregate, { /// Handle an event in a transactional fashion and perform a read side crate, update or delete. /// If an error is returned the transaction will be aborted and the handling of a command by an /// aggregate will return an error. async fn handle(&self, event: &StoreEvent, executor: &mut Ex) -> Result<(), Er>; /// Perform a deletion of a read side projection using the given aggregate_id. async fn delete(&self, _aggregate_id: Uuid, _executor: &mut Ex) -> Result<(), Er> { Ok(()) } /// The name of the event handler. By default, this is the type name of the event handler, /// but it can be overridden to provide a custom name. This name is used as /// part of tracing spans, to identify the event handler being run. fn name(&self) -> &'static str { std::any::type_name::() } } #[async_trait] impl TransactionalEventHandler for T where A: Aggregate, A::Event: Send + Sync, Ex: Send, Q: TransactionalEventHandler, T: Deref + Send + Sync, { /// Deref call to [`TransactionalEventHandler::handle`]. async fn handle(&self, event: &StoreEvent, executor: &mut Ex) -> Result<(), Er> { self.deref().handle(event, executor).await } /// Deref call to [`TransactionalEventHandler::delete`]. async fn delete(&self, aggregate_id: Uuid, executor: &mut Ex) -> Result<(), Er> { self.deref().delete(aggregate_id, executor).await } /// Deref call to [`TransactionalEventHandler::name`]. fn name(&self) -> &'static str { self.deref().name() } } /// The [`ReplayableEventHandler`] trait is used to add the `replay` behavior on an [`EventHandler`]. /// /// Being replayable means that the operation performed by this EventHandler should be idempotent /// and should be intended to be "eventually consistent". /// In other words it means that they should not perform external API calls, generate random numbers /// or do anything that relies on external state and might change the outcome of this function. /// /// The most common use case for this is when rebuilding read models: [`EventHandler`]s that write on /// the database should be marked as replayable. /// /// Another use case could be if there's the need to implement a retry logic for this event handler. pub trait ReplayableEventHandler: Sync where Self: EventHandler, A: Aggregate, { }