eventuali_core/event.rs
1//! Core event types and structures for the Eventuali event sourcing library.
2//!
3//! This module defines the fundamental building blocks of event sourcing:
4//! - [`Event`] - The core event structure containing all event data
5//! - [`EventData`] - Flexible event payload supporting JSON and Protocol Buffers
6//! - [`EventMetadata`] - Correlation, causation, and tracing information
7//!
8//! # Example
9//!
10//! ```rust
11//! use eventuali_core::{Event, EventData, EventMetadata};
12//! use chrono::Utc;
13//! use serde_json::json;
14//! use uuid::Uuid;
15//!
16//! let event = Event {
17//! id: Uuid::new_v4(),
18//! aggregate_id: "user-123".to_string(),
19//! aggregate_type: "User".to_string(),
20//! event_type: "UserRegistered".to_string(),
21//! event_version: 1,
22//! aggregate_version: 1,
23//! data: EventData::from_json(&json!({
24//! "name": "Alice",
25//! "email": "alice@example.com"
26//! })).unwrap(),
27//! metadata: EventMetadata::default(),
28//! timestamp: Utc::now(),
29//! };
30//! ```
31
32use chrono::{DateTime, Utc};
33use serde::{Deserialize, Serialize};
34use uuid::Uuid;
35
36/// Unique identifier for events, using UUID for global uniqueness
37pub type EventId = Uuid;
38
39/// Core event structure representing a domain event in the event store.
40///
41/// Events are immutable records of things that happened in your domain.
42/// Each event belongs to an aggregate and represents a state change.
43///
44/// # Fields
45///
46/// * `id` - Unique identifier for this event
47/// * `aggregate_id` - ID of the aggregate this event belongs to
48/// * `aggregate_type` - Type of aggregate (e.g., "User", "Order")
49/// * `event_type` - Type of event (e.g., "UserRegistered", "OrderCreated")
50/// * `event_version` - Schema version of the event type
51/// * `aggregate_version` - Version of the aggregate after this event
52/// * `data` - The event payload (JSON or Protocol Buffers)
53/// * `metadata` - Additional event metadata for tracing and correlation
54/// * `timestamp` - When the event occurred
55#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
56pub struct Event {
57 pub id: EventId,
58 pub aggregate_id: String,
59 pub aggregate_type: String,
60 pub event_type: String,
61 pub event_version: i32,
62 pub aggregate_version: i64,
63 pub data: EventData,
64 pub metadata: EventMetadata,
65 pub timestamp: DateTime<Utc>,
66}
67
68/// Event payload data supporting multiple serialization formats.
69///
70/// EventData provides flexibility in how you store event payloads:
71/// - JSON for human-readable, schema-flexible events
72/// - Protocol Buffers for compact, strongly-typed events
73///
74/// # Examples
75///
76/// ```rust
77/// use eventuali_core::EventData;
78/// use serde_json::json;
79///
80/// // JSON event data
81/// let json_data = EventData::from_json(&json!({
82/// "user_id": "123",
83/// "action": "login"
84/// })).unwrap();
85///
86/// // Protocol Buffer event data
87/// let proto_data = EventData::Protobuf(vec![1, 2, 3, 4]);
88/// ```
89#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
90pub enum EventData {
91 Json(serde_json::Value),
92 Protobuf(Vec<u8>),
93}
94
95/// Metadata associated with events for tracing, correlation, and context.
96///
97/// EventMetadata provides essential context for event processing:
98/// - Causation and correlation IDs for distributed tracing
99/// - User context for audit trails
100/// - Custom headers for additional context
101///
102/// # Example
103///
104/// ```rust
105/// use eventuali_core::EventMetadata;
106/// use uuid::Uuid;
107/// use std::collections::HashMap;
108///
109/// let mut metadata = EventMetadata::default();
110/// metadata.user_id = Some("user-123".to_string());
111/// metadata.correlation_id = Some(Uuid::new_v4());
112/// metadata.headers.insert("source".to_string(), "web-app".to_string());
113/// ```
114#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
115pub struct EventMetadata {
116 pub causation_id: Option<EventId>,
117 pub correlation_id: Option<EventId>,
118 pub user_id: Option<String>,
119 pub headers: std::collections::HashMap<String, String>,
120}
121
122impl Event {
123 /// Creates a new event with the specified parameters.
124 ///
125 /// This is a convenience constructor that generates a new UUID for the event ID
126 /// and sets the timestamp to the current UTC time.
127 ///
128 /// # Arguments
129 ///
130 /// * `aggregate_id` - ID of the aggregate this event belongs to
131 /// * `aggregate_type` - Type of the aggregate
132 /// * `event_type` - Type of the event
133 /// * `event_version` - Schema version of the event
134 /// * `aggregate_version` - Version of the aggregate after this event
135 /// * `data` - The event payload
136 ///
137 /// # Example
138 ///
139 /// ```rust
140 /// use eventuali_core::{Event, EventData};
141 /// use serde_json::json;
142 ///
143 /// let event = Event::new(
144 /// "user-123".to_string(),
145 /// "User".to_string(),
146 /// "UserRegistered".to_string(),
147 /// 1,
148 /// 1,
149 /// EventData::from_json(&json!({"name": "Alice"})).unwrap(),
150 /// );
151 /// ```
152 pub fn new(
153 aggregate_id: String,
154 aggregate_type: String,
155 event_type: String,
156 event_version: i32,
157 aggregate_version: i64,
158 data: EventData,
159 ) -> Self {
160 Self {
161 id: Uuid::new_v4(),
162 aggregate_id,
163 aggregate_type,
164 event_type,
165 event_version,
166 aggregate_version,
167 data,
168 metadata: EventMetadata::default(),
169 timestamp: Utc::now(),
170 }
171 }
172
173 pub fn with_metadata(mut self, metadata: EventMetadata) -> Self {
174 self.metadata = metadata;
175 self
176 }
177}
178
179
180impl EventData {
181 pub fn from_json<T: Serialize>(value: &T) -> crate::Result<Self> {
182 let json_value = serde_json::to_value(value)?;
183 Ok(EventData::Json(json_value))
184 }
185
186 pub fn to_json<T: for<'de> Deserialize<'de>>(&self) -> crate::Result<T> {
187 match self {
188 EventData::Json(value) => Ok(serde_json::from_value(value.clone())?),
189 EventData::Protobuf(_) => Err(crate::EventualiError::InvalidEventData(
190 "Cannot deserialize protobuf data as JSON".to_string(),
191 )),
192 }
193 }
194
195 pub fn from_protobuf(data: Vec<u8>) -> Self {
196 EventData::Protobuf(data)
197 }
198
199 pub fn to_protobuf(&self) -> crate::Result<&[u8]> {
200 match self {
201 EventData::Protobuf(data) => Ok(data),
202 EventData::Json(_) => Err(crate::EventualiError::InvalidEventData(
203 "Cannot get protobuf data from JSON event".to_string(),
204 )),
205 }
206 }
207
208 pub fn from_protobuf_message<T: prost::Message>(message: &T) -> crate::Result<Self> {
209 let mut buf = Vec::new();
210 message.encode(&mut buf)
211 .map_err(|e| crate::EventualiError::InvalidEventData(e.to_string()))?;
212 Ok(EventData::Protobuf(buf))
213 }
214
215 pub fn to_protobuf_message<T: prost::Message + Default>(&self) -> crate::Result<T> {
216 match self {
217 EventData::Protobuf(data) => {
218 T::decode(&data[..])
219 .map_err(crate::EventualiError::Protobuf)
220 },
221 EventData::Json(_) => Err(crate::EventualiError::InvalidEventData(
222 "Cannot decode protobuf message from JSON data".to_string(),
223 )),
224 }
225 }
226}