backbone_core/
aggregate.rs1use crate::persistence::PersistentEntity;
44
45pub trait AggregateRoot: PersistentEntity {
53 type Event: Send + Sync + Clone;
55
56 fn uncommitted_events(&self) -> &[Self::Event];
61
62 fn clear_events(&mut self);
66
67 fn apply_event(&mut self, event: Self::Event);
73
74 fn version(&self) -> Option<u64> {
78 None
79 }
80
81 fn increment_version(&mut self) {
85 }
87
88 fn has_uncommitted_events(&self) -> bool {
90 !self.uncommitted_events().is_empty()
91 }
92
93 fn uncommitted_event_count(&self) -> usize {
95 self.uncommitted_events().len()
96 }
97}
98
99pub trait EventSourcedAggregate: AggregateRoot {
101 fn from_events(id: String, events: impl IntoIterator<Item = Self::Event>) -> Self
103 where
104 Self: Sized;
105
106 fn all_events(&self) -> Vec<Self::Event>;
108}
109
110pub trait InvariantAggregate: AggregateRoot {
112 type InvariantError: std::error::Error + Send + Sync;
114
115 fn check_invariants(&self) -> Result<(), Self::InvariantError>;
119}
120
121#[derive(Debug, Clone, Default)]
123pub struct AggregateMetadata {
124 pub version: u64,
126 pub last_modified: Option<chrono::DateTime<chrono::Utc>>,
128 pub last_modified_by: Option<String>,
130}
131
132impl AggregateMetadata {
133 pub fn new() -> Self {
135 Self::default()
136 }
137
138 pub fn with_version(version: u64) -> Self {
140 Self {
141 version,
142 ..Default::default()
143 }
144 }
145
146 pub fn increment(&mut self) {
148 self.version += 1;
149 self.last_modified = Some(chrono::Utc::now());
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156 use chrono::{DateTime, Utc};
157 use serde::{Deserialize, Serialize};
158
159 #[derive(Clone, Debug, Serialize, Deserialize)]
160 struct TestEvent {
161 data: String,
162 }
163
164 #[derive(Clone, Debug, Serialize, Deserialize)]
165 struct TestAggregate {
166 id: String,
167 data: String,
168 events: Vec<TestEvent>,
169 created_at: DateTime<Utc>,
170 updated_at: DateTime<Utc>,
171 deleted_at: Option<DateTime<Utc>>,
172 }
173
174 impl PersistentEntity for TestAggregate {
175 fn entity_id(&self) -> String {
176 self.id.clone()
177 }
178
179 fn set_entity_id(&mut self, id: String) {
180 self.id = id;
181 }
182
183 fn created_at(&self) -> Option<DateTime<Utc>> {
184 Some(self.created_at)
185 }
186
187 fn set_created_at(&mut self, at: DateTime<Utc>) {
188 self.created_at = at;
189 }
190
191 fn updated_at(&self) -> Option<DateTime<Utc>> {
192 Some(self.updated_at)
193 }
194
195 fn set_updated_at(&mut self, at: DateTime<Utc>) {
196 self.updated_at = at;
197 }
198
199 fn deleted_at(&self) -> Option<DateTime<Utc>> {
200 self.deleted_at
201 }
202
203 fn set_deleted_at(&mut self, at: Option<DateTime<Utc>>) {
204 self.deleted_at = at;
205 }
206 }
207
208 impl AggregateRoot for TestAggregate {
209 type Event = TestEvent;
210
211 fn uncommitted_events(&self) -> &[Self::Event] {
212 &self.events
213 }
214
215 fn clear_events(&mut self) {
216 self.events.clear();
217 }
218
219 fn apply_event(&mut self, event: Self::Event) {
220 self.data = event.data.clone();
221 self.events.push(event);
222 }
223 }
224
225 #[test]
226 fn test_aggregate_events() {
227 let mut aggregate = TestAggregate {
228 id: "1".to_string(),
229 data: "initial".to_string(),
230 events: vec![],
231 created_at: Utc::now(),
232 updated_at: Utc::now(),
233 deleted_at: None,
234 };
235
236 assert!(!aggregate.has_uncommitted_events());
237
238 aggregate.apply_event(TestEvent {
239 data: "updated".to_string(),
240 });
241
242 assert!(aggregate.has_uncommitted_events());
243 assert_eq!(aggregate.uncommitted_event_count(), 1);
244 assert_eq!(aggregate.data, "updated");
245
246 aggregate.clear_events();
247 assert!(!aggregate.has_uncommitted_events());
248 }
249
250 #[test]
251 fn test_aggregate_metadata() {
252 let mut metadata = AggregateMetadata::new();
253 assert_eq!(metadata.version, 0);
254
255 metadata.increment();
256 assert_eq!(metadata.version, 1);
257 assert!(metadata.last_modified.is_some());
258 }
259}