#![allow(clippy::type_complexity)]
use crate::cqrs::OperationState;
use crate::{
AggregateRoot, AsyncDomainEventPublisher, AsyncRepository, Entity, LifecycleCompletionError,
LifecycleOperationError, dispatch_events_async,
};
use std::future::Future;
use std::pin::Pin;
struct AttemptGuard<'a> {
state: &'a mut OperationState,
succeeded: bool,
}
impl<'a> AttemptGuard<'a> {
fn new(state: &'a mut OperationState) -> Self {
Self {
state,
succeeded: false,
}
}
fn succeed(&mut self) {
*self.state = OperationState::Succeeded;
self.succeeded = true;
}
}
impl Drop for AttemptGuard<'_> {
fn drop(&mut self) {
if !self.succeeded {
*self.state = OperationState::FailedConsumed;
}
}
}
pub struct AsyncLifecycleContext<'storage, A, R, P>
where
A: AggregateRoot + Send + 'storage,
R: AsyncRepository<Aggregate = A> + Send + 'storage,
P: AsyncDomainEventPublisher<Event = A::Event> + Send + 'storage,
A::Event: Send + Sync + 'storage,
R::Error: Send + 'storage,
P::Error: Send + 'storage,
{
aggregate: &'storage mut A,
repository: &'storage mut R,
publisher: &'storage mut P,
save_state: OperationState,
dispatch_state: OperationState,
}
impl<'storage, A, R, P> AsyncLifecycleContext<'storage, A, R, P>
where
A: AggregateRoot + Send + 'storage,
R: AsyncRepository<Aggregate = A> + Send + 'storage,
P: AsyncDomainEventPublisher<Event = A::Event> + Send + 'storage,
A::Event: Send + Sync + 'storage,
R::Error: Send + 'storage,
P::Error: Send + 'storage,
{
pub fn new(
aggregate: &'storage mut A,
repository: &'storage mut R,
publisher: &'storage mut P,
) -> Self {
Self {
aggregate,
repository,
publisher,
save_state: OperationState::Pending,
dispatch_state: OperationState::Pending,
}
}
pub fn save<'ctx>(
&'ctx mut self,
) -> Pin<Box<dyn Future<Output = Result<(), LifecycleOperationError<R::Error>>> + Send + 'ctx>>
where
'storage: 'ctx,
A: Send + 'ctx,
R: Send + 'ctx,
R::Error: Send + 'ctx,
{
if self.save_state != OperationState::Pending {
return Box::pin(async { Err(LifecycleOperationError::AlreadySaved) });
}
self.save_state = OperationState::FailedConsumed;
let state = &mut self.save_state;
let repository: &'ctx mut R = &mut *self.repository;
let aggregate: &'ctx mut A = &mut *self.aggregate;
Box::pin(async move {
let mut guard = AttemptGuard::new(state);
match repository.save(&*aggregate).await {
Ok(()) => {
guard.succeed();
Ok(())
}
Err(error) => Err(LifecycleOperationError::Adapter(error)),
}
})
}
pub fn dispatch<'ctx>(
&'ctx mut self,
) -> Pin<Box<dyn Future<Output = Result<(), LifecycleOperationError<P::Error>>> + Send + 'ctx>>
where
'storage: 'ctx,
A: Send + 'ctx,
P: Send + 'ctx,
A::Event: Send + Sync + 'ctx,
P::Error: Send + 'ctx,
P::Error: 'ctx,
{
if self.dispatch_state != OperationState::Pending {
return Box::pin(async { Err(LifecycleOperationError::AlreadyDispatched) });
}
self.dispatch_state = OperationState::FailedConsumed;
let state = &mut self.dispatch_state;
let aggregate: &'ctx mut A = &mut *self.aggregate;
let publisher: &'ctx mut P = &mut *self.publisher;
Box::pin(async move {
let mut guard = AttemptGuard::new(state);
match dispatch_events_async(aggregate, publisher).await {
Ok(()) => {
guard.succeed();
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 AsyncLifecycleCallback<A, R, P>
where
A: AggregateRoot + Send,
R: AsyncRepository<Aggregate = A> + Send,
P: AsyncDomainEventPublisher<Event = A::Event> + Send,
A::Event: Send + Sync,
R::Error: Send,
P::Error: Send,
{
type Output;
type Error;
fn call<'storage, 'callback>(
self,
context: &'callback mut AsyncLifecycleContext<'storage, A, R, P>,
) -> Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send + 'callback>>
where
'storage: 'callback,
A: 'storage,
R: 'storage,
P: 'storage,
A::Event: 'storage,
R::Error: 'storage,
P::Error: 'storage,
Self: Send + 'callback,
Self::Output: Send + 'callback,
Self::Error: Send + 'callback;
}
pub trait AsyncCommandHandler {
type Command;
type Aggregate: AggregateRoot;
type Repository: AsyncRepository<Aggregate = Self::Aggregate>;
type Publisher: AsyncDomainEventPublisher<Event = <Self::Aggregate as AggregateRoot>::Event>;
type Output;
type Error;
fn handle<'a, F>(
&'a mut self,
id: &'a <Self::Aggregate as Entity>::Id,
command: Self::Command,
callback: F,
) -> Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send + 'a>>
where
Self: Send + 'a,
Self::Command: Send + 'a,
Self::Aggregate: Send + 'a,
<Self::Aggregate as Entity>::Id: Sync + 'a,
<Self::Aggregate as AggregateRoot>::Event: Send + Sync + 'a,
Self::Repository: Send + 'a,
<Self::Repository as AsyncRepository>::Error: Send + 'a,
Self::Publisher: Send + 'a,
<Self::Publisher as AsyncDomainEventPublisher>::Error: Send + 'a,
Self::Output: Send + 'a,
Self::Error: From<LifecycleOperationError<<Self::Repository as AsyncRepository>::Error>>
+ From<LifecycleOperationError<<Self::Publisher as AsyncDomainEventPublisher>::Error>>
+ From<LifecycleCompletionError>
+ Send
+ 'a,
F: AsyncLifecycleCallback<
Self::Aggregate,
Self::Repository,
Self::Publisher,
Output = Self::Output,
Error = Self::Error,
> + Send
+ 'a;
}
pub trait AsyncAggregateCreator {
type Command;
type Aggregate: AggregateRoot;
type Repository: AsyncRepository<Aggregate = Self::Aggregate>;
type Publisher: AsyncDomainEventPublisher<Event = <Self::Aggregate as AggregateRoot>::Event>;
type Output;
type Error;
fn create<'a, F>(
&'a mut self,
command: Self::Command,
callback: F,
) -> Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send + 'a>>
where
Self: Send + 'a,
Self::Command: Send + 'a,
Self::Aggregate: Send + 'a,
<Self::Aggregate as AggregateRoot>::Event: Send + Sync + 'a,
Self::Repository: Send + 'a,
<Self::Repository as AsyncRepository>::Error: Send + 'a,
Self::Publisher: Send + 'a,
<Self::Publisher as AsyncDomainEventPublisher>::Error: Send + 'a,
Self::Output: Send + 'a,
Self::Error: From<LifecycleOperationError<<Self::Repository as AsyncRepository>::Error>>
+ From<LifecycleOperationError<<Self::Publisher as AsyncDomainEventPublisher>::Error>>
+ From<LifecycleCompletionError>
+ Send
+ 'a,
F: AsyncLifecycleCallback<
Self::Aggregate,
Self::Repository,
Self::Publisher,
Output = Self::Output,
Error = Self::Error,
> + Send
+ 'a;
}
pub trait AsyncQueryHandler {
type Query;
type Result;
type Error;
fn handle<'a>(
&'a self,
query: Self::Query,
) -> Pin<Box<dyn Future<Output = Result<Self::Result, Self::Error>> + Send + 'a>>
where
Self: Sync + 'a,
Self::Query: Send + 'a,
Self::Result: Send + 'a,
Self::Error: Send + 'a;
}