1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//! [`DeliberationObserverPort`] — per-call hook for observing a
//! deliberation's lifecycle.
//!
//! Unlike [`MessagingPort`](super::MessagingPort), which broadcasts
//! persistent domain events (NATS), this port is for **ephemeral
//! per-call observation**: the server-streaming `StreamDeliberation`
//! RPC wires an adapter that forwards events to an `mpsc::channel`
//! becoming the response stream, and discards them when the stream
//! closes. There is no subscription / replay semantics; the observer
//! is call-scoped.
//!
//! Keeping this separate from `MessagingPort` means `DeliberateUseCase`
//! can stay oblivious to whether a caller is listening on a stream,
//! and messaging adapters stay oblivious to live stream semantics.
//!
//! The port is implementation-optional: callers that do not care pass a
//! no-op adapter.
use async_trait::async_trait;
use time::OffsetDateTime;
use crate::entities::DeliberationPhase;
use crate::value_objects::TaskId;
#[async_trait]
pub trait DeliberationObserverPort: Send + Sync {
/// Called when the aggregate transitions into `phase`.
///
/// Observers must not block or the use case stalls; adapters that
/// need unbounded work should spawn it. Adapters whose sink has
/// closed (receiver dropped) should become a no-op silently —
/// the use case does not care whether anyone is still listening.
async fn on_phase_changed(
&self,
task_id: &TaskId,
phase: DeliberationPhase,
emitted_at: OffsetDateTime,
);
}