Skip to main content

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}