feat: atomic review writes with events
This commit is contained in:
@@ -2,6 +2,7 @@ use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
errors::DomainError,
|
||||
events::DomainEvent,
|
||||
models::{
|
||||
DiaryEntry, DiaryFilter, ExportFormat, FeedEntry, FeedSortBy, FollowingFilter, MovieStats,
|
||||
Review, ReviewHistory, UserStats, UserTrends,
|
||||
@@ -42,10 +43,19 @@ pub trait DiaryQuery: Send + Sync {
|
||||
|
||||
#[async_trait]
|
||||
pub trait ReviewRepository: Send + Sync {
|
||||
async fn save_review(&self, review: &Review) -> Result<(), DomainError>;
|
||||
async fn save_review(&self, review: &Review, events: &[DomainEvent])
|
||||
-> Result<(), DomainError>;
|
||||
async fn get_review_by_id(&self, review_id: &ReviewId) -> Result<Option<Review>, DomainError>;
|
||||
async fn update_review(&self, review: &Review) -> Result<(), DomainError>;
|
||||
async fn delete_review(&self, review_id: &ReviewId) -> Result<(), DomainError>;
|
||||
async fn update_review(
|
||||
&self,
|
||||
review: &Review,
|
||||
events: &[DomainEvent],
|
||||
) -> Result<(), DomainError>;
|
||||
async fn delete_review(
|
||||
&self,
|
||||
review_id: &ReviewId,
|
||||
events: &[DomainEvent],
|
||||
) -> Result<(), DomainError>;
|
||||
async fn get_all_reviews_for_user(&self, user_id: &UserId) -> Result<Vec<Review>, DomainError>;
|
||||
}
|
||||
|
||||
|
||||
@@ -538,16 +538,16 @@ pub struct PanicReviewRepository;
|
||||
|
||||
#[async_trait]
|
||||
impl ReviewRepository for PanicReviewRepository {
|
||||
async fn save_review(&self, _: &Review) -> Result<(), DomainError> {
|
||||
async fn save_review(&self, _: &Review, _: &[DomainEvent]) -> Result<(), DomainError> {
|
||||
panic!("PanicReviewRepository called")
|
||||
}
|
||||
async fn get_review_by_id(&self, _: &ReviewId) -> Result<Option<Review>, DomainError> {
|
||||
panic!("PanicReviewRepository called")
|
||||
}
|
||||
async fn update_review(&self, _: &Review) -> Result<(), DomainError> {
|
||||
async fn update_review(&self, _: &Review, _: &[DomainEvent]) -> Result<(), DomainError> {
|
||||
panic!("PanicReviewRepository called")
|
||||
}
|
||||
async fn delete_review(&self, _: &ReviewId) -> Result<(), DomainError> {
|
||||
async fn delete_review(&self, _: &ReviewId, _: &[DomainEvent]) -> Result<(), DomainError> {
|
||||
panic!("PanicReviewRepository called")
|
||||
}
|
||||
async fn get_all_reviews_for_user(&self, _: &UserId) -> Result<Vec<Review>, DomainError> {
|
||||
|
||||
@@ -21,16 +21,16 @@ pub struct RefusingReviewRepository;
|
||||
|
||||
#[async_trait]
|
||||
impl ReviewRepository for RefusingReviewRepository {
|
||||
async fn save_review(&self, _: &Review) -> Result<(), DomainError> {
|
||||
async fn save_review(&self, _: &Review, _: &[DomainEvent]) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn get_review_by_id(&self, _: &ReviewId) -> Result<Option<Review>, DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn update_review(&self, _: &Review) -> Result<(), DomainError> {
|
||||
async fn update_review(&self, _: &Review, _: &[DomainEvent]) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn delete_review(&self, _: &ReviewId) -> Result<(), DomainError> {
|
||||
async fn delete_review(&self, _: &ReviewId, _: &[DomainEvent]) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn get_all_reviews_for_user(&self, _: &UserId) -> Result<Vec<Review>, DomainError> {
|
||||
|
||||
@@ -72,6 +72,7 @@ pub struct InMemoryStore {
|
||||
pub(super) follows: RwLock<Vec<(Uuid, SocialIdentity, FollowState)>>,
|
||||
pub(super) blocked: RwLock<Vec<(Uuid, SocialIdentity)>>,
|
||||
pub(super) processed_events: RwLock<HashMap<(Uuid, String), LedgerRow>>,
|
||||
pub(super) recorded_events: RwLock<Vec<crate::events::DomainEvent>>,
|
||||
}
|
||||
|
||||
impl InMemoryStore {
|
||||
@@ -94,6 +95,7 @@ impl InMemoryStore {
|
||||
follows: RwLock::default(),
|
||||
blocked: RwLock::default(),
|
||||
processed_events: RwLock::default(),
|
||||
recorded_events: RwLock::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -103,6 +105,19 @@ impl InMemoryStore {
|
||||
store
|
||||
}
|
||||
|
||||
/// Events recorded atomically with a change (the outbox rows the sqlite
|
||||
/// adapter would have written in the same transaction), in write order.
|
||||
pub fn recorded_events(&self) -> Vec<crate::events::DomainEvent> {
|
||||
self.recorded_events.read().unwrap().clone()
|
||||
}
|
||||
|
||||
pub(super) fn record_events(&self, events: &[crate::events::DomainEvent]) {
|
||||
self.recorded_events
|
||||
.write()
|
||||
.unwrap()
|
||||
.extend_from_slice(events);
|
||||
}
|
||||
|
||||
pub fn movie_count(&self) -> usize {
|
||||
self.movies.read().unwrap().len()
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
errors::DomainError,
|
||||
events::DomainEvent,
|
||||
models::Review,
|
||||
ports::ReviewRepository,
|
||||
value_objects::{ReviewId, UserId},
|
||||
@@ -11,11 +12,16 @@ use super::InMemoryStore;
|
||||
|
||||
#[async_trait]
|
||||
impl ReviewRepository for InMemoryStore {
|
||||
async fn save_review(&self, review: &Review) -> Result<(), DomainError> {
|
||||
async fn save_review(
|
||||
&self,
|
||||
review: &Review,
|
||||
events: &[DomainEvent],
|
||||
) -> Result<(), DomainError> {
|
||||
self.reviews
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(review.id().value(), review.clone());
|
||||
self.record_events(events);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -28,16 +34,26 @@ impl ReviewRepository for InMemoryStore {
|
||||
.cloned())
|
||||
}
|
||||
|
||||
async fn update_review(&self, review: &Review) -> Result<(), DomainError> {
|
||||
async fn update_review(
|
||||
&self,
|
||||
review: &Review,
|
||||
events: &[DomainEvent],
|
||||
) -> Result<(), DomainError> {
|
||||
self.reviews
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(review.id().value(), review.clone());
|
||||
self.record_events(events);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_review(&self, review_id: &ReviewId) -> Result<(), DomainError> {
|
||||
async fn delete_review(
|
||||
&self,
|
||||
review_id: &ReviewId,
|
||||
events: &[DomainEvent],
|
||||
) -> Result<(), DomainError> {
|
||||
self.reviews.write().unwrap().remove(&review_id.value());
|
||||
self.record_events(events);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user