refactor: remaining MEDIUM — CQRS splits, DI Deps, profile dedup, event Value, response enum
M1: MovieRepository→MovieCommand/MovieQuery, WatchEventRepository→ WatchEventCommand/WatchEventQuery M2: goals/ and import/ use Deps structs M7: extract upload_image helper in update_profile M8: FederationDeliveryRequested activity_json String→serde_json::Value M11: UserProfileResponse uses ProfileViewData enum
This commit is contained in:
@@ -36,7 +36,7 @@ pub async fn execute(deps: &DeleteReviewDeps, cmd: DeleteReviewCommand) -> Resul
|
||||
let history = deps.diary.get_review_history(&movie_id).await?;
|
||||
if history.viewings().is_empty() {
|
||||
let poster_path = history.movie().poster_path().cloned();
|
||||
deps.movie.delete_movie(&movie_id).await?;
|
||||
deps.movie_command.delete_movie(&movie_id).await?;
|
||||
// best-effort: movie is already deleted, so publish failure is non-fatal
|
||||
if let Err(e) = deps
|
||||
.event_publisher
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::ports::{
|
||||
DiaryRepository, EventPublisher, MovieProfileRepository, MovieRepository, ReviewRepository,
|
||||
SocialQueryPort,
|
||||
DiaryRepository, EventPublisher, MovieCommand, MovieProfileRepository, MovieQuery,
|
||||
ReviewRepository, SocialQueryPort,
|
||||
};
|
||||
|
||||
use crate::config::AppConfig;
|
||||
@@ -10,7 +10,7 @@ use crate::config::AppConfig;
|
||||
pub struct DeleteReviewDeps {
|
||||
pub review: Arc<dyn ReviewRepository>,
|
||||
pub diary: Arc<dyn DiaryRepository>,
|
||||
pub movie: Arc<dyn MovieRepository>,
|
||||
pub movie_command: Arc<dyn MovieCommand>,
|
||||
pub event_publisher: Arc<dyn EventPublisher>,
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct EditReviewDeps {
|
||||
}
|
||||
|
||||
pub struct GetMovieSocialPageDeps {
|
||||
pub movie: Arc<dyn MovieRepository>,
|
||||
pub movie_query: Arc<dyn MovieQuery>,
|
||||
pub diary: Arc<dyn DiaryRepository>,
|
||||
pub movie_profile: Arc<dyn MovieProfileRepository>,
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ pub async fn execute(
|
||||
let page = PageParams::new(Some(query.limit), Some(query.offset))?;
|
||||
|
||||
let movie = deps
|
||||
.movie
|
||||
.movie_query
|
||||
.get_movie_by_id(&movie_id)
|
||||
.await?
|
||||
.ok_or_else(|| DomainError::NotFound(format!("Movie {}", query.movie_id)))?;
|
||||
|
||||
@@ -2,14 +2,14 @@ use async_trait::async_trait;
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
models::{MetadataSearchCriteria, Movie},
|
||||
ports::{MetadataClient, MovieRepository},
|
||||
ports::{MetadataClient, MovieQuery},
|
||||
value_objects::{ExternalMetadataId, MovieTitle, ReleaseYear},
|
||||
};
|
||||
|
||||
use crate::diary::commands::MovieInput;
|
||||
|
||||
pub struct MovieResolverDeps<'a> {
|
||||
pub repository: &'a dyn MovieRepository,
|
||||
pub repository: &'a dyn MovieQuery,
|
||||
pub metadata_client: &'a dyn MetadataClient,
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ use domain::{
|
||||
events::DomainEvent,
|
||||
models::Review,
|
||||
ports::{
|
||||
EventPublisher, MetadataClient, MovieRepository, ReviewRepository, WatchlistRepository,
|
||||
EventPublisher, MetadataClient, MovieCommand, MovieQuery, ReviewRepository,
|
||||
WatchlistRepository,
|
||||
},
|
||||
value_objects::{Comment, Rating, UserId},
|
||||
};
|
||||
@@ -16,7 +17,8 @@ use crate::movies::resolve::resolve_and_persist_movie;
|
||||
use crate::ports::ReviewLogger;
|
||||
|
||||
pub struct DefaultReviewLogger {
|
||||
movie_repo: Arc<dyn MovieRepository>,
|
||||
movie_command: Arc<dyn MovieCommand>,
|
||||
movie_query: Arc<dyn MovieQuery>,
|
||||
review_repo: Arc<dyn ReviewRepository>,
|
||||
watchlist_repo: Arc<dyn WatchlistRepository>,
|
||||
metadata_client: Arc<dyn MetadataClient>,
|
||||
@@ -25,14 +27,16 @@ pub struct DefaultReviewLogger {
|
||||
|
||||
impl DefaultReviewLogger {
|
||||
pub fn new(
|
||||
movie_repo: Arc<dyn MovieRepository>,
|
||||
movie_command: Arc<dyn MovieCommand>,
|
||||
movie_query: Arc<dyn MovieQuery>,
|
||||
review_repo: Arc<dyn ReviewRepository>,
|
||||
watchlist_repo: Arc<dyn WatchlistRepository>,
|
||||
metadata_client: Arc<dyn MetadataClient>,
|
||||
event_publisher: Arc<dyn EventPublisher>,
|
||||
) -> Self {
|
||||
Self {
|
||||
movie_repo,
|
||||
movie_command,
|
||||
movie_query,
|
||||
review_repo,
|
||||
watchlist_repo,
|
||||
metadata_client,
|
||||
@@ -50,7 +54,8 @@ impl ReviewLogger for DefaultReviewLogger {
|
||||
|
||||
let (movie, is_new_movie) = resolve_and_persist_movie(
|
||||
&cmd.input,
|
||||
self.movie_repo.as_ref(),
|
||||
self.movie_command.as_ref(),
|
||||
self.movie_query.as_ref(),
|
||||
self.metadata_client.as_ref(),
|
||||
self.event_publisher.as_ref(),
|
||||
)
|
||||
@@ -58,7 +63,7 @@ impl ReviewLogger for DefaultReviewLogger {
|
||||
|
||||
// Always upsert: even existing movies may have updated metadata
|
||||
if !is_new_movie {
|
||||
self.movie_repo.upsert_movie(&movie).await?;
|
||||
self.movie_command.upsert_movie(&movie).await?;
|
||||
}
|
||||
|
||||
let review = Review::new(
|
||||
|
||||
@@ -4,7 +4,7 @@ use chrono::Utc;
|
||||
|
||||
use domain::{
|
||||
models::{Movie, Review},
|
||||
ports::{MovieRepository, ReviewRepository},
|
||||
ports::{MovieCommand, MovieQuery, ReviewRepository},
|
||||
testing::{
|
||||
FakeDiaryRepository, InMemoryMovieRepository, InMemoryReviewRepository, NoopEventPublisher,
|
||||
},
|
||||
@@ -55,7 +55,7 @@ async fn test_delete_review_removes_it() {
|
||||
let deps = DeleteReviewDeps {
|
||||
review: Arc::clone(&reviews) as _,
|
||||
diary: diary.clone() as _,
|
||||
movie: Arc::clone(&movies) as _,
|
||||
movie_command: Arc::clone(&movies) as _,
|
||||
event_publisher: Arc::clone(&events) as _,
|
||||
};
|
||||
|
||||
@@ -93,7 +93,7 @@ async fn test_delete_review_wrong_user_is_unauthorized() {
|
||||
let deps = DeleteReviewDeps {
|
||||
review: Arc::clone(&reviews) as _,
|
||||
diary: diary as _,
|
||||
movie: movies as _,
|
||||
movie_command: movies as _,
|
||||
event_publisher: Arc::clone(&events) as _,
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use uuid::Uuid;
|
||||
|
||||
use domain::{
|
||||
models::Movie,
|
||||
ports::MovieRepository,
|
||||
ports::MovieCommand,
|
||||
testing::{FakeDiaryRepository, InMemoryMovieProfileRepository, InMemoryMovieRepository},
|
||||
value_objects::{MovieTitle, ReleaseYear},
|
||||
};
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
#[tokio::test]
|
||||
async fn fails_when_movie_not_found() {
|
||||
let deps = GetMovieSocialPageDeps {
|
||||
movie: InMemoryMovieRepository::new(),
|
||||
movie_query: InMemoryMovieRepository::new(),
|
||||
diary: FakeDiaryRepository::new() as _,
|
||||
movie_profile: InMemoryMovieProfileRepository::new(),
|
||||
};
|
||||
@@ -50,7 +50,7 @@ async fn returns_movie_social_page() {
|
||||
movies.upsert_movie(&movie).await.unwrap();
|
||||
|
||||
let deps = GetMovieSocialPageDeps {
|
||||
movie: Arc::clone(&movies) as _,
|
||||
movie_query: Arc::clone(&movies) as _,
|
||||
diary: FakeDiaryRepository::new() as _,
|
||||
movie_profile: InMemoryMovieProfileRepository::new(),
|
||||
};
|
||||
|
||||
@@ -4,10 +4,10 @@ use chrono::Utc;
|
||||
|
||||
use domain::{
|
||||
models::Movie,
|
||||
ports::MovieCommand,
|
||||
value_objects::{MovieTitle, ReleaseYear},
|
||||
};
|
||||
|
||||
use domain::ports::MovieRepository;
|
||||
use domain::testing::{InMemoryMovieRepository, InMemoryReviewRepository, NoopEventPublisher};
|
||||
|
||||
use crate::{
|
||||
@@ -23,6 +23,7 @@ fn build_logger(
|
||||
events: &Arc<NoopEventPublisher>,
|
||||
) -> Arc<dyn crate::ports::ReviewLogger> {
|
||||
Arc::new(DefaultReviewLogger::new(
|
||||
Arc::clone(movies) as _,
|
||||
Arc::clone(movies) as _,
|
||||
Arc::clone(reviews) as _,
|
||||
TestContextBuilder::new().watchlist_repo,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::diary::commands::MovieInput;
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
models::{MetadataSearchCriteria, Movie},
|
||||
ports::MovieRepository,
|
||||
ports::MovieQuery,
|
||||
value_objects::{ExternalMetadataId, MovieId, MovieTitle, PosterUrl, ReleaseYear},
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ struct RepoEmpty;
|
||||
struct RepoWithTitleMatch(Movie);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MovieRepository for RepoWithExternalMovie {
|
||||
impl MovieQuery for RepoWithExternalMovie {
|
||||
async fn get_movie_by_external_id(
|
||||
&self,
|
||||
_: &ExternalMetadataId,
|
||||
@@ -49,12 +49,6 @@ impl MovieRepository for RepoWithExternalMovie {
|
||||
) -> Result<Vec<Movie>, DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn existing_external_ids(
|
||||
&self,
|
||||
_: &[ExternalMetadataId],
|
||||
@@ -83,7 +77,7 @@ impl MovieRepository for RepoWithExternalMovie {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MovieRepository for RepoEmpty {
|
||||
impl MovieQuery for RepoEmpty {
|
||||
async fn get_movie_by_external_id(
|
||||
&self,
|
||||
_: &ExternalMetadataId,
|
||||
@@ -100,12 +94,6 @@ impl MovieRepository for RepoEmpty {
|
||||
) -> Result<Vec<Movie>, DomainError> {
|
||||
Ok(vec![])
|
||||
}
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn existing_external_ids(
|
||||
&self,
|
||||
_: &[ExternalMetadataId],
|
||||
@@ -134,7 +122,7 @@ impl MovieRepository for RepoEmpty {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MovieRepository for RepoWithTitleMatch {
|
||||
impl MovieQuery for RepoWithTitleMatch {
|
||||
async fn get_movie_by_external_id(
|
||||
&self,
|
||||
_: &ExternalMetadataId,
|
||||
@@ -151,12 +139,6 @@ impl MovieRepository for RepoWithTitleMatch {
|
||||
) -> Result<Vec<Movie>, DomainError> {
|
||||
Ok(vec![self.0.clone()])
|
||||
}
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn existing_external_ids(
|
||||
&self,
|
||||
_: &[ExternalMetadataId],
|
||||
|
||||
@@ -6,7 +6,7 @@ use domain::{
|
||||
errors::DomainError,
|
||||
models::WatchlistEntry,
|
||||
models::{MetadataSearchCriteria, Movie},
|
||||
ports::{MetadataClient, MovieRepository, WatchlistRepository},
|
||||
ports::{MetadataClient, MovieCommand, WatchlistRepository},
|
||||
testing::{
|
||||
FakeMetadataClient, InMemoryMovieRepository, InMemoryReviewRepository,
|
||||
InMemoryWatchlistRepository, NoopEventPublisher,
|
||||
@@ -26,6 +26,7 @@ fn make_logger(
|
||||
events: &Arc<NoopEventPublisher>,
|
||||
) -> DefaultReviewLogger {
|
||||
DefaultReviewLogger::new(
|
||||
Arc::clone(movies) as _,
|
||||
Arc::clone(movies) as _,
|
||||
Arc::clone(reviews) as _,
|
||||
Arc::clone(watchlist) as _,
|
||||
@@ -276,6 +277,7 @@ async fn publishes_movie_discovered_for_new_movie_with_external_id() {
|
||||
let events = NoopEventPublisher::new();
|
||||
|
||||
let logger = DefaultReviewLogger::new(
|
||||
Arc::clone(&movies) as _,
|
||||
Arc::clone(&movies) as _,
|
||||
Arc::clone(&reviews) as _,
|
||||
Arc::clone(&watchlist) as _,
|
||||
|
||||
Reference in New Issue
Block a user