pub struct RepositoryEx<T: AggregateRootEx> { /* private fields */ }
Expand description
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
}
}
// 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());
Implementations§
Source§impl<T: AggregateRootEx> RepositoryEx<T>
impl<T: AggregateRootEx> RepositoryEx<T>
Sourcepub fn new(
domain_event_handler: Arc<dyn DomainEventHandler<T>>,
repository: Arc<dyn Repository<T>>,
) -> Self
pub fn new( domain_event_handler: Arc<dyn DomainEventHandler<T>>, repository: Arc<dyn Repository<T>>, ) -> Self
Creates a new instance of the extended repository.
Trait Implementations§
Source§impl<T: AggregateRootEx> ReadRepository<T> for RepositoryEx<T>
impl<T: AggregateRootEx> ReadRepository<T> for RepositoryEx<T>
Source§fn get_by_id<'life0, 'async_trait>(
&'life0 self,
id: <T as Entity>::Id,
) -> Pin<Box<dyn Future<Output = Result<Option<T>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_by_id<'life0, 'async_trait>(
&'life0 self,
id: <T as Entity>::Id,
) -> Pin<Box<dyn Future<Output = Result<Option<T>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Gets an entity with the given ID.
Source§fn list<'life0, 'async_trait>(
&'life0 self,
skip: usize,
take: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<T>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn list<'life0, 'async_trait>(
&'life0 self,
skip: usize,
take: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<T>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Lists all entities within a given page.
Source§fn count<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn count<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Returns the total number of entities in the repository.
Source§impl<T: AggregateRootEx> Repository<T> for RepositoryEx<T>
impl<T: AggregateRootEx> Repository<T> for RepositoryEx<T>
Source§fn add<'life0, 'async_trait>(
&'life0 self,
entity: T,
) -> Pin<Box<dyn Future<Output = Result<T>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn add<'life0, 'async_trait>(
&'life0 self,
entity: T,
) -> Pin<Box<dyn Future<Output = Result<T>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Adds an entity to the repository.
Source§fn update<'life0, 'async_trait>(
&'life0 self,
entity: T,
) -> Pin<Box<dyn Future<Output = Result<T>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn update<'life0, 'async_trait>(
&'life0 self,
entity: T,
) -> Pin<Box<dyn Future<Output = Result<T>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Updates an entity on the repository.
Source§fn delete<'life0, 'async_trait>(
&'life0 self,
entity: T,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn delete<'life0, 'async_trait>(
&'life0 self,
entity: T,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Deletes the entity from the repository.
Source§fn add_range<'life0, 'async_trait>(
&'life0 self,
entities: Vec<T>,
) -> Pin<Box<dyn Future<Output = Result<Vec<T>>> + Send + 'async_trait>>where
Self: Sync + 'async_trait,
'life0: 'async_trait,
fn add_range<'life0, 'async_trait>(
&'life0 self,
entities: Vec<T>,
) -> Pin<Box<dyn Future<Output = Result<Vec<T>>> + Send + 'async_trait>>where
Self: Sync + 'async_trait,
'life0: 'async_trait,
Adds the given entities to the repository.
Auto Trait Implementations§
impl<T> Freeze for RepositoryEx<T>
impl<T> !RefUnwindSafe for RepositoryEx<T>
impl<T> Send for RepositoryEx<T>
impl<T> Sync for RepositoryEx<T>
impl<T> Unpin for RepositoryEx<T>
impl<T> !UnwindSafe for RepositoryEx<T>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more