eventide_application/context.rs
1use eventide_domain::{domain_event::EventContext, persist::SerializedEvent};
2
3/// Application Context for a single command/query invocation.
4///
5/// `AppContext` carries the cross-cutting metadata required by the
6/// application layer when handling one logical request. It is created at the
7/// system boundary (HTTP handler, message consumer, scheduled job) and
8/// passed unchanged through the command/query bus down to the handler so
9/// that downstream domain events can be tagged with the correct provenance.
10///
11/// The context bundles two concerns:
12///
13/// - Business / tracing context ([`EventContext`]): correlation id, causation
14/// id, and the actor (type and id) responsible for the operation. These
15/// fields are persisted alongside emitted domain events so that the full
16/// causal chain can be reconstructed for auditing and debugging.
17/// - Idempotency key (`idempotency_key`): an optional infrastructure-level
18/// token used by the API or messaging layer to deduplicate retried
19/// submissions. The application/domain layers do not interpret this
20/// value — they merely propagate it to the infrastructure boundary.
21///
22/// # Examples
23///
24/// ```rust
25/// use eventide_application::context::AppContext;
26/// use eventide_domain::domain_event::EventContext;
27///
28/// let ctx = AppContext {
29/// event_context: EventContext::builder()
30/// .maybe_correlation_id(Some("cor-123".into()))
31/// .maybe_causation_id(Some("cau-abc".into()))
32/// .maybe_actor_type(Some("user".into()))
33/// .maybe_actor_id(Some("u-1".into()))
34/// .build(),
35/// idempotency_key: Some("idem-xyz".into()),
36/// };
37/// ```
38#[derive(Clone, Debug, Default)]
39pub struct AppContext {
40 /// Business context: tracing identifiers, audit subject, causal lineage.
41 pub event_context: EventContext,
42 /// Optional idempotency key. When `None`, the upper layer or
43 /// infrastructure decides whether and how to apply idempotent semantics.
44 pub idempotency_key: Option<String>,
45}
46
47impl From<&SerializedEvent> for AppContext {
48 fn from(event: &SerializedEvent) -> Self {
49 Self {
50 event_context: EventContext::builder()
51 .maybe_correlation_id(event.correlation_id().map(ToString::to_string))
52 .causation_id(event.event_id().to_string())
53 .maybe_actor_type(event.actor_type().map(ToString::to_string))
54 .maybe_actor_id(event.actor_id().map(ToString::to_string))
55 .build(),
56 idempotency_key: None,
57 }
58 }
59}