From 66bd138927e8747977e275dda771c99e3d761a5e Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 11 Jun 2026 22:05:38 +0200 Subject: [PATCH] refactor(person): EnrichPersonDeps + GetPersonDeps, PersonEnrichmentHandler --- .../tmdb-enrichment/src/person_handler.rs | 30 ++++++++++++++----- crates/application/src/person/deps.rs | 14 +++++++++ crates/application/src/person/enrich.rs | 12 ++++---- crates/application/src/person/get.rs | 10 +++---- crates/application/src/person/get_credits.rs | 10 +++---- crates/application/src/person/mod.rs | 1 + crates/application/src/person/tests/get.rs | 12 +++++--- .../src/person/tests/get_credits.rs | 12 +++++--- crates/presentation/src/handlers/search.rs | 14 +++++++-- crates/worker/src/main.rs | 13 ++++---- 10 files changed, 89 insertions(+), 39 deletions(-) create mode 100644 crates/application/src/person/deps.rs diff --git a/crates/adapters/tmdb-enrichment/src/person_handler.rs b/crates/adapters/tmdb-enrichment/src/person_handler.rs index 45e2bb2..0c06802 100644 --- a/crates/adapters/tmdb-enrichment/src/person_handler.rs +++ b/crates/adapters/tmdb-enrichment/src/person_handler.rs @@ -1,15 +1,31 @@ -use async_trait::async_trait; -use domain::{errors::DomainError, events::DomainEvent, ports::EventHandler}; +use std::sync::Arc; -use application::context::AppContext; +use async_trait::async_trait; +use domain::{ + errors::DomainError, + events::DomainEvent, + ports::{EventHandler, PersonCommand, PersonEnrichmentClient, PersonQuery}, +}; + +use application::person::deps::EnrichPersonDeps; pub struct PersonEnrichmentHandler { - ctx: AppContext, + deps: EnrichPersonDeps, } impl PersonEnrichmentHandler { - pub fn new(ctx: AppContext) -> Self { - Self { ctx } + pub fn new( + person_query: Arc, + person_enrichment: Option>, + person_command: Arc, + ) -> Self { + Self { + deps: EnrichPersonDeps { + person_query, + person_enrichment, + person_command, + }, + } } } @@ -24,6 +40,6 @@ impl EventHandler for PersonEnrichmentHandler { _ => return Ok(()), }; - application::person::enrich::execute(&self.ctx, person_id, &external_person_id).await + application::person::enrich::execute(&self.deps, person_id, &external_person_id).await } } diff --git a/crates/application/src/person/deps.rs b/crates/application/src/person/deps.rs new file mode 100644 index 0000000..68c3e55 --- /dev/null +++ b/crates/application/src/person/deps.rs @@ -0,0 +1,14 @@ +use std::sync::Arc; + +use domain::ports::{EventPublisher, PersonCommand, PersonEnrichmentClient, PersonQuery}; + +pub struct GetPersonDeps { + pub person_query: Arc, + pub event_publisher: Arc, +} + +pub struct EnrichPersonDeps { + pub person_query: Arc, + pub person_enrichment: Option>, + pub person_command: Arc, +} diff --git a/crates/application/src/person/enrich.rs b/crates/application/src/person/enrich.rs index d3a8bd2..8fa002d 100644 --- a/crates/application/src/person/enrich.rs +++ b/crates/application/src/person/enrich.rs @@ -1,15 +1,16 @@ -use crate::context::AppContext; use chrono::Utc; use domain::{errors::DomainError, models::PersonId}; +use super::deps::EnrichPersonDeps; + const STALENESS_DAYS: i64 = 90; pub async fn execute( - ctx: &AppContext, + deps: &EnrichPersonDeps, person_id: PersonId, external_id: &str, ) -> Result<(), DomainError> { - if let Some(person) = ctx.repos.person_query.get_by_id(&person_id).await? + if let Some(person) = deps.person_query.get_by_id(&person_id).await? && let Some(at) = person.enriched_at() && (Utc::now() - at).num_days() < STALENESS_DAYS { @@ -17,7 +18,7 @@ pub async fn execute( return Ok(()); } - let client = ctx.services.person_enrichment.as_ref().ok_or_else(|| { + let client = deps.person_enrichment.as_ref().ok_or_else(|| { DomainError::InfrastructureError("person enrichment client not configured".into()) })?; @@ -30,8 +31,7 @@ pub async fn execute( Err(e) => return Err(e), }; - ctx.repos - .person_command + deps.person_command .update_enrichment(&person_id, &data) .await?; tracing::info!(person_id = %person_id.value(), "person enriched"); diff --git a/crates/application/src/person/get.rs b/crates/application/src/person/get.rs index c8c52f1..2c95514 100644 --- a/crates/application/src/person/get.rs +++ b/crates/application/src/person/get.rs @@ -1,4 +1,3 @@ -use crate::context::AppContext; use chrono::Utc; use domain::{ errors::DomainError, @@ -6,15 +5,16 @@ use domain::{ models::{Person, PersonId}, }; +use super::deps::GetPersonDeps; + const ENRICHMENT_TTL_DAYS: i64 = 90; -pub async fn execute(ctx: &AppContext, id: PersonId) -> Result, DomainError> { - let person = ctx.repos.person_query.get_by_id(&id).await?; +pub async fn execute(deps: &GetPersonDeps, id: PersonId) -> Result, DomainError> { + let person = deps.person_query.get_by_id(&id).await?; if let Some(ref p) = person && should_enrich(p) { - let _ = ctx - .services + let _ = deps .event_publisher .publish(&DomainEvent::PersonEnrichmentRequested { person_id: id, diff --git a/crates/application/src/person/get_credits.rs b/crates/application/src/person/get_credits.rs index 8a72a0c..907a8b4 100644 --- a/crates/application/src/person/get_credits.rs +++ b/crates/application/src/person/get_credits.rs @@ -1,4 +1,3 @@ -use crate::context::AppContext; use chrono::Utc; use domain::{ errors::DomainError, @@ -6,13 +5,14 @@ use domain::{ models::{Person, PersonCredits, PersonId}, }; +use super::deps::GetPersonDeps; + const ENRICHMENT_TTL_DAYS: i64 = 90; -pub async fn execute(ctx: &AppContext, id: PersonId) -> Result { - let credits = ctx.repos.person_query.get_credits(&id).await?; +pub async fn execute(deps: &GetPersonDeps, id: PersonId) -> Result { + let credits = deps.person_query.get_credits(&id).await?; if should_enrich(&credits.person) { - let _ = ctx - .services + let _ = deps .event_publisher .publish(&DomainEvent::PersonEnrichmentRequested { person_id: id, diff --git a/crates/application/src/person/mod.rs b/crates/application/src/person/mod.rs index 246c9dc..40552e0 100644 --- a/crates/application/src/person/mod.rs +++ b/crates/application/src/person/mod.rs @@ -1,3 +1,4 @@ +pub mod deps; pub mod enrich; pub mod get; pub mod get_credits; diff --git a/crates/application/src/person/tests/get.rs b/crates/application/src/person/tests/get.rs index bf7c723..3a1684a 100644 --- a/crates/application/src/person/tests/get.rs +++ b/crates/application/src/person/tests/get.rs @@ -1,14 +1,18 @@ use domain::models::PersonId; +use domain::testing::{FakePersonQuery, NoopEventPublisher}; +use std::sync::Arc; use uuid::Uuid; -use crate::person::get; -use crate::test_helpers::TestContextBuilder; +use crate::person::{deps::GetPersonDeps, get}; #[tokio::test] async fn returns_none_for_unknown_person() { - let ctx = TestContextBuilder::new().build(); + let deps = GetPersonDeps { + person_query: Arc::new(FakePersonQuery), + event_publisher: NoopEventPublisher::new(), + }; - let result = get::execute(&ctx, PersonId::from_uuid(Uuid::new_v4())) + let result = get::execute(&deps, PersonId::from_uuid(Uuid::new_v4())) .await .unwrap(); diff --git a/crates/application/src/person/tests/get_credits.rs b/crates/application/src/person/tests/get_credits.rs index 4bddb6d..0ab6b61 100644 --- a/crates/application/src/person/tests/get_credits.rs +++ b/crates/application/src/person/tests/get_credits.rs @@ -1,14 +1,18 @@ use domain::models::PersonId; +use domain::testing::{FakePersonQuery, NoopEventPublisher}; +use std::sync::Arc; use uuid::Uuid; -use crate::person::get_credits; -use crate::test_helpers::TestContextBuilder; +use crate::person::{deps::GetPersonDeps, get_credits}; #[tokio::test] async fn returns_empty_credits() { - let ctx = TestContextBuilder::new().build(); + let deps = GetPersonDeps { + person_query: Arc::new(FakePersonQuery), + event_publisher: NoopEventPublisher::new(), + }; - let result = get_credits::execute(&ctx, PersonId::from_uuid(Uuid::new_v4())) + let result = get_credits::execute(&deps, PersonId::from_uuid(Uuid::new_v4())) .await .unwrap(); diff --git a/crates/presentation/src/handlers/search.rs b/crates/presentation/src/handlers/search.rs index fb140cf..be86f3c 100644 --- a/crates/presentation/src/handlers/search.rs +++ b/crates/presentation/src/handlers/search.rs @@ -5,7 +5,7 @@ use axum::{ }; use application::{ - person::{get as get_person, get_credits as get_person_credits}, + person::{deps::GetPersonDeps, get as get_person, get_credits as get_person_credits}, search::execute as search_uc, }; use domain::models::{PersonId, collections::PageParams}; @@ -101,7 +101,11 @@ pub async fn get_person_handler( State(state): State, Path(id): Path, ) -> impl IntoResponse { - match get_person::execute(&state.app_ctx, PersonId::from_uuid(id)).await { + let deps = GetPersonDeps { + person_query: state.app_ctx.repos.person_query.clone(), + event_publisher: state.app_ctx.services.event_publisher.clone(), + }; + match get_person::execute(&deps, PersonId::from_uuid(id)).await { Ok(Some(person)) => axum::Json(PersonDto { id: person.id().value(), external_id: person.external_id().value().to_string(), @@ -138,7 +142,11 @@ pub async fn get_person_credits_handler( State(state): State, Path(id): Path, ) -> impl IntoResponse { - match get_person_credits::execute(&state.app_ctx, PersonId::from_uuid(id)).await { + let deps = GetPersonDeps { + person_query: state.app_ctx.repos.person_query.clone(), + event_publisher: state.app_ctx.services.event_publisher.clone(), + }; + match get_person_credits::execute(&deps, PersonId::from_uuid(id)).await { Ok(credits) => axum::Json(PersonCreditsDto { person: PersonDto { id: credits.person.id().value(), diff --git a/crates/worker/src/main.rs b/crates/worker/src/main.rs index 098c7bd..f776c7e 100644 --- a/crates/worker/src/main.rs +++ b/crates/worker/src/main.rs @@ -147,11 +147,14 @@ async fn main() -> anyhow::Result<()> { Arc::clone(&ctx.repos.search_command), Arc::clone(&ctx.services.object_storage), )) as Arc; - ctx.services.person_enrichment = - Some(Arc::clone(&client) as Arc); - let person_handler = - Arc::new(tmdb_enrichment::PersonEnrichmentHandler::new(ctx.clone())) - as Arc; + let person_enrichment_arc = + Arc::clone(&client) as Arc; + ctx.services.person_enrichment = Some(Arc::clone(&person_enrichment_arc)); + let person_handler = Arc::new(tmdb_enrichment::PersonEnrichmentHandler::new( + Arc::clone(&ctx.repos.person_query), + Some(person_enrichment_arc), + Arc::clone(&ctx.repos.person_command), + )) as Arc; let job = Arc::new(application::jobs::EnrichmentStalenessJob::new(ctx.clone())) as Arc; (Some(handler), Some(person_handler), Some(job))