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}