feat: atomic review writes with events

This commit is contained in:
2026-10-26 00:40:43 +01:00
parent 0236a488fa
commit c8a1e8bfe4
19 changed files with 440 additions and 117 deletions

View File

@@ -20,18 +20,15 @@ pub async fn execute(deps: &DeleteReviewDeps, cmd: DeleteReviewCommand) -> Resul
}
let movie_id = review.movie_id().clone();
deps.review.delete_review(&review_id).await?;
if let Err(e) = deps
.event_publisher
.publish(&DomainEvent::ReviewDeleted {
review_id: review_id.clone(),
user_id: requesting_user_id.clone(),
})
.await
{
tracing::warn!("failed to publish ReviewDeleted: {e}");
}
deps.review
.delete_review(
&review_id,
&[DomainEvent::ReviewDeleted {
review_id: review_id.clone(),
user_id: requesting_user_id.clone(),
}],
)
.await?;
let history = deps.diary.get_review_history(&movie_id).await?;
if history.viewings().is_empty() {

View File

@@ -16,7 +16,6 @@ pub struct DeleteReviewDeps {
pub struct EditReviewDeps {
pub review: Arc<dyn ReviewRepository>,
pub event_publisher: Arc<dyn EventPublisher>,
}
pub struct GetMovieSocialPageDeps {

View File

@@ -37,21 +37,12 @@ pub async fn execute(deps: &EditReviewDeps, cmd: EditReviewCommand) -> Result<()
spoilers: cmd.spoilers,
});
deps.review.update_review(&updated).await?;
if let Err(e) = deps
.event_publisher
.publish(&DomainEvent::ReviewUpdated {
review_id: updated.id().clone(),
movie_id: updated.movie_id().clone(),
user_id: updated.user_id().clone(),
rating: updated.rating().clone(),
watched_at: *updated.watched_at(),
})
.await
{
tracing::warn!("failed to publish ReviewUpdated: {e}");
}
Ok(())
let event = DomainEvent::ReviewUpdated {
review_id: updated.id().clone(),
movie_id: updated.movie_id().clone(),
user_id: updated.user_id().clone(),
rating: updated.rating().clone(),
watched_at: *updated.watched_at(),
};
deps.review.update_review(&updated, &[event]).await
}

View File

@@ -75,7 +75,6 @@ impl ReviewLogger for DefaultReviewLogger {
cmd.watch_medium,
cmd.spoilers,
)?;
self.review_repo.save_review(&review).await?;
let review_event = DomainEvent::ReviewLogged {
review_id: review.id().clone(),
movie_id: review.movie_id().clone(),
@@ -84,6 +83,17 @@ impl ReviewLogger for DefaultReviewLogger {
watched_at: *review.watched_at(),
};
// Events ride the review save (the decisive write) so they commit with it.
let mut events = Vec::with_capacity(2);
if let Some(ext_id) = movie.external_metadata_id() {
events.push(DomainEvent::MovieEnrichmentRequested {
movie_id: movie.id().clone(),
external_metadata_id: ext_id.clone(),
});
}
events.push(review_event);
self.review_repo.save_review(&review, &events).await?;
let was_on_watchlist = self
.watchlist_repo
.remove_if_present(review.user_id(), review.movie_id())
@@ -98,15 +108,6 @@ impl ReviewLogger for DefaultReviewLogger {
.await;
}
if let Some(ext_id) = movie.external_metadata_id() {
self.event_publisher
.publish(&DomainEvent::MovieEnrichmentRequested {
movie_id: movie.id().clone(),
external_metadata_id: ext_id.clone(),
})
.await?;
}
self.event_publisher.publish(&review_event).await
Ok(())
}
}