domain ports: CQRS-split traits for all bounded contexts

This commit is contained in:
2026-07-12 01:23:38 +02:00
parent 616c60e213
commit 0166e829c1
13 changed files with 693 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
//! Library persistence ports (CQRS split) and sync adapter.
use async_trait::async_trait;
use crate::errors::DomainResult;
use crate::models::{
LibraryCollection, LibraryItem, LibrarySyncLogEntry, LibrarySyncResult,
SeasonSummary, ShowSummary,
};
use crate::value_objects::{ContentType, LibrarySearchFilter};
use super::media::IMediaProvider;
/// Write-side port for library persistence.
#[async_trait]
pub trait LibraryCommand: Send + Sync {
/// Upsert a batch of library items for a given provider.
async fn upsert_items(&self, provider_id: &str, items: Vec<LibraryItem>) -> DomainResult<()>;
/// Remove all items belonging to a provider (used before full re-sync).
async fn clear_provider(&self, provider_id: &str) -> DomainResult<()>;
/// Create a sync log entry marking the start of a sync run.
/// Returns the log entry ID for later completion.
async fn log_sync_start(&self, provider_id: &str) -> DomainResult<i64>;
/// Mark a sync log entry as finished with the given result.
async fn log_sync_finish(&self, log_id: i64, result: &LibrarySyncResult) -> DomainResult<()>;
}
/// Read-side port for library persistence.
#[async_trait]
pub trait LibraryQuery: Send + Sync {
/// Search the library with the given filter. Returns (items, total_count).
async fn search(
&self,
filter: &LibrarySearchFilter,
) -> DomainResult<(Vec<LibraryItem>, u32)>;
/// Get a single library item by its composite ID.
async fn get_by_id(&self, id: &str) -> DomainResult<Option<LibraryItem>>;
/// List all collections, optionally filtered by provider.
async fn list_collections(
&self,
provider_id: Option<&str>,
) -> DomainResult<Vec<LibraryCollection>>;
/// List all unique series names, optionally filtered by provider.
async fn list_series(&self, provider_id: Option<&str>) -> DomainResult<Vec<String>>;
/// List all genres, optionally filtered by content type and provider.
async fn list_genres(
&self,
content_type: Option<&ContentType>,
provider_id: Option<&str>,
) -> DomainResult<Vec<String>>;
/// Get the latest sync log entries (one per provider).
async fn latest_sync_status(&self) -> DomainResult<Vec<LibrarySyncLogEntry>>;
/// Check whether a sync is currently running for a provider.
async fn is_sync_running(&self, provider_id: &str) -> DomainResult<bool>;
/// List TV show summaries, optionally filtered by provider, search term, and genres.
async fn list_shows(
&self,
provider_id: Option<&str>,
search_term: Option<&str>,
genres: &[String],
) -> DomainResult<Vec<ShowSummary>>;
/// List season summaries for a specific series.
async fn list_seasons(
&self,
series_name: &str,
provider_id: Option<&str>,
) -> DomainResult<Vec<SeasonSummary>>;
}
/// Port: sync one provider's items into the library.
///
/// DB writes are handled entirely inside implementations — no pool in the trait.
#[async_trait]
pub trait LibrarySyncAdapter: Send + Sync {
async fn sync_provider(
&self,
provider: &dyn IMediaProvider,
provider_id: &str,
) -> LibrarySyncResult;
}