use crate::{
aggregate::Aggregate,
domain_event::{EventContext, EventEnvelope},
persist::AggregateRepository,
value_object::Version,
};
use std::marker::PhantomData;
pub struct AggregateRoot<A, R>
where
A: Aggregate,
R: AggregateRepository<A>,
{
repo: R,
_marker: PhantomData<A>,
}
impl<A, R> AggregateRoot<A, R>
where
A: Aggregate,
R: AggregateRepository<A>,
{
pub fn new(repo: R) -> Self {
Self {
repo,
_marker: PhantomData,
}
}
pub async fn execute(
&self,
aggregate_id: &A::Id,
commands: Vec<A::Command>,
context: EventContext,
) -> Result<Vec<EventEnvelope<A>>, A::Error> {
let mut aggregate = self
.load(aggregate_id)
.await?
.unwrap_or_else(|| A::new(aggregate_id.clone(), Version::new()));
let events = commands.into_iter().try_fold(Vec::new(), |mut acc, cmd| {
let mut events = aggregate.execute(cmd)?;
for event in &events {
aggregate.apply(event);
}
acc.append(&mut events);
Ok(acc)
})?;
if events.is_empty() {
return Ok(vec![]);
}
self.repo.save(&aggregate, events, context).await
}
pub async fn load(&self, aggregate_id: &A::Id) -> Result<Option<A>, A::Error> {
self.repo.load(aggregate_id).await
}
}