use std::sync::Arc; use async_trait::async_trait; use domain::{ errors::DomainError, events::DomainEvent, models::EntityType, ports::{EventHandler, PersonQuery, SearchCommand}, }; pub struct SearchCleanupHandler { search_command: Arc, person_query: Arc, } impl SearchCleanupHandler { pub fn new(search_command: Arc, person_query: Arc) -> Self { Self { search_command, person_query } } } #[async_trait] impl EventHandler for SearchCleanupHandler { async fn handle(&self, event: &DomainEvent) -> Result<(), DomainError> { let movie_id = match event { DomainEvent::MovieDeleted { movie_id, .. } => movie_id.value().to_string(), _ => return Ok(()), }; if let Err(e) = self.search_command.remove(EntityType::Movie, &movie_id).await { tracing::warn!("search cleanup failed for movie {movie_id}: {e}"); } // Remove persons who have no remaining movie credits (orphaned after cascade delete). match self.person_query.list_orphaned_persons().await { Ok(orphans) => { for person_id in orphans { let id = person_id.value().to_string(); if let Err(e) = self.search_command.remove(EntityType::Person, &id).await { tracing::warn!("search cleanup failed for orphaned person {id}: {e}"); } } } Err(e) => tracing::warn!("failed to list orphaned persons after movie {movie_id} deletion: {e}"), } Ok(()) } }