domain events: DomainEvent enum w/ ID-only variants
This commit is contained in:
123
crates/domain/src/events/mod.rs
Normal file
123
crates/domain/src/events/mod.rs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
//! Domain events emitted when important state transitions occur.
|
||||||
|
//!
|
||||||
|
//! Events carry only IDs — consumers fetch the data they need from
|
||||||
|
//! repositories. This keeps `Clone` cheap and avoids coupling events
|
||||||
|
//! to model internals.
|
||||||
|
|
||||||
|
use crate::value_objects::{ChannelId, ScheduleId, SlotId};
|
||||||
|
|
||||||
|
/// Events emitted by domain aggregates on state transitions.
|
||||||
|
///
|
||||||
|
/// Must be `Clone + Send + 'static` so publishers can fan out cheaply.
|
||||||
|
/// Marked `#[non_exhaustive]` — downstream consumers must handle unknown
|
||||||
|
/// variants gracefully (wildcard arm).
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum DomainEvent {
|
||||||
|
/// The active broadcast switched to a new slot.
|
||||||
|
BroadcastTransition {
|
||||||
|
channel_id: ChannelId,
|
||||||
|
slot_id: SlotId,
|
||||||
|
},
|
||||||
|
/// No content is currently airing on the channel.
|
||||||
|
NoSignal { channel_id: ChannelId },
|
||||||
|
/// A new schedule was generated for a channel.
|
||||||
|
ScheduleGenerated {
|
||||||
|
channel_id: ChannelId,
|
||||||
|
schedule_id: ScheduleId,
|
||||||
|
},
|
||||||
|
/// A channel was created.
|
||||||
|
ChannelCreated { channel_id: ChannelId },
|
||||||
|
/// A channel configuration was updated.
|
||||||
|
ChannelUpdated { channel_id: ChannelId },
|
||||||
|
/// A channel was deleted.
|
||||||
|
ChannelDeleted { channel_id: ChannelId },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn broadcast_transition_carries_ids() {
|
||||||
|
let ch = ChannelId::from(Uuid::new_v4());
|
||||||
|
let sl = SlotId::from(Uuid::new_v4());
|
||||||
|
let event = DomainEvent::BroadcastTransition {
|
||||||
|
channel_id: ch,
|
||||||
|
slot_id: sl,
|
||||||
|
};
|
||||||
|
match event {
|
||||||
|
DomainEvent::BroadcastTransition {
|
||||||
|
channel_id,
|
||||||
|
slot_id,
|
||||||
|
} => {
|
||||||
|
assert_eq!(channel_id, ch);
|
||||||
|
assert_eq!(slot_id, sl);
|
||||||
|
}
|
||||||
|
_ => panic!("wrong variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_signal_carries_channel_id() {
|
||||||
|
let ch = ChannelId::from(Uuid::new_v4());
|
||||||
|
let event = DomainEvent::NoSignal { channel_id: ch };
|
||||||
|
match event {
|
||||||
|
DomainEvent::NoSignal { channel_id } => assert_eq!(channel_id, ch),
|
||||||
|
_ => panic!("wrong variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn schedule_generated_carries_ids() {
|
||||||
|
let ch = ChannelId::from(Uuid::new_v4());
|
||||||
|
let sc = ScheduleId::from(Uuid::new_v4());
|
||||||
|
let event = DomainEvent::ScheduleGenerated {
|
||||||
|
channel_id: ch,
|
||||||
|
schedule_id: sc,
|
||||||
|
};
|
||||||
|
match event {
|
||||||
|
DomainEvent::ScheduleGenerated {
|
||||||
|
channel_id,
|
||||||
|
schedule_id,
|
||||||
|
} => {
|
||||||
|
assert_eq!(channel_id, ch);
|
||||||
|
assert_eq!(schedule_id, sc);
|
||||||
|
}
|
||||||
|
_ => panic!("wrong variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn channel_lifecycle_events() {
|
||||||
|
let ch = ChannelId::from(Uuid::new_v4());
|
||||||
|
|
||||||
|
let created = DomainEvent::ChannelCreated { channel_id: ch };
|
||||||
|
let updated = DomainEvent::ChannelUpdated { channel_id: ch };
|
||||||
|
let deleted = DomainEvent::ChannelDeleted { channel_id: ch };
|
||||||
|
|
||||||
|
// All three carry the same channel_id.
|
||||||
|
for event in [created, updated, deleted] {
|
||||||
|
match event {
|
||||||
|
DomainEvent::ChannelCreated { channel_id }
|
||||||
|
| DomainEvent::ChannelUpdated { channel_id }
|
||||||
|
| DomainEvent::ChannelDeleted { channel_id } => {
|
||||||
|
assert_eq!(channel_id, ch);
|
||||||
|
}
|
||||||
|
_ => panic!("wrong variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn event_is_clone() {
|
||||||
|
let ch = ChannelId::from(Uuid::new_v4());
|
||||||
|
let event = DomainEvent::NoSignal { channel_id: ch };
|
||||||
|
let cloned = event.clone();
|
||||||
|
match cloned {
|
||||||
|
DomainEvent::NoSignal { channel_id } => assert_eq!(channel_id, ch),
|
||||||
|
_ => panic!("wrong variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
pub mod events;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod ports;
|
pub mod ports;
|
||||||
pub mod value_objects;
|
pub mod value_objects;
|
||||||
|
|
||||||
pub use errors::{DomainError, DomainResult};
|
pub use errors::{DomainError, DomainResult};
|
||||||
|
pub use events::DomainEvent;
|
||||||
pub use models::*;
|
pub use models::*;
|
||||||
pub use value_objects::*;
|
pub use value_objects::*;
|
||||||
|
|||||||
@@ -8,30 +8,7 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use crate::errors::DomainResult;
|
use crate::errors::DomainResult;
|
||||||
use crate::value_objects::ChannelId;
|
pub use crate::events::DomainEvent;
|
||||||
|
|
||||||
/// A domain event emitted by aggregate operations.
|
|
||||||
///
|
|
||||||
/// New variants will be added as the system grows. Downstream consumers
|
|
||||||
/// pattern-match and ignore unknown variants.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum DomainEvent {
|
|
||||||
/// A new schedule was generated for a channel.
|
|
||||||
ScheduleGenerated {
|
|
||||||
channel_id: ChannelId,
|
|
||||||
generation: u32,
|
|
||||||
},
|
|
||||||
/// A library sync completed for a provider.
|
|
||||||
LibrarySyncCompleted {
|
|
||||||
provider_id: String,
|
|
||||||
items_found: u32,
|
|
||||||
},
|
|
||||||
/// A channel was created.
|
|
||||||
ChannelCreated { channel_id: ChannelId },
|
|
||||||
/// A channel was deleted.
|
|
||||||
ChannelDeleted { channel_id: ChannelId },
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Port for publishing domain events.
|
/// Port for publishing domain events.
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user