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:
@@ -74,7 +74,7 @@ pub enum DomainEvent {
|
||||
},
|
||||
FederationDeliveryRequested {
|
||||
inbox_url: String,
|
||||
activity_json: String,
|
||||
activity_json: serde_json::Value,
|
||||
signing_actor_id: uuid::Uuid,
|
||||
},
|
||||
WatchEventIngested {
|
||||
|
||||
@@ -12,32 +12,38 @@ pub trait MediaServerParser: Send + Sync {
|
||||
-> Result<Option<ParsedPlaybackEvent>, DomainError>;
|
||||
}
|
||||
|
||||
/// Write port — mutates watch events.
|
||||
#[async_trait]
|
||||
pub trait WatchEventRepository: Send + Sync {
|
||||
pub trait WatchEventCommand: Send + Sync {
|
||||
async fn save(&self, event: &WatchEvent) -> Result<(), DomainError>;
|
||||
async fn update_status(
|
||||
&self,
|
||||
id: &WatchEventId,
|
||||
status: WatchEventStatus,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn list_pending(&self, user_id: &UserId) -> Result<Vec<WatchEvent>, DomainError>;
|
||||
async fn get_by_id(&self, id: &WatchEventId) -> Result<Option<WatchEvent>, DomainError>;
|
||||
async fn get_by_ids(&self, ids: &[WatchEventId]) -> Result<Vec<WatchEvent>, DomainError>;
|
||||
async fn update_status_batch(
|
||||
&self,
|
||||
ids: &[WatchEventId],
|
||||
status: WatchEventStatus,
|
||||
) -> Result<u64, DomainError>;
|
||||
async fn delete_non_pending_older_than(
|
||||
&self,
|
||||
before: NaiveDateTime,
|
||||
) -> Result<u64, DomainError>;
|
||||
}
|
||||
|
||||
/// Read port — queries watch events. No mutations.
|
||||
#[async_trait]
|
||||
pub trait WatchEventQuery: Send + Sync {
|
||||
async fn list_pending(&self, user_id: &UserId) -> Result<Vec<WatchEvent>, DomainError>;
|
||||
async fn get_by_id(&self, id: &WatchEventId) -> Result<Option<WatchEvent>, DomainError>;
|
||||
async fn get_by_ids(&self, ids: &[WatchEventId]) -> Result<Vec<WatchEvent>, DomainError>;
|
||||
async fn find_duplicate(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
external_id: &str,
|
||||
after: NaiveDateTime,
|
||||
) -> Result<bool, DomainError>;
|
||||
async fn delete_non_pending_older_than(
|
||||
&self,
|
||||
before: NaiveDateTime,
|
||||
) -> Result<u64, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -9,8 +9,16 @@ use crate::{
|
||||
value_objects::{ExternalMetadataId, MovieId, MovieTitle, PosterUrl, ReleaseYear},
|
||||
};
|
||||
|
||||
/// Write port — mutates the movies table.
|
||||
#[async_trait]
|
||||
pub trait MovieRepository: Send + Sync {
|
||||
pub trait MovieCommand: Send + Sync {
|
||||
async fn upsert_movie(&self, movie: &Movie) -> Result<(), DomainError>;
|
||||
async fn delete_movie(&self, movie_id: &MovieId) -> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
/// Read port — queries movies. No mutations.
|
||||
#[async_trait]
|
||||
pub trait MovieQuery: Send + Sync {
|
||||
async fn get_movie_by_external_id(
|
||||
&self,
|
||||
external_metadata_id: &ExternalMetadataId,
|
||||
@@ -21,8 +29,6 @@ pub trait MovieRepository: Send + Sync {
|
||||
title: &MovieTitle,
|
||||
year: &ReleaseYear,
|
||||
) -> Result<Vec<Movie>, DomainError>;
|
||||
async fn upsert_movie(&self, movie: &Movie) -> Result<(), DomainError>;
|
||||
async fn delete_movie(&self, movie_id: &MovieId) -> Result<(), DomainError>;
|
||||
async fn existing_external_ids(
|
||||
&self,
|
||||
ids: &[ExternalMetadataId],
|
||||
|
||||
@@ -18,8 +18,9 @@ use crate::{
|
||||
},
|
||||
ports::{
|
||||
GoalRepository, ImportProfileRepository, ImportSessionRepository, MovieProfileRepository,
|
||||
MovieRepository, RefreshSessionRepository, ReviewRepository, UserFederationSettingsQuery,
|
||||
UserProfileFieldsRepository, UserRepository, UserSettingsRepository, WatchEventRepository,
|
||||
MovieCommand, MovieQuery, RefreshSessionRepository, ReviewRepository,
|
||||
UserFederationSettingsQuery, UserProfileFieldsRepository, UserRepository,
|
||||
UserSettingsRepository, WatchEventCommand, WatchEventQuery,
|
||||
WatchlistRepository, WebhookTokenRepository,
|
||||
},
|
||||
value_objects::{
|
||||
@@ -47,7 +48,23 @@ impl InMemoryMovieRepository {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MovieRepository for InMemoryMovieRepository {
|
||||
impl MovieCommand for InMemoryMovieRepository {
|
||||
async fn upsert_movie(&self, movie: &Movie) -> Result<(), DomainError> {
|
||||
self.store
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(movie.id().value(), movie.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_movie(&self, movie_id: &MovieId) -> Result<(), DomainError> {
|
||||
self.store.lock().unwrap().remove(&movie_id.value());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MovieQuery for InMemoryMovieRepository {
|
||||
async fn get_movie_by_external_id(
|
||||
&self,
|
||||
external_metadata_id: &ExternalMetadataId,
|
||||
@@ -80,19 +97,6 @@ impl MovieRepository for InMemoryMovieRepository {
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn upsert_movie(&self, movie: &Movie) -> Result<(), DomainError> {
|
||||
self.store
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(movie.id().value(), movie.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_movie(&self, movie_id: &MovieId) -> Result<(), DomainError> {
|
||||
self.store.lock().unwrap().remove(&movie_id.value());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn existing_external_ids(
|
||||
&self,
|
||||
ids: &[ExternalMetadataId],
|
||||
@@ -526,7 +530,7 @@ impl InMemoryWatchEventRepository {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WatchEventRepository for InMemoryWatchEventRepository {
|
||||
impl WatchEventCommand for InMemoryWatchEventRepository {
|
||||
async fn save(&self, event: &WatchEvent) -> Result<(), DomainError> {
|
||||
self.store.lock().unwrap().push(event.clone());
|
||||
Ok(())
|
||||
@@ -540,6 +544,27 @@ impl WatchEventRepository for InMemoryWatchEventRepository {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_status_batch(
|
||||
&self,
|
||||
ids: &[WatchEventId],
|
||||
_status: WatchEventStatus,
|
||||
) -> Result<u64, DomainError> {
|
||||
Ok(ids.len() as u64)
|
||||
}
|
||||
|
||||
async fn delete_non_pending_older_than(
|
||||
&self,
|
||||
before: NaiveDateTime,
|
||||
) -> Result<u64, DomainError> {
|
||||
let mut store = self.store.lock().unwrap();
|
||||
let before_len = store.len();
|
||||
store.retain(|e| *e.status() == WatchEventStatus::Pending || *e.created_at() >= before);
|
||||
Ok((before_len - store.len()) as u64)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WatchEventQuery for InMemoryWatchEventRepository {
|
||||
async fn list_pending(&self, user_id: &UserId) -> Result<Vec<WatchEvent>, DomainError> {
|
||||
let store = self.store.lock().unwrap();
|
||||
Ok(store
|
||||
@@ -566,14 +591,6 @@ impl WatchEventRepository for InMemoryWatchEventRepository {
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn update_status_batch(
|
||||
&self,
|
||||
ids: &[WatchEventId],
|
||||
_status: WatchEventStatus,
|
||||
) -> Result<u64, DomainError> {
|
||||
Ok(ids.len() as u64)
|
||||
}
|
||||
|
||||
async fn find_duplicate(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
@@ -588,15 +605,6 @@ impl WatchEventRepository for InMemoryWatchEventRepository {
|
||||
}))
|
||||
}
|
||||
|
||||
async fn delete_non_pending_older_than(
|
||||
&self,
|
||||
before: NaiveDateTime,
|
||||
) -> Result<u64, DomainError> {
|
||||
let mut store = self.store.lock().unwrap();
|
||||
let before_len = store.len();
|
||||
store.retain(|e| *e.status() == WatchEventStatus::Pending || *e.created_at() >= before);
|
||||
Ok((before_len - store.len()) as u64)
|
||||
}
|
||||
}
|
||||
|
||||
// ── InMemoryImportSessionRepository ─────────────────────────────────────────
|
||||
|
||||
@@ -371,44 +371,56 @@ impl crate::ports::FederatedProfileQuery for PanicFederatedProfileQuery {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicWatchEventRepository;
|
||||
pub struct PanicWatchEventCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ports::WatchEventRepository for PanicWatchEventRepository {
|
||||
impl crate::ports::WatchEventCommand for PanicWatchEventCommand {
|
||||
async fn save(&self, _: &crate::models::WatchEvent) -> Result<(), DomainError> {
|
||||
panic!("PanicWatchEventRepository called")
|
||||
panic!("PanicWatchEventCommand called")
|
||||
}
|
||||
async fn update_status(
|
||||
&self,
|
||||
_: &crate::value_objects::WatchEventId,
|
||||
_: crate::models::WatchEventStatus,
|
||||
) -> Result<(), DomainError> {
|
||||
panic!("PanicWatchEventRepository called")
|
||||
}
|
||||
async fn list_pending(
|
||||
&self,
|
||||
_: &UserId,
|
||||
) -> Result<Vec<crate::models::WatchEvent>, DomainError> {
|
||||
panic!("PanicWatchEventRepository called")
|
||||
}
|
||||
async fn get_by_id(
|
||||
&self,
|
||||
_: &crate::value_objects::WatchEventId,
|
||||
) -> Result<Option<crate::models::WatchEvent>, DomainError> {
|
||||
panic!("PanicWatchEventRepository called")
|
||||
}
|
||||
async fn get_by_ids(
|
||||
&self,
|
||||
_: &[crate::value_objects::WatchEventId],
|
||||
) -> Result<Vec<crate::models::WatchEvent>, DomainError> {
|
||||
panic!("PanicWatchEventRepository called")
|
||||
panic!("PanicWatchEventCommand called")
|
||||
}
|
||||
async fn update_status_batch(
|
||||
&self,
|
||||
_: &[crate::value_objects::WatchEventId],
|
||||
_: crate::models::WatchEventStatus,
|
||||
) -> Result<u64, DomainError> {
|
||||
panic!("PanicWatchEventRepository called")
|
||||
panic!("PanicWatchEventCommand called")
|
||||
}
|
||||
async fn delete_non_pending_older_than(
|
||||
&self,
|
||||
_: chrono::NaiveDateTime,
|
||||
) -> Result<u64, DomainError> {
|
||||
panic!("PanicWatchEventCommand called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicWatchEventQuery;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ports::WatchEventQuery for PanicWatchEventQuery {
|
||||
async fn list_pending(
|
||||
&self,
|
||||
_: &UserId,
|
||||
) -> Result<Vec<crate::models::WatchEvent>, DomainError> {
|
||||
panic!("PanicWatchEventQuery called")
|
||||
}
|
||||
async fn get_by_id(
|
||||
&self,
|
||||
_: &crate::value_objects::WatchEventId,
|
||||
) -> Result<Option<crate::models::WatchEvent>, DomainError> {
|
||||
panic!("PanicWatchEventQuery called")
|
||||
}
|
||||
async fn get_by_ids(
|
||||
&self,
|
||||
_: &[crate::value_objects::WatchEventId],
|
||||
) -> Result<Vec<crate::models::WatchEvent>, DomainError> {
|
||||
panic!("PanicWatchEventQuery called")
|
||||
}
|
||||
async fn find_duplicate(
|
||||
&self,
|
||||
@@ -416,13 +428,7 @@ impl crate::ports::WatchEventRepository for PanicWatchEventRepository {
|
||||
_: &str,
|
||||
_: chrono::NaiveDateTime,
|
||||
) -> Result<bool, DomainError> {
|
||||
panic!("PanicWatchEventRepository called")
|
||||
}
|
||||
async fn delete_non_pending_older_than(
|
||||
&self,
|
||||
_: chrono::NaiveDateTime,
|
||||
) -> Result<u64, DomainError> {
|
||||
panic!("PanicWatchEventRepository called")
|
||||
panic!("PanicWatchEventQuery called")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user