1use crate::{
2 domain::value_objects::{EntityId, EventType, TenantId},
3 error::Result,
4};
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
22pub struct Event {
23 pub id: Uuid,
24 pub event_type: EventType,
25 pub entity_id: EntityId,
26 #[serde(default = "default_tenant_id")]
27 pub tenant_id: TenantId,
28 pub payload: serde_json::Value,
29 pub timestamp: DateTime<Utc>,
30 pub metadata: Option<serde_json::Value>,
31 pub version: i64,
32}
33
34fn default_tenant_id() -> TenantId {
35 TenantId::default_tenant()
36}
37
38impl Event {
39 pub fn new(
41 event_type: EventType,
42 entity_id: EntityId,
43 tenant_id: TenantId,
44 payload: serde_json::Value,
45 ) -> Self {
46 Self {
47 id: Uuid::new_v4(),
48 event_type,
49 entity_id,
50 tenant_id,
51 payload,
52 timestamp: Utc::now(),
53 metadata: None,
54 version: 1,
55 }
56 }
57
58 pub fn with_metadata(
60 event_type: EventType,
61 entity_id: EntityId,
62 tenant_id: TenantId,
63 payload: serde_json::Value,
64 metadata: serde_json::Value,
65 ) -> Self {
66 Self {
67 id: Uuid::new_v4(),
68 event_type,
69 entity_id,
70 tenant_id,
71 payload,
72 timestamp: Utc::now(),
73 metadata: Some(metadata),
74 version: 1,
75 }
76 }
77
78 pub fn with_default_tenant(
80 event_type: EventType,
81 entity_id: EntityId,
82 payload: serde_json::Value,
83 ) -> Self {
84 Self::new(event_type, entity_id, TenantId::default_tenant(), payload)
85 }
86
87 pub fn from_strings(
92 event_type: String,
93 entity_id: String,
94 tenant_id: String,
95 payload: serde_json::Value,
96 metadata: Option<serde_json::Value>,
97 ) -> Result<Self> {
98 let event_type = EventType::new(event_type)?;
99 let entity_id = EntityId::new(entity_id)?;
100 let tenant_id = TenantId::new(tenant_id)?;
101
102 Ok(Self {
103 id: Uuid::new_v4(),
104 event_type,
105 entity_id,
106 tenant_id,
107 payload,
108 timestamp: Utc::now(),
109 metadata,
110 version: 1,
111 })
112 }
113
114 pub fn reconstruct(
116 id: Uuid,
117 event_type: EventType,
118 entity_id: EntityId,
119 tenant_id: TenantId,
120 payload: serde_json::Value,
121 timestamp: DateTime<Utc>,
122 metadata: Option<serde_json::Value>,
123 version: i64,
124 ) -> Self {
125 Self {
126 id,
127 event_type,
128 entity_id,
129 tenant_id,
130 payload,
131 timestamp,
132 metadata,
133 version,
134 }
135 }
136
137 pub fn reconstruct_from_strings(
139 id: Uuid,
140 event_type: String,
141 entity_id: String,
142 tenant_id: String,
143 payload: serde_json::Value,
144 timestamp: DateTime<Utc>,
145 metadata: Option<serde_json::Value>,
146 version: i64,
147 ) -> Self {
148 Self {
149 id,
150 event_type: EventType::new_unchecked(event_type),
151 entity_id: EntityId::new_unchecked(entity_id),
152 tenant_id: TenantId::new_unchecked(tenant_id),
153 payload,
154 timestamp,
155 metadata,
156 version,
157 }
158 }
159
160 pub fn id(&self) -> Uuid {
163 self.id
164 }
165
166 pub fn event_type(&self) -> &EventType {
167 &self.event_type
168 }
169
170 pub fn event_type_str(&self) -> &str {
171 self.event_type.as_str()
172 }
173
174 pub fn entity_id(&self) -> &EntityId {
175 &self.entity_id
176 }
177
178 pub fn entity_id_str(&self) -> &str {
179 self.entity_id.as_str()
180 }
181
182 pub fn tenant_id(&self) -> &TenantId {
183 &self.tenant_id
184 }
185
186 pub fn tenant_id_str(&self) -> &str {
187 self.tenant_id.as_str()
188 }
189
190 pub fn payload(&self) -> &serde_json::Value {
191 &self.payload
192 }
193
194 pub fn timestamp(&self) -> DateTime<Utc> {
195 self.timestamp
196 }
197
198 pub fn metadata(&self) -> Option<&serde_json::Value> {
199 self.metadata.as_ref()
200 }
201
202 pub fn version(&self) -> i64 {
203 self.version
204 }
205
206 pub fn estimated_size_bytes(&self) -> u64 {
224 const FIXED_OVERHEAD: u64 = 256;
225 let payload_bytes = serde_json::to_vec(&self.payload).map_or(0, |v| v.len() as u64);
226 let metadata_bytes = self
227 .metadata
228 .as_ref()
229 .and_then(|m| serde_json::to_vec(m).ok())
230 .map_or(0, |v| v.len() as u64);
231 FIXED_OVERHEAD + payload_bytes + metadata_bytes
232 }
233
234 pub fn belongs_to_tenant(&self, tenant_id: &TenantId) -> bool {
238 &self.tenant_id == tenant_id
239 }
240
241 pub fn belongs_to_tenant_str(&self, tenant_id: &str) -> bool {
243 self.tenant_id.as_str() == tenant_id
244 }
245
246 pub fn relates_to_entity(&self, entity_id: &EntityId) -> bool {
248 &self.entity_id == entity_id
249 }
250
251 pub fn relates_to_entity_str(&self, entity_id: &str) -> bool {
253 self.entity_id.as_str() == entity_id
254 }
255
256 pub fn is_type(&self, event_type: &EventType) -> bool {
258 &self.event_type == event_type
259 }
260
261 pub fn is_type_str(&self, event_type: &str) -> bool {
263 self.event_type.as_str() == event_type
264 }
265
266 pub fn is_in_namespace(&self, namespace: &str) -> bool {
268 self.event_type.is_in_namespace(namespace)
269 }
270
271 pub fn occurred_between(&self, start: DateTime<Utc>, end: DateTime<Utc>) -> bool {
273 self.timestamp >= start && self.timestamp <= end
274 }
275
276 pub fn occurred_before(&self, time: DateTime<Utc>) -> bool {
278 self.timestamp < time
279 }
280
281 pub fn occurred_after(&self, time: DateTime<Utc>) -> bool {
283 self.timestamp > time
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290 use serde_json::json;
291
292 fn test_event_type() -> EventType {
293 EventType::new("user.created".to_string()).unwrap()
294 }
295
296 fn test_entity_id() -> EntityId {
297 EntityId::new("user-123".to_string()).unwrap()
298 }
299
300 fn test_tenant_id() -> TenantId {
301 TenantId::new("tenant-1".to_string()).unwrap()
302 }
303
304 #[test]
305 fn test_estimated_size_bytes_grows_with_payload() {
306 let small = Event::new(
310 test_event_type(),
311 test_entity_id(),
312 test_tenant_id(),
313 json!({"k": "v"}),
314 );
315 let large_payload = json!({"data": "x".repeat(10_000)});
316 let large = Event::new(
317 test_event_type(),
318 test_entity_id(),
319 test_tenant_id(),
320 large_payload,
321 );
322
323 assert!(small.estimated_size_bytes() < large.estimated_size_bytes());
324 assert!(small.estimated_size_bytes() >= 256);
327 assert!(large.estimated_size_bytes() > 10_000);
328 }
329
330 #[test]
331 fn test_estimated_size_bytes_includes_metadata() {
332 let no_meta = Event::new(
333 test_event_type(),
334 test_entity_id(),
335 test_tenant_id(),
336 json!({"k": "v"}),
337 );
338 let with_meta = Event::with_metadata(
339 test_event_type(),
340 test_entity_id(),
341 test_tenant_id(),
342 json!({"k": "v"}),
343 json!({"correlation_id": "abc-123-def-456-very-long"}),
344 );
345 assert!(with_meta.estimated_size_bytes() > no_meta.estimated_size_bytes());
346 }
347
348 #[test]
349 fn test_event_creation_with_value_objects() {
350 let event = Event::new(
351 test_event_type(),
352 test_entity_id(),
353 test_tenant_id(),
354 json!({"name": "Alice"}),
355 );
356
357 assert_eq!(event.event_type_str(), "user.created");
358 assert_eq!(event.entity_id_str(), "user-123");
359 assert_eq!(event.tenant_id_str(), "tenant-1");
360 assert_eq!(event.version(), 1);
361 }
362
363 #[test]
364 fn test_event_creation_from_strings() {
365 let event = Event::from_strings(
366 "user.created".to_string(),
367 "user-123".to_string(),
368 "tenant-1".to_string(),
369 json!({"name": "Alice"}),
370 None,
371 );
372
373 assert!(event.is_ok());
374 let event = event.unwrap();
375 assert_eq!(event.event_type_str(), "user.created");
376 assert_eq!(event.entity_id_str(), "user-123");
377 assert_eq!(event.tenant_id_str(), "tenant-1");
378 }
379
380 #[test]
381 fn test_event_with_metadata() {
382 let event = Event::with_metadata(
383 test_event_type(),
384 test_entity_id(),
385 test_tenant_id(),
386 json!({"name": "Bob"}),
387 json!({"source": "api"}),
388 );
389
390 assert!(event.metadata().is_some());
391 assert_eq!(event.metadata().unwrap(), &json!({"source": "api"}));
392 }
393
394 #[test]
395 fn test_event_with_default_tenant() {
396 let event = Event::with_default_tenant(test_event_type(), test_entity_id(), json!({}));
397
398 assert_eq!(event.tenant_id_str(), "default");
399 }
400
401 #[test]
402 fn test_from_strings_validates_event_type() {
403 let result = Event::from_strings(
405 "User.Created".to_string(),
406 "e1".to_string(),
407 "t1".to_string(),
408 json!({}),
409 None,
410 );
411 assert!(result.is_err());
412
413 let result = Event::from_strings(
415 String::new(),
416 "e1".to_string(),
417 "t1".to_string(),
418 json!({}),
419 None,
420 );
421 assert!(result.is_err());
422 }
423
424 #[test]
425 fn test_from_strings_validates_entity_id() {
426 let result = Event::from_strings(
428 "user.created".to_string(),
429 String::new(),
430 "t1".to_string(),
431 json!({}),
432 None,
433 );
434 assert!(result.is_err());
435 }
436
437 #[test]
438 fn test_from_strings_validates_tenant_id() {
439 let result = Event::from_strings(
441 "user.created".to_string(),
442 "e1".to_string(),
443 String::new(),
444 json!({}),
445 None,
446 );
447 assert!(result.is_err());
448 }
449
450 #[test]
451 fn test_belongs_to_tenant() {
452 let tenant1 = TenantId::new("tenant-1".to_string()).unwrap();
453 let tenant2 = TenantId::new("tenant-2".to_string()).unwrap();
454
455 let event = Event::new(
456 test_event_type(),
457 test_entity_id(),
458 tenant1.clone(),
459 json!({}),
460 );
461
462 assert!(event.belongs_to_tenant(&tenant1));
463 assert!(!event.belongs_to_tenant(&tenant2));
464 }
465
466 #[test]
467 fn test_belongs_to_tenant_str() {
468 let event = Event::new(
469 test_event_type(),
470 test_entity_id(),
471 test_tenant_id(),
472 json!({}),
473 );
474
475 assert!(event.belongs_to_tenant_str("tenant-1"));
476 assert!(!event.belongs_to_tenant_str("tenant-2"));
477 }
478
479 #[test]
480 fn test_relates_to_entity() {
481 let entity1 = EntityId::new("order-456".to_string()).unwrap();
482 let entity2 = EntityId::new("order-789".to_string()).unwrap();
483
484 let event = Event::new(
485 EventType::new("order.placed".to_string()).unwrap(),
486 entity1.clone(),
487 test_tenant_id(),
488 json!({}),
489 );
490
491 assert!(event.relates_to_entity(&entity1));
492 assert!(!event.relates_to_entity(&entity2));
493 }
494
495 #[test]
496 fn test_relates_to_entity_str() {
497 let event = Event::new(
498 EventType::new("order.placed".to_string()).unwrap(),
499 EntityId::new("order-456".to_string()).unwrap(),
500 test_tenant_id(),
501 json!({}),
502 );
503
504 assert!(event.relates_to_entity_str("order-456"));
505 assert!(!event.relates_to_entity_str("order-789"));
506 }
507
508 #[test]
509 fn test_is_type() {
510 let type1 = EventType::new("order.placed".to_string()).unwrap();
511 let type2 = EventType::new("order.cancelled".to_string()).unwrap();
512
513 let event = Event::new(type1.clone(), test_entity_id(), test_tenant_id(), json!({}));
514
515 assert!(event.is_type(&type1));
516 assert!(!event.is_type(&type2));
517 }
518
519 #[test]
520 fn test_is_type_str() {
521 let event = Event::new(
522 EventType::new("order.placed".to_string()).unwrap(),
523 test_entity_id(),
524 test_tenant_id(),
525 json!({}),
526 );
527
528 assert!(event.is_type_str("order.placed"));
529 assert!(!event.is_type_str("order.cancelled"));
530 }
531
532 #[test]
533 fn test_is_in_namespace() {
534 let event = Event::new(
535 EventType::new("order.placed".to_string()).unwrap(),
536 test_entity_id(),
537 test_tenant_id(),
538 json!({}),
539 );
540
541 assert!(event.is_in_namespace("order"));
542 assert!(!event.is_in_namespace("user"));
543 }
544
545 #[test]
546 fn test_time_range_queries() {
547 let event = Event::new(
548 test_event_type(),
549 test_entity_id(),
550 test_tenant_id(),
551 json!({}),
552 );
553
554 let past = Utc::now() - chrono::Duration::hours(1);
555 let future = Utc::now() + chrono::Duration::hours(1);
556
557 assert!(event.occurred_after(past));
558 assert!(event.occurred_before(future));
559 assert!(event.occurred_between(past, future));
560 }
561
562 #[test]
563 fn test_serde_serialization() {
564 let event = Event::new(
565 test_event_type(),
566 test_entity_id(),
567 test_tenant_id(),
568 json!({"test": "data"}),
569 );
570
571 let json = serde_json::to_string(&event);
573 assert!(json.is_ok());
574
575 let deserialized = serde_json::from_str::<Event>(&json.unwrap());
577 assert!(deserialized.is_ok());
578
579 let deserialized = deserialized.unwrap();
580 assert_eq!(deserialized.event_type_str(), "user.created");
581 assert_eq!(deserialized.entity_id_str(), "user-123");
582 }
583
584 #[test]
585 fn test_reconstruct_from_strings() {
586 let id = Uuid::new_v4();
587 let timestamp = Utc::now();
588
589 let event = Event::reconstruct_from_strings(
590 id,
591 "order.placed".to_string(),
592 "order-123".to_string(),
593 "tenant-1".to_string(),
594 json!({"amount": 100}),
595 timestamp,
596 Some(json!({"source": "api"})),
597 1,
598 );
599
600 assert_eq!(event.id(), id);
601 assert_eq!(event.event_type_str(), "order.placed");
602 assert_eq!(event.entity_id_str(), "order-123");
603 assert_eq!(event.tenant_id_str(), "tenant-1");
604 assert_eq!(event.version(), 1);
605 }
606}