use async_trait::async_trait; use chrono::{DateTime, Utc}; use crate::{ errors::DomainError, events::DomainEvent, models::{ DiaryEntry, DiaryFilter, FeedEntry, Movie, Review, ReviewHistory, User, UserStats, UserTrends, UserSummary, collections::{PageParams, Paginated}, }, value_objects::{ Email, ExternalMetadataId, MovieId, MovieTitle, PasswordHash, PosterPath, PosterUrl, ReleaseYear, ReviewId, UserId, }, }; #[async_trait] pub trait MovieRepository: Send + Sync { async fn get_movie_by_external_id( &self, external_metadata_id: &ExternalMetadataId, ) -> Result, DomainError>; async fn get_movie_by_id(&self, movie_id: &MovieId) -> Result, DomainError>; async fn get_movies_by_title_and_year( &self, title: &MovieTitle, year: &ReleaseYear, ) -> Result, DomainError>; async fn upsert_movie(&self, movie: &Movie) -> Result<(), DomainError>; async fn save_review(&self, review: &Review) -> Result; async fn query_diary(&self, filter: &DiaryFilter) -> Result, DomainError>; async fn get_review_history(&self, movie_id: &MovieId) -> Result; async fn get_review_by_id(&self, review_id: &ReviewId) -> Result, DomainError>; async fn delete_review(&self, review_id: &ReviewId) -> Result<(), DomainError>; async fn delete_movie(&self, movie_id: &MovieId) -> Result<(), DomainError>; async fn query_activity_feed( &self, page: &PageParams, ) -> Result, DomainError>; async fn get_user_stats(&self, user_id: &UserId) -> Result; async fn get_user_history(&self, user_id: &UserId) -> Result, DomainError>; async fn get_user_trends(&self, user_id: &UserId) -> Result; } pub enum MetadataSearchCriteria { ImdbId(ExternalMetadataId), Title { title: String, year: Option }, } #[async_trait] pub trait MetadataClient: Send + Sync { async fn fetch_movie_metadata( &self, criteria: &MetadataSearchCriteria, ) -> Result; async fn get_poster_url( &self, external_metadata_id: &ExternalMetadataId, ) -> Result, DomainError>; } #[async_trait] pub trait PosterFetcherClient: Send + Sync { async fn fetch_poster_bytes(&self, poster_url: &PosterUrl) -> Result, DomainError>; } #[async_trait] pub trait PosterStorage: Send + Sync { async fn store_poster( &self, movie_id: &MovieId, image_bytes: &[u8], ) -> Result; async fn get_poster(&self, poster_path: &PosterPath) -> Result, DomainError>; } pub struct GeneratedToken { pub token: String, pub expires_at: DateTime, } #[async_trait] pub trait AuthService: Send + Sync { async fn generate_token(&self, user_id: &UserId) -> Result; async fn validate_token(&self, token: &str) -> Result; } #[async_trait] pub trait UserRepository: Send + Sync { async fn find_by_email(&self, email: &Email) -> Result, DomainError>; async fn save(&self, user: &User) -> Result<(), DomainError>; async fn find_by_id(&self, id: &UserId) -> Result, DomainError>; async fn list_with_stats(&self) -> Result, DomainError>; } #[async_trait] pub trait EventPublisher: Send + Sync { async fn publish(&self, event: &DomainEvent) -> Result<(), DomainError>; } #[async_trait] pub trait PasswordHasher: Send + Sync { async fn hash(&self, plain_password: &str) -> Result; async fn verify(&self, plain_password: &str, hash: &PasswordHash) -> Result; }