1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
use Arc;
use crate;
use DomainEventHandler;
/// Trait for representing a **Repository**.
///
/// > Therefore, use a Repository, the purpose of which is to encapsulate all the logic needed to
/// > obtain object references. The domain objects won’t have to deal with the infrastructure to get
/// > the needed references to other objects of the domain. They will just get them from the
/// > Repository and the model is regaining its clarity and focus.
///
/// # Examples
///
/// This example uses the [InMemoryRepository](crate::infrastructure::memory::InMemoryRepository)
/// which is a sample implementation of this trait.
///
/// ```
/// use ddd_rs::{
/// application::{ReadRepository, Repository},
/// infrastructure::InMemoryRepository
/// };
///
/// // By definition, only `AggregateRoot`s have repositories.
/// //
/// // Common entities must be retrieved and persisted through their associated aggregate roots.
/// #[derive(ddd_rs::AggregateRoot, ddd_rs::Entity, Clone)]
/// struct MyEntity {
/// #[entity(id)]
/// id: u32,
/// my_field: String,
/// }
///
/// impl MyEntity {
/// pub fn new(id: u32, my_field: impl ToString) -> Self {
/// Self {
/// id,
/// my_field: my_field.to_string(),
/// }
/// }
/// }
///
/// # tokio_test::block_on(async {
/// let repository: InMemoryRepository<MyEntity> = InMemoryRepository::new();
///
/// // Add some entities to the repository.
/// repository.add(MyEntity::new(1, "foo")).await.unwrap();
/// repository.add(MyEntity::new(2, "bar")).await.unwrap();
/// repository.add(MyEntity::new(3, "baz")).await.unwrap();
///
/// // Attempt to retrieve an entity by its ID.
/// let my_entity_2 = repository.get_by_id(2).await.unwrap();
///
/// assert!(my_entity_2.is_some());
/// assert_eq!(my_entity_2.as_ref().map(|e| e.my_field.as_str()), Some("bar"));
///
/// let mut my_entity_2 = my_entity_2.unwrap();
///
/// // Update the entity, then persist its changes.
/// my_entity_2.my_field = "qux".to_string();
///
/// let my_entity_2 = repository.update(my_entity_2).await.unwrap();
///
/// assert_eq!(my_entity_2.my_field.as_str(), "qux");
///
/// // Delete the entity permanently.
/// repository.delete(my_entity_2).await.unwrap();
///
/// // Assert it no longer exists.
/// assert!(!repository.exists(2).await.unwrap());
/// # })
/// ```
/// Trait for representing a read-only **Repository**.
///
/// See the [Repository] trait for the definition of a repository and a sample of its usage.
/// Repository extension abstraction, for performing operations over aggregates that implement the
/// [AggregateRootEx] trait.
///
/// # Examples
///
/// Building upon the [Repository] sample, this example shows how a repository object can be
/// extended in order to support concepts from the [AggregateRootEx] trait.
///
/// ```
/// use std::sync::Arc;
///
/// use ddd_rs::{
/// application::{DomainEventHandler, ReadRepository, Repository, RepositoryEx},
/// infrastructure::InMemoryRepository
/// };
///
/// // The aggregate below requires an action to be performed asynchronously, but doing so directly
/// // would require the aggregate root to:
/// //
/// // - Have a reference to one or many application services, thus breaching the isolation between
/// // the Application and Domain layers;
/// // - Expect an async runtime, which is generally associated with I/O operations and
/// // long-running tasks, to be available for the implementation of business rules.
/// //
/// // These can be seem as contrary to the modeling principles of DDD, since the domain model
/// // should be self-sufficient when enforcing its own business rules.
/// //
/// // Instead, the aggregate will register a domain event requesting the async action to be
/// // performed prior to being persisted to the repository.
/// #[derive(Clone, Debug, PartialEq)]
/// enum MyDomainEvent {
/// AsyncActionRequested { action: String },
/// }
///
/// #[derive(ddd_rs::AggregateRoot, ddd_rs::Entity, Clone)]
/// struct MyEntity {
/// #[entity(id)]
/// id: u32,
/// last_performed_action: Option<String>,
/// #[aggregate_root(domain_events)]
/// domain_events: Vec<MyDomainEvent>,
/// }
///
/// impl MyEntity {
/// pub fn new(id: u32) -> Self {
/// Self {
/// id,
/// last_performed_action: None,
/// domain_events: Default::default(),
/// }
/// }
///
/// pub fn request_async_action(&mut self, action: impl ToString) {
/// let domain_event = MyDomainEvent::AsyncActionRequested { action: action.to_string() };
///
/// self.register_domain_event(domain_event);
/// }
///
/// pub fn confirm_async_action_performed(&mut self, action: impl ToString) {
/// self.last_performed_action.replace(action.to_string());
/// }
/// }
///
/// // The domain event handler will usually be a context that holds references to all necessary
/// // services and providers to handle domain events.
/// struct MyDomainEventHandler {
/// repository: Arc<dyn Repository<MyEntity>>,
/// }
///
/// impl MyDomainEventHandler {
/// pub fn new(repository: Arc<dyn Repository<MyEntity>>) -> Self {
/// Self { repository }
/// }
/// }
///
/// #[async_trait::async_trait]
/// impl DomainEventHandler<MyEntity> for MyDomainEventHandler {
/// async fn handle(&self, mut entity: MyEntity, event: MyDomainEvent) -> ddd_rs::Result<MyEntity> {
/// let action = match event {
/// MyDomainEvent::AsyncActionRequested { action, .. } => action,
/// };
///
/// // Perform the async action...
///
/// entity.confirm_async_action_performed(action);
///
/// self.repository.update(entity).await
/// }
/// }
///
/// # tokio_test::block_on(async {
///
/// // Extend the basic repository to enable processing of domain events registered by the
/// // aggregate, upon persistence.
/// let repository = Arc::new(InMemoryRepository::new());
/// let domain_event_handler = Arc::new(MyDomainEventHandler::new(repository.clone()));
///
/// let repository_ex = RepositoryEx::new(domain_event_handler, repository);
///
/// // Create a new entity and request an async action.
/// let mut entity = MyEntity::new(42);
///
/// entity.request_async_action("foo");
///
/// // Assert that the action was not performed yet, but registered a domain event.
/// assert!(entity.last_performed_action.is_none());
/// assert_eq!(entity.domain_events.len(), 1);
///
/// // Persist the entity and assert that the action was performed as a result.
/// repository_ex.add(entity).await.unwrap();
///
/// let entity = repository_ex.get_by_id(42).await.unwrap().unwrap();
///
/// assert_eq!(entity.last_performed_action.unwrap(), "foo");
/// assert!(entity.domain_events.is_empty());
/// # })
/// ```