use crate::persistence::PersistentEntity;
pub trait AggregateRoot: PersistentEntity {
type Event: Send + Sync + Clone;
fn uncommitted_events(&self) -> &[Self::Event];
fn clear_events(&mut self);
fn apply_event(&mut self, event: Self::Event);
fn version(&self) -> Option<u64> {
None
}
fn increment_version(&mut self) {
}
fn has_uncommitted_events(&self) -> bool {
!self.uncommitted_events().is_empty()
}
fn uncommitted_event_count(&self) -> usize {
self.uncommitted_events().len()
}
}
pub trait EventSourcedAggregate: AggregateRoot {
fn from_events(id: String, events: impl IntoIterator<Item = Self::Event>) -> Self
where
Self: Sized;
fn all_events(&self) -> Vec<Self::Event>;
}
pub trait InvariantAggregate: AggregateRoot {
type InvariantError: std::error::Error + Send + Sync;
fn check_invariants(&self) -> Result<(), Self::InvariantError>;
}
#[derive(Debug, Clone, Default)]
pub struct AggregateMetadata {
pub version: u64,
pub last_modified: Option<chrono::DateTime<chrono::Utc>>,
pub last_modified_by: Option<String>,
}
impl AggregateMetadata {
pub fn new() -> Self {
Self::default()
}
pub fn with_version(version: u64) -> Self {
Self {
version,
..Default::default()
}
}
pub fn increment(&mut self) {
self.version += 1;
self.last_modified = Some(chrono::Utc::now());
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
struct TestEvent {
data: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct TestAggregate {
id: String,
data: String,
events: Vec<TestEvent>,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
deleted_at: Option<DateTime<Utc>>,
}
impl PersistentEntity for TestAggregate {
fn entity_id(&self) -> String {
self.id.clone()
}
fn set_entity_id(&mut self, id: String) {
self.id = id;
}
fn created_at(&self) -> Option<DateTime<Utc>> {
Some(self.created_at)
}
fn set_created_at(&mut self, at: DateTime<Utc>) {
self.created_at = at;
}
fn updated_at(&self) -> Option<DateTime<Utc>> {
Some(self.updated_at)
}
fn set_updated_at(&mut self, at: DateTime<Utc>) {
self.updated_at = at;
}
fn deleted_at(&self) -> Option<DateTime<Utc>> {
self.deleted_at
}
fn set_deleted_at(&mut self, at: Option<DateTime<Utc>>) {
self.deleted_at = at;
}
}
impl AggregateRoot for TestAggregate {
type Event = TestEvent;
fn uncommitted_events(&self) -> &[Self::Event] {
&self.events
}
fn clear_events(&mut self) {
self.events.clear();
}
fn apply_event(&mut self, event: Self::Event) {
self.data = event.data.clone();
self.events.push(event);
}
}
#[test]
fn test_aggregate_events() {
let mut aggregate = TestAggregate {
id: "1".to_string(),
data: "initial".to_string(),
events: vec![],
created_at: Utc::now(),
updated_at: Utc::now(),
deleted_at: None,
};
assert!(!aggregate.has_uncommitted_events());
aggregate.apply_event(TestEvent {
data: "updated".to_string(),
});
assert!(aggregate.has_uncommitted_events());
assert_eq!(aggregate.uncommitted_event_count(), 1);
assert_eq!(aggregate.data, "updated");
aggregate.clear_events();
assert!(!aggregate.has_uncommitted_events());
}
#[test]
fn test_aggregate_metadata() {
let mut metadata = AggregateMetadata::new();
assert_eq!(metadata.version, 0);
metadata.increment();
assert_eq!(metadata.version, 1);
assert!(metadata.last_modified.is_some());
}
}