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:
@@ -1,12 +1,13 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::ports::{
|
||||
EventPublisher, MetadataClient, MovieProfileRepository, MovieRepository, ObjectStorage,
|
||||
PersonCommand, PersonQuery, PosterFetcherClient, SearchCommand,
|
||||
EventPublisher, MetadataClient, MovieCommand, MovieProfileRepository, MovieQuery,
|
||||
ObjectStorage, PersonCommand, PersonQuery, PosterFetcherClient, SearchCommand,
|
||||
};
|
||||
|
||||
pub struct SyncPosterDeps {
|
||||
pub movie: Arc<dyn MovieRepository>,
|
||||
pub movie_command: Arc<dyn MovieCommand>,
|
||||
pub movie_query: Arc<dyn MovieQuery>,
|
||||
pub movie_profile: Arc<dyn MovieProfileRepository>,
|
||||
pub metadata: Arc<dyn MetadataClient>,
|
||||
pub poster_fetcher: Arc<dyn PosterFetcherClient>,
|
||||
@@ -16,14 +17,14 @@ pub struct SyncPosterDeps {
|
||||
}
|
||||
|
||||
pub struct EnrichMovieDeps {
|
||||
pub movie: Arc<dyn MovieRepository>,
|
||||
pub movie_query: Arc<dyn MovieQuery>,
|
||||
pub movie_profile: Arc<dyn MovieProfileRepository>,
|
||||
pub person_command: Arc<dyn PersonCommand>,
|
||||
pub search_command: Arc<dyn SearchCommand>,
|
||||
}
|
||||
|
||||
pub struct ReindexSearchDeps {
|
||||
pub movie: Arc<dyn MovieRepository>,
|
||||
pub movie_query: Arc<dyn MovieQuery>,
|
||||
pub movie_profile: Arc<dyn MovieProfileRepository>,
|
||||
pub search_command: Arc<dyn SearchCommand>,
|
||||
pub person_command: Arc<dyn PersonCommand>,
|
||||
|
||||
@@ -5,20 +5,20 @@ use domain::{
|
||||
errors::DomainError,
|
||||
events::DomainEvent,
|
||||
models::IndexableDocument,
|
||||
ports::{EventHandler, MovieRepository, SearchCommand},
|
||||
ports::{EventHandler, MovieQuery, SearchCommand},
|
||||
};
|
||||
|
||||
/// Reacts to `MovieDiscovered` and inserts a bare search index entry immediately,
|
||||
/// so movies are findable before TMDb enrichment runs.
|
||||
/// Enrichment will later overwrite this with the full document (cast, genres, etc.).
|
||||
pub struct MovieDiscoveryIndexer {
|
||||
movie_repository: Arc<dyn MovieRepository>,
|
||||
movie_repository: Arc<dyn MovieQuery>,
|
||||
search_command: Arc<dyn SearchCommand>,
|
||||
}
|
||||
|
||||
impl MovieDiscoveryIndexer {
|
||||
pub fn new(
|
||||
movie_repository: Arc<dyn MovieRepository>,
|
||||
movie_repository: Arc<dyn MovieQuery>,
|
||||
search_command: Arc<dyn SearchCommand>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -18,7 +18,7 @@ pub async fn execute(deps: &EnrichMovieDeps, cmd: EnrichMovieCommand) -> Result<
|
||||
}
|
||||
|
||||
// 3. Fetch the movie for the search index document
|
||||
let Some(movie) = deps.movie.get_movie_by_id(&cmd.movie_id).await? else {
|
||||
let Some(movie) = deps.movie_query.get_movie_by_id(&cmd.movie_id).await? else {
|
||||
tracing::warn!(movie_id = %cmd.movie_id.value(), "enrich_movie: movie not found after profile upsert");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ use domain::{
|
||||
events::DomainEvent,
|
||||
models::MovieProfile,
|
||||
ports::{
|
||||
EventHandler, ImageFetcher, MovieEnrichmentClient, MovieProfileRepository, MovieRepository,
|
||||
EventHandler, ImageFetcher, MovieEnrichmentClient, MovieProfileRepository, MovieQuery,
|
||||
ObjectStorage, PersonCommand, SearchCommand,
|
||||
},
|
||||
};
|
||||
@@ -17,7 +17,7 @@ use crate::movies::{
|
||||
|
||||
pub struct MovieEnrichmentHandler {
|
||||
enrichment_client: Arc<dyn MovieEnrichmentClient>,
|
||||
movie_repository: Arc<dyn MovieRepository>,
|
||||
movie_repository: Arc<dyn MovieQuery>,
|
||||
profile_repo: Arc<dyn MovieProfileRepository>,
|
||||
person_command: Arc<dyn PersonCommand>,
|
||||
search_command: Arc<dyn SearchCommand>,
|
||||
@@ -28,7 +28,7 @@ pub struct MovieEnrichmentHandler {
|
||||
impl MovieEnrichmentHandler {
|
||||
pub fn new(
|
||||
enrichment_client: Arc<dyn MovieEnrichmentClient>,
|
||||
movie_repository: Arc<dyn MovieRepository>,
|
||||
movie_repository: Arc<dyn MovieQuery>,
|
||||
profile_repo: Arc<dyn MovieProfileRepository>,
|
||||
person_command: Arc<dyn PersonCommand>,
|
||||
search_command: Arc<dyn SearchCommand>,
|
||||
@@ -92,7 +92,7 @@ impl EventHandler for MovieEnrichmentHandler {
|
||||
|
||||
self.download_cast_photos(&profile).await;
|
||||
let enrich_deps = EnrichMovieDeps {
|
||||
movie: self.movie_repository.clone(),
|
||||
movie_query: self.movie_repository.clone(),
|
||||
movie_profile: self.profile_repo.clone(),
|
||||
person_command: self.person_command.clone(),
|
||||
search_command: self.search_command.clone(),
|
||||
|
||||
@@ -4,13 +4,13 @@ use domain::{
|
||||
errors::DomainError,
|
||||
models::collections::{PageParams, Paginated},
|
||||
models::{MovieFilter, MovieSummary},
|
||||
ports::MovieRepository,
|
||||
ports::MovieQuery,
|
||||
};
|
||||
|
||||
use crate::movies::queries::GetMoviesQuery;
|
||||
|
||||
pub async fn execute(
|
||||
movie: Arc<dyn MovieRepository>,
|
||||
movie: Arc<dyn MovieQuery>,
|
||||
query: GetMoviesQuery,
|
||||
) -> Result<Paginated<MovieSummary>, DomainError> {
|
||||
let page = PageParams::new(query.limit, query.offset)?;
|
||||
|
||||
@@ -2,12 +2,12 @@ use std::sync::Arc;
|
||||
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
ports::{MovieDeduplicator, MovieRepository, ObjectStorage},
|
||||
ports::{MovieDeduplicator, MovieQuery, ObjectStorage},
|
||||
value_objects::MovieId,
|
||||
};
|
||||
|
||||
pub struct MergeDuplicatesDeps {
|
||||
pub movie: Arc<dyn MovieRepository>,
|
||||
pub movie_query: Arc<dyn MovieQuery>,
|
||||
pub deduplicator: Arc<dyn MovieDeduplicator>,
|
||||
pub object_storage: Arc<dyn ObjectStorage>,
|
||||
}
|
||||
@@ -18,7 +18,7 @@ pub struct MergeReport {
|
||||
}
|
||||
|
||||
pub async fn execute(deps: &MergeDuplicatesDeps) -> Result<MergeReport, DomainError> {
|
||||
let movies = deps.movie.list_movies_with_external_id().await?;
|
||||
let movies = deps.movie_query.list_movies_with_external_id().await?;
|
||||
|
||||
let mut pairs_found = 0u64;
|
||||
let mut rows_repointed = 0u64;
|
||||
@@ -37,7 +37,7 @@ pub async fn execute(deps: &MergeDuplicatesDeps) -> Result<MergeReport, DomainEr
|
||||
pairs_found += 1;
|
||||
|
||||
// Determine which poster will be dropped after merge
|
||||
let canonical = match deps.movie.get_movie_by_id(&canonical_id).await? {
|
||||
let canonical = match deps.movie_query.get_movie_by_id(&canonical_id).await? {
|
||||
Some(existing) => existing,
|
||||
None => domain::models::Movie::from_persistence(
|
||||
canonical_id,
|
||||
|
||||
@@ -34,7 +34,7 @@ async fn reindex_movies(deps: &ReindexSearchDeps) -> Result<u64, DomainError> {
|
||||
let mut offset: u32 = 0;
|
||||
loop {
|
||||
let page = deps
|
||||
.movie
|
||||
.movie_query
|
||||
.list_movies(
|
||||
&PageParams {
|
||||
limit: BATCH_SIZE,
|
||||
|
||||
@@ -2,7 +2,7 @@ use domain::{
|
||||
errors::DomainError,
|
||||
events::DomainEvent,
|
||||
models::Movie,
|
||||
ports::{EventPublisher, MetadataClient, MovieRepository},
|
||||
ports::{EventPublisher, MetadataClient, MovieCommand, MovieQuery},
|
||||
value_objects::MovieId,
|
||||
};
|
||||
|
||||
@@ -14,20 +14,21 @@ use crate::diary::movie_resolver::{MovieResolver, MovieResolverDeps};
|
||||
/// Returns `(movie, is_new_movie)`.
|
||||
pub async fn resolve_and_persist_movie(
|
||||
input: &MovieInput,
|
||||
movie_repo: &dyn MovieRepository,
|
||||
movie_command: &dyn MovieCommand,
|
||||
movie_query: &dyn MovieQuery,
|
||||
metadata_client: &dyn MetadataClient,
|
||||
event_publisher: &dyn EventPublisher,
|
||||
) -> Result<(Movie, bool), DomainError> {
|
||||
let (movie, is_new) = if let Some(id) = input.movie_id {
|
||||
let movie_id = MovieId::from_uuid(id);
|
||||
let movie = movie_repo
|
||||
let movie = movie_query
|
||||
.get_movie_by_id(&movie_id)
|
||||
.await?
|
||||
.ok_or_else(|| DomainError::NotFound(format!("Movie {id}")))?;
|
||||
(movie, false)
|
||||
} else {
|
||||
let deps = MovieResolverDeps {
|
||||
repository: movie_repo,
|
||||
repository: movie_query,
|
||||
metadata_client,
|
||||
};
|
||||
MovieResolver::default_pipeline()
|
||||
@@ -36,7 +37,7 @@ pub async fn resolve_and_persist_movie(
|
||||
};
|
||||
|
||||
if is_new {
|
||||
movie_repo.upsert_movie(&movie).await?;
|
||||
movie_command.upsert_movie(&movie).await?;
|
||||
if let Some(ext_id) = movie.external_metadata_id() {
|
||||
let _ = event_publisher
|
||||
.publish(&DomainEvent::MovieDiscovered {
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{diary::commands::SyncPosterCommand, movies::deps::SyncPosterDeps};
|
||||
pub async fn execute(deps: &SyncPosterDeps, cmd: SyncPosterCommand) -> Result<(), DomainError> {
|
||||
let movie_id = MovieId::from_uuid(cmd.movie_id);
|
||||
|
||||
let mut movie = match deps.movie.get_movie_by_id(&movie_id).await? {
|
||||
let mut movie = match deps.movie_query.get_movie_by_id(&movie_id).await? {
|
||||
Some(m) => m,
|
||||
None => {
|
||||
tracing::warn!(
|
||||
@@ -59,7 +59,7 @@ pub async fn execute(deps: &SyncPosterDeps, cmd: SyncPosterCommand) -> Result<()
|
||||
let poster_path = PosterPath::new(stored_path)?;
|
||||
|
||||
movie.update_poster(poster_path);
|
||||
deps.movie.upsert_movie(&movie).await?;
|
||||
deps.movie_command.upsert_movie(&movie).await?;
|
||||
|
||||
// Refresh search index so the new poster_path is reflected immediately.
|
||||
// Fetch existing profile if available for a complete index document.
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use chrono::Utc;
|
||||
use domain::{
|
||||
models::{Movie, MovieProfile},
|
||||
ports::MovieRepository,
|
||||
ports::MovieCommand,
|
||||
testing::{
|
||||
FakeSearchCommand, InMemoryMovieProfileRepository, InMemoryMovieRepository,
|
||||
PanicPersonCommand,
|
||||
@@ -50,7 +50,7 @@ async fn stores_profile_and_indexes() {
|
||||
};
|
||||
|
||||
let deps = EnrichMovieDeps {
|
||||
movie: movie_repo as Arc<_>,
|
||||
movie_query: movie_repo as Arc<_>,
|
||||
movie_profile: Arc::clone(&profile_repo) as Arc<_>,
|
||||
person_command: Arc::new(PanicPersonCommand),
|
||||
search_command: Arc::new(FakeSearchCommand),
|
||||
@@ -142,7 +142,7 @@ async fn extracts_and_indexes_persons() {
|
||||
};
|
||||
|
||||
let deps = EnrichMovieDeps {
|
||||
movie: movie_repo as Arc<_>,
|
||||
movie_query: movie_repo as Arc<_>,
|
||||
movie_profile: Arc::clone(&profile_repo) as Arc<_>,
|
||||
person_command: Arc::new(NoopPersonCommand),
|
||||
search_command: Arc::new(FakeSearchCommand),
|
||||
|
||||
@@ -5,7 +5,7 @@ use uuid::Uuid;
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
models::Movie,
|
||||
ports::{MetadataClient, MovieRepository},
|
||||
ports::{MetadataClient, MovieCommand, MovieQuery},
|
||||
testing::{
|
||||
FakeSearchCommand, InMemoryMovieProfileRepository, InMemoryMovieRepository,
|
||||
NoopEventPublisher, NoopObjectStorage,
|
||||
@@ -20,7 +20,8 @@ use crate::{
|
||||
|
||||
fn default_deps() -> SyncPosterDeps {
|
||||
SyncPosterDeps {
|
||||
movie: InMemoryMovieRepository::new(),
|
||||
movie_command: InMemoryMovieRepository::new(),
|
||||
movie_query: InMemoryMovieRepository::new(),
|
||||
movie_profile: InMemoryMovieProfileRepository::new(),
|
||||
metadata: Arc::new(domain::testing::FakeMetadataClient),
|
||||
poster_fetcher: Arc::new(domain::testing::FakePosterFetcher),
|
||||
@@ -59,7 +60,8 @@ async fn fails_when_no_external_id() {
|
||||
movies.upsert_movie(&movie).await.unwrap();
|
||||
|
||||
let deps = SyncPosterDeps {
|
||||
movie: Arc::clone(&movies) as _,
|
||||
movie_command: Arc::clone(&movies) as _,
|
||||
movie_query: Arc::clone(&movies) as _,
|
||||
..default_deps()
|
||||
};
|
||||
|
||||
@@ -103,7 +105,8 @@ async fn syncs_poster_for_movie_with_external_id() {
|
||||
movies.upsert_movie(&movie).await.unwrap();
|
||||
|
||||
let deps = SyncPosterDeps {
|
||||
movie: Arc::clone(&movies) as _,
|
||||
movie_command: Arc::clone(&movies) as _,
|
||||
movie_query: Arc::clone(&movies) as _,
|
||||
metadata: Arc::new(FakeMetaWithPoster) as _,
|
||||
..default_deps()
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user