#![allow(clippy::type_complexity)]
use crate::{
AggregateRoot, DomainEventPublisher, Entity, LifecycleCompletionError, LifecycleOperationError,
Repository, cqrs::OperationState, dispatch_events,
};
pub struct LifecycleContext<'a, A, R, P>
where
A: AggregateRoot,
R: Repository<Aggregate = A>,
P: DomainEventPublisher<Event = A::Event>,
{
aggregate: &'a mut A,
repository: &'a mut R,
publisher: &'a mut P,
save_state: OperationState,
dispatch_state: OperationState,
}
impl<'a, A, R, P> LifecycleContext<'a, A, R, P>
where
A: AggregateRoot,
R: Repository<Aggregate = A>,
P: DomainEventPublisher<Event = A::Event>,
{
pub fn new(aggregate: &'a mut A, repository: &'a mut R, publisher: &'a mut P) -> Self {
Self {
aggregate,
repository,
publisher,
save_state: OperationState::Pending,
dispatch_state: OperationState::Pending,
}
}
pub fn save(&mut self) -> Result<(), LifecycleOperationError<R::Error>> {
if self.save_state != OperationState::Pending {
return Err(LifecycleOperationError::AlreadySaved);
}
self.save_state = OperationState::FailedConsumed;
match self.repository.save(self.aggregate) {
Ok(()) => {
self.save_state = OperationState::Succeeded;
Ok(())
}
Err(error) => Err(LifecycleOperationError::Adapter(error)),
}
}
pub fn dispatch(&mut self) -> Result<(), LifecycleOperationError<P::Error>> {
if self.dispatch_state != OperationState::Pending {
return Err(LifecycleOperationError::AlreadyDispatched);
}
self.dispatch_state = OperationState::FailedConsumed;
match dispatch_events(self.aggregate, self.publisher) {
Ok(()) => {
self.dispatch_state = OperationState::Succeeded;
Ok(())
}
Err(error) => Err(LifecycleOperationError::Adapter(error)),
}
}
pub fn complete(self) -> Result<(), LifecycleCompletionError> {
match (self.save_state, self.dispatch_state) {
(OperationState::Succeeded, OperationState::Succeeded) => Ok(()),
(OperationState::FailedConsumed, _) => {
Err(LifecycleCompletionError::SaveFailedConsumed)
}
(_, OperationState::FailedConsumed) => {
Err(LifecycleCompletionError::DispatchFailedConsumed)
}
(OperationState::Pending, _) => Err(LifecycleCompletionError::MissingSave),
(_, OperationState::Pending) => Err(LifecycleCompletionError::MissingDispatch),
}
}
}
pub trait LifecycleCallback<'storage, A, R, P>
where
A: AggregateRoot,
R: Repository<Aggregate = A>,
P: DomainEventPublisher<Event = A::Event>,
{
type Output;
type Error;
fn call<'callback>(
self,
context: &'callback mut LifecycleContext<'storage, A, R, P>,
) -> Result<Self::Output, Self::Error>
where
'storage: 'callback;
}
pub trait CommandHandler {
type Command;
type Aggregate: AggregateRoot;
type Repository: Repository<Aggregate = Self::Aggregate>;
type Publisher: DomainEventPublisher<Event = <Self::Aggregate as AggregateRoot>::Event>;
type Output;
type Error;
fn handle<F>(
&mut self,
id: &<Self::Aggregate as Entity>::Id,
command: Self::Command,
callback: F,
) -> Result<Self::Output, Self::Error>
where
F: for<'a> LifecycleCallback<
'a,
Self::Aggregate,
Self::Repository,
Self::Publisher,
Output = Self::Output,
Error = Self::Error,
>,
Self::Error: From<LifecycleOperationError<<Self::Repository as Repository>::Error>>
+ From<LifecycleOperationError<<Self::Publisher as DomainEventPublisher>::Error>>
+ From<LifecycleCompletionError>;
}
pub trait AggregateCreator {
type Command;
type Aggregate: AggregateRoot;
type Repository: Repository<Aggregate = Self::Aggregate>;
type Publisher: DomainEventPublisher<Event = <Self::Aggregate as AggregateRoot>::Event>;
type Output;
type Error;
fn create<F>(
&mut self,
command: Self::Command,
callback: F,
) -> Result<Self::Output, Self::Error>
where
F: for<'a> LifecycleCallback<
'a,
Self::Aggregate,
Self::Repository,
Self::Publisher,
Output = Self::Output,
Error = Self::Error,
>,
Self::Error: From<LifecycleOperationError<<Self::Repository as Repository>::Error>>
+ From<LifecycleOperationError<<Self::Publisher as DomainEventPublisher>::Error>>
+ From<LifecycleCompletionError>;
}
pub trait QueryHandler {
type Query;
type Result;
type Error;
fn handle(&self, query: Self::Query) -> Result<Self::Result, Self::Error>;
}