use async_trait::async_trait; use crate::{ errors::DomainError, events::{DomainEvent, EventEnvelope}, models::{ api_key::ApiKey, feed::{FeedEntry, PageParams, Paginated, UserSummary}, notification::Notification, remote_actor::RemoteActor, social::{Block, Boost, Follow, FollowState, Like}, tag::Tag, thought::Thought, top_friend::TopFriend, user::User, }, value_objects::{ApiKeyId, Content, Email, NotificationId, PasswordHash, ThoughtId, UserId, Username}, }; pub struct GeneratedToken { pub token: String, pub user_id: UserId } #[async_trait] pub trait AuthService: Send + Sync { fn generate_token(&self, user_id: &UserId) -> Result; fn validate_token(&self, token: &str) -> Result; } #[async_trait] pub trait PasswordHasher: Send + Sync { async fn hash(&self, plain: &str) -> Result; async fn verify(&self, plain: &str, hash: &PasswordHash) -> Result; } #[async_trait] pub trait EventPublisher: Send + Sync { async fn publish(&self, event: &DomainEvent) -> Result<(), DomainError>; } pub trait EventConsumer: Send + Sync { fn consume(&self) -> futures::stream::BoxStream<'_, Result>; } #[async_trait] pub trait UserRepository: Send + Sync { async fn find_by_id(&self, id: &UserId) -> Result, DomainError>; async fn find_by_username(&self, username: &Username) -> Result, DomainError>; async fn find_by_email(&self, email: &Email) -> Result, DomainError>; async fn save(&self, user: &User) -> Result<(), DomainError>; async fn update_profile(&self, user_id: &UserId, display_name: Option, bio: Option, avatar_url: Option, header_url: Option, custom_css: Option) -> Result<(), DomainError>; async fn list_with_stats(&self) -> Result, DomainError>; } #[async_trait] pub trait ThoughtRepository: Send + Sync { async fn save(&self, thought: &Thought) -> Result<(), DomainError>; async fn find_by_id(&self, id: &ThoughtId) -> Result, DomainError>; async fn delete(&self, id: &ThoughtId, user_id: &UserId) -> Result<(), DomainError>; async fn update_content(&self, id: &ThoughtId, content: &Content) -> Result<(), DomainError>; async fn get_thread(&self, id: &ThoughtId) -> Result, DomainError>; async fn list_by_user(&self, user_id: &UserId, page: &PageParams) -> Result, DomainError>; } #[async_trait] pub trait LikeRepository: Send + Sync { async fn save(&self, like: &Like) -> Result<(), DomainError>; async fn delete(&self, user_id: &UserId, thought_id: &ThoughtId) -> Result<(), DomainError>; async fn find(&self, user_id: &UserId, thought_id: &ThoughtId) -> Result, DomainError>; async fn count_for_thought(&self, thought_id: &ThoughtId) -> Result; } #[async_trait] pub trait BoostRepository: Send + Sync { async fn save(&self, boost: &Boost) -> Result<(), DomainError>; async fn delete(&self, user_id: &UserId, thought_id: &ThoughtId) -> Result<(), DomainError>; async fn find(&self, user_id: &UserId, thought_id: &ThoughtId) -> Result, DomainError>; async fn count_for_thought(&self, thought_id: &ThoughtId) -> Result; } #[async_trait] pub trait FollowRepository: Send + Sync { async fn save(&self, follow: &Follow) -> Result<(), DomainError>; async fn delete(&self, follower_id: &UserId, following_id: &UserId) -> Result<(), DomainError>; async fn find(&self, follower_id: &UserId, following_id: &UserId) -> Result, DomainError>; async fn update_state(&self, follower_id: &UserId, following_id: &UserId, state: &FollowState) -> Result<(), DomainError>; async fn list_followers(&self, user_id: &UserId, page: &PageParams) -> Result, DomainError>; async fn list_following(&self, user_id: &UserId, page: &PageParams) -> Result, DomainError>; async fn get_accepted_following_ids(&self, user_id: &UserId) -> Result, DomainError>; } #[async_trait] pub trait BlockRepository: Send + Sync { async fn save(&self, block: &Block) -> Result<(), DomainError>; async fn delete(&self, blocker_id: &UserId, blocked_id: &UserId) -> Result<(), DomainError>; async fn exists(&self, blocker_id: &UserId, blocked_id: &UserId) -> Result; } #[async_trait] pub trait TagRepository: Send + Sync { async fn find_or_create(&self, name: &str) -> Result; async fn attach_to_thought(&self, thought_id: &ThoughtId, tag_id: i32) -> Result<(), DomainError>; async fn detach_from_thought(&self, thought_id: &ThoughtId) -> Result<(), DomainError>; async fn list_for_thought(&self, thought_id: &ThoughtId) -> Result, DomainError>; async fn list_thoughts_by_tag(&self, tag_name: &str, page: &PageParams) -> Result, DomainError>; } #[async_trait] pub trait ApiKeyRepository: Send + Sync { async fn save(&self, key: &ApiKey) -> Result<(), DomainError>; async fn find_by_hash(&self, key_hash: &str) -> Result, DomainError>; async fn list_for_user(&self, user_id: &UserId) -> Result, DomainError>; async fn delete(&self, id: &ApiKeyId, user_id: &UserId) -> Result<(), DomainError>; } #[async_trait] pub trait TopFriendRepository: Send + Sync { async fn set_top_friends(&self, user_id: &UserId, friends: Vec<(UserId, i16)>) -> Result<(), DomainError>; async fn list_for_user(&self, user_id: &UserId) -> Result, DomainError>; } #[async_trait] pub trait NotificationRepository: Send + Sync { async fn save(&self, n: &Notification) -> Result<(), DomainError>; async fn list_for_user(&self, user_id: &UserId, page: &PageParams) -> Result, DomainError>; async fn mark_read(&self, id: &NotificationId, user_id: &UserId) -> Result<(), DomainError>; async fn mark_all_read(&self, user_id: &UserId) -> Result<(), DomainError>; } #[async_trait] pub trait RemoteActorRepository: Send + Sync { async fn upsert(&self, actor: &RemoteActor) -> Result<(), DomainError>; async fn find_by_url(&self, url: &str) -> Result, DomainError>; } #[async_trait] pub trait FeedRepository: Send + Sync { async fn home_feed(&self, following_ids: &[UserId], page: &PageParams, viewer_id: Option<&UserId>) -> Result, DomainError>; async fn public_feed(&self, page: &PageParams, viewer_id: Option<&UserId>) -> Result, DomainError>; async fn search(&self, query: &str, page: &PageParams, viewer_id: Option<&UserId>) -> Result, DomainError>; } #[async_trait] pub trait SearchPort: Send + Sync { /// Full-text search over public thoughts, ranked by trigram similarity. async fn search_thoughts( &self, query: &str, page: &PageParams, viewer_id: Option<&UserId>, ) -> Result, DomainError>; /// Search users by username or display_name, ranked by trigram similarity. async fn search_users( &self, query: &str, page: &PageParams, ) -> Result, DomainError>; }