Skip to main content

cqrs_rust_lib/
aggregate.rs

1use crate::event::Event;
2use crate::CqrsContext;
3use crate::{MaybeSend, MaybeSync};
4use http::StatusCode;
5use serde::de::DeserializeOwned;
6use serde::Serialize;
7use std::fmt::Debug;
8#[cfg(feature = "utoipa")]
9use utoipa::ToSchema;
10
11/// Trait for generating aggregate IDs during creation.
12///
13/// The default implementation (`DefaultIdGenerator`) generates a UUID v4.
14/// Implement this trait to derive the aggregate ID from the create command,
15/// which is useful for association-as-aggregate patterns where the ID
16/// must be deterministic (e.g., composed from two entity IDs).
17pub trait AggregateIdGenerator<A: Aggregate + CommandHandler>: MaybeSend + MaybeSync {
18    fn next_id(&self, cmd: &A::CreateCommand, ctx: &CqrsContext) -> String;
19}
20
21/// Default ID generator that produces a UUID v4 via `CqrsContext::next_uuid()`.
22pub struct DefaultIdGenerator;
23
24impl<A: Aggregate + CommandHandler> AggregateIdGenerator<A> for DefaultIdGenerator {
25    fn next_id(&self, _cmd: &A::CreateCommand, ctx: &CqrsContext) -> String {
26        ctx.next_uuid()
27    }
28}
29
30cqrs_async_trait! {
31pub trait Aggregate: Default + Debug + Clone + Serialize + DeserializeOwned + MaybeSync + MaybeSend {
32    const TYPE: &'static str;
33
34    #[cfg(feature = "utoipa")]
35    type Event: Event + ToSchema;
36    #[cfg(not(feature = "utoipa"))]
37    type Event: Event;
38
39    type Error: std::error::Error + MaybeSend + MaybeSync + 'static;
40
41    fn aggregate_id(&self) -> String;
42    fn with_aggregate_id(self, id: String) -> Self;
43
44    fn apply(&mut self, event: Self::Event) -> Result<(), Self::Error>;
45
46    fn error(status: StatusCode, details: &str) -> Self::Error;
47}
48}
49
50cqrs_async_trait! {
51pub trait CommandHandler: Aggregate {
52    #[cfg(feature = "utoipa")]
53    type CreateCommand: DeserializeOwned + MaybeSync + MaybeSend + ToSchema;
54    #[cfg(not(feature = "utoipa"))]
55    type CreateCommand: DeserializeOwned + MaybeSync + MaybeSend;
56
57    #[cfg(feature = "utoipa")]
58    type UpdateCommand: DeserializeOwned + MaybeSync + MaybeSend + ToSchema;
59    #[cfg(not(feature = "utoipa"))]
60    type UpdateCommand: DeserializeOwned + MaybeSync + MaybeSend;
61
62    type Services: MaybeSend + MaybeSync;
63
64    async fn handle_create(
65        &self,
66        command: Self::CreateCommand,
67        services: &Self::Services,
68        context: &CqrsContext,
69    ) -> Result<Vec<Self::Event>, Self::Error>;
70
71    async fn handle_update(
72        &self,
73        command: Self::UpdateCommand,
74        services: &Self::Services,
75        context: &CqrsContext,
76    ) -> Result<Vec<Self::Event>, Self::Error>;
77}
78}