1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3
4use crate::repositories::PortError;
5use crate::value_objects::{RelationExplanation, RelationSemanticClass};
6
7use super::ProjectionCheckpoint;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub struct ProjectionEnvelope {
11 pub event_id: String,
12 pub correlation_id: String,
13 pub causation_id: String,
14 pub occurred_at: String,
15 pub aggregate_id: String,
16 pub aggregate_type: String,
17 pub schema_version: String,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct RelatedNodeReference {
22 pub node_id: String,
23 pub relation_type: String,
24 pub explanation: RelatedNodeExplanationData,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct RelatedNodeExplanationData {
29 pub semantic_class: RelationSemanticClass,
30 #[serde(default)]
31 pub rationale: Option<String>,
32 #[serde(default)]
33 pub motivation: Option<String>,
34 #[serde(default)]
35 pub method: Option<String>,
36 #[serde(default)]
37 pub decision_id: Option<String>,
38 #[serde(default)]
39 pub caused_by_node_id: Option<String>,
40 #[serde(default)]
41 pub evidence: Option<String>,
42 #[serde(default)]
43 pub confidence: Option<String>,
44 #[serde(default)]
45 pub sequence: Option<u32>,
46}
47
48impl TryFrom<RelatedNodeExplanationData> for RelationExplanation {
49 type Error = crate::DomainError;
50
51 fn try_from(value: RelatedNodeExplanationData) -> Result<Self, Self::Error> {
52 Ok(RelationExplanation::new(value.semantic_class)
53 .with_optional_rationale(value.rationale)
54 .with_optional_motivation(value.motivation)
55 .with_optional_method(value.method)
56 .with_optional_decision_id(value.decision_id)
57 .with_optional_caused_by_node_id(value.caused_by_node_id)
58 .with_optional_evidence(value.evidence)
59 .with_optional_confidence(value.confidence)
60 .with_optional_sequence(value.sequence))
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65pub struct GraphNodeMaterializedData {
66 pub node_id: String,
67 pub node_kind: String,
68 pub title: String,
69 pub summary: String,
70 pub status: String,
71 pub labels: Vec<String>,
72 pub properties: BTreeMap<String, String>,
73 pub related_nodes: Vec<RelatedNodeReference>,
74 #[serde(default)]
76 pub source_kind: Option<String>,
77 #[serde(default)]
78 pub source_agent: Option<String>,
79 #[serde(default)]
80 pub observed_at: Option<String>,
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
84pub struct GraphNodeMaterializedEvent {
85 #[serde(flatten)]
86 pub envelope: ProjectionEnvelope,
87 pub data: GraphNodeMaterializedData,
88}
89
90#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
91pub struct GraphRelationMaterializedData {
92 pub source_node_id: String,
93 pub target_node_id: String,
94 pub relation_type: String,
95 pub explanation: RelatedNodeExplanationData,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
99pub struct GraphRelationMaterializedEvent {
100 #[serde(flatten)]
101 pub envelope: ProjectionEnvelope,
102 pub data: GraphRelationMaterializedData,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
106pub struct NodeDetailMaterializedData {
107 pub node_id: String,
108 pub detail: String,
109 pub content_hash: String,
110 pub revision: u64,
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114pub struct NodeDetailMaterializedEvent {
115 #[serde(flatten)]
116 pub envelope: ProjectionEnvelope,
117 pub data: NodeDetailMaterializedData,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq)]
121pub enum ProjectionEvent {
122 GraphNodeMaterialized(GraphNodeMaterializedEvent),
123 GraphRelationMaterialized(GraphRelationMaterializedEvent),
124 NodeDetailMaterialized(NodeDetailMaterializedEvent),
125}
126
127impl ProjectionEvent {
128 pub fn event_id(&self) -> &str {
129 match self {
130 Self::GraphNodeMaterialized(event) => &event.envelope.event_id,
131 Self::GraphRelationMaterialized(event) => &event.envelope.event_id,
132 Self::NodeDetailMaterialized(event) => &event.envelope.event_id,
133 }
134 }
135
136 pub fn envelope(&self) -> &ProjectionEnvelope {
137 match self {
138 Self::GraphNodeMaterialized(event) => &event.envelope,
139 Self::GraphRelationMaterialized(event) => &event.envelope,
140 Self::NodeDetailMaterialized(event) => &event.envelope,
141 }
142 }
143}
144
145#[derive(Debug, Clone, PartialEq, Eq)]
146pub struct ProjectionHandlingRequest {
147 pub consumer_name: String,
148 pub stream_name: String,
149 pub subject: String,
150 pub event: ProjectionEvent,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq)]
154pub struct ProjectionHandlingResult {
155 pub event_id: String,
156 pub subject: String,
157 pub duplicate: bool,
158 pub applied_mutations: usize,
159 pub checkpoint: Option<ProjectionCheckpoint>,
160}
161
162pub trait ProjectionEventHandler {
163 fn handle_projection_event(
164 &self,
165 request: ProjectionHandlingRequest,
166 ) -> impl std::future::Future<Output = Result<ProjectionHandlingResult, PortError>> + Send;
167}