Skip to main content

made_core/ports/
messaging.rs

1//! [`MessagingPort`] — asynchronous message bus used to publish and
2//! consume domain events.
3//!
4//! The port speaks in domain-event terms; adapters map to/from NATS,
5//! Kafka, or any other substrate without leaking transport details
6//! into the core.
7
8use async_trait::async_trait;
9use serde::{de::DeserializeOwned, Serialize};
10
11use crate::error::DomainError;
12use crate::events::{
13    DeliberationCompletedEvent, PhaseChangedEvent, TaskCompletedEvent, TaskDispatchedEvent,
14    TaskFailedEvent,
15};
16
17/// Marker trait shared by every published domain event so they can be
18/// serialized by the adapter. The marker is automatically implemented
19/// for all `Serialize + DeserializeOwned + Send + Sync` types — no
20/// explicit `impl DomainEvent for …` is required.
21pub trait DomainEvent: Serialize + DeserializeOwned + Send + Sync + 'static {}
22
23impl DomainEvent for TaskDispatchedEvent {}
24impl DomainEvent for TaskCompletedEvent {}
25impl DomainEvent for TaskFailedEvent {}
26impl DomainEvent for DeliberationCompletedEvent {}
27impl DomainEvent for PhaseChangedEvent {}
28
29/// Handler invoked by the messaging adapter for every message on a
30/// subscribed subject. Handlers receive already-deserialized domain
31/// events; transport errors never reach them.
32#[async_trait]
33pub trait SubscriptionHandler<E: DomainEvent>: Send + Sync {
34    async fn handle(&self, event: E) -> Result<(), DomainError>;
35}
36
37/// Publish / subscribe surface. Intentionally narrow: specific
38/// `publish_*` methods per event type enforce that publishing is a
39/// first-class, audited action — publishing an arbitrary untyped
40/// payload is not possible.
41#[async_trait]
42pub trait MessagingPort: Send + Sync {
43    async fn publish_task_dispatched(&self, event: &TaskDispatchedEvent)
44        -> Result<(), DomainError>;
45    async fn publish_task_completed(&self, event: &TaskCompletedEvent) -> Result<(), DomainError>;
46    async fn publish_task_failed(&self, event: &TaskFailedEvent) -> Result<(), DomainError>;
47    async fn publish_deliberation_completed(
48        &self,
49        event: &DeliberationCompletedEvent,
50    ) -> Result<(), DomainError>;
51    async fn publish_phase_changed(&self, event: &PhaseChangedEvent) -> Result<(), DomainError>;
52}