domain ports: CQRS-split traits for all bounded contexts
This commit is contained in:
76
crates/domain/src/ports/schedule.rs
Normal file
76
crates/domain/src/ports/schedule.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
//! Schedule persistence ports (CQRS split).
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::errors::DomainResult;
|
||||
use crate::models::{GeneratedSchedule, PlaybackRecord};
|
||||
use crate::value_objects::{BlockId, ChannelId, MediaItemId};
|
||||
|
||||
/// Write-side port for schedule and playback persistence.
|
||||
#[async_trait]
|
||||
pub trait ScheduleCommand: Send + Sync {
|
||||
/// Insert or replace a generated schedule.
|
||||
async fn save(&self, schedule: &GeneratedSchedule) -> DomainResult<()>;
|
||||
|
||||
/// Persist a playback record (item was aired on a channel).
|
||||
async fn save_playback_record(&self, record: &PlaybackRecord) -> DomainResult<()>;
|
||||
|
||||
/// Delete all schedules with generation > `target_generation` for this channel.
|
||||
///
|
||||
/// Also deletes matching playback_records (no DB cascade between those tables).
|
||||
/// `scheduled_slots` cascade via FK from `generated_schedules`.
|
||||
async fn delete_schedules_after(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
target_generation: u32,
|
||||
) -> DomainResult<()>;
|
||||
}
|
||||
|
||||
/// Read-side port for schedule and playback persistence.
|
||||
#[async_trait]
|
||||
pub trait ScheduleQuery: Send + Sync {
|
||||
/// Find the schedule whose `[valid_from, valid_until)` window contains `at`.
|
||||
async fn find_active(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
at: DateTime<Utc>,
|
||||
) -> DomainResult<Option<GeneratedSchedule>>;
|
||||
|
||||
/// Find the most recently generated schedule for a channel.
|
||||
/// Used to derive the next generation number.
|
||||
async fn find_latest(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
) -> DomainResult<Option<GeneratedSchedule>>;
|
||||
|
||||
/// All playback records for a channel, used by the recycle policy engine.
|
||||
async fn find_playback_history(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
) -> DomainResult<Vec<PlaybackRecord>>;
|
||||
|
||||
/// Return the most recent slot per block_id across ALL schedules for a channel.
|
||||
///
|
||||
/// Resilient to any single generation having empty slots for a block.
|
||||
async fn find_last_slot_per_block(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
) -> DomainResult<HashMap<BlockId, MediaItemId>>;
|
||||
|
||||
/// List all generated schedule headers for a channel, newest first.
|
||||
async fn list_schedule_history(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
) -> DomainResult<Vec<GeneratedSchedule>>;
|
||||
|
||||
/// Fetch a specific schedule with its slots, verifying channel ownership.
|
||||
async fn get_schedule_by_id(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
schedule_id: Uuid,
|
||||
) -> DomainResult<Option<GeneratedSchedule>>;
|
||||
}
|
||||
Reference in New Issue
Block a user