made_core/value_objects/outbox/
outbox_message.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use time::OffsetDateTime;
4
5use crate::error::DomainError;
6use crate::value_objects::EventId;
7
8use super::OutboxSubject;
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct OutboxMessage {
18 event_id: EventId,
19 subject: OutboxSubject,
20 payload: Value,
21 #[serde(with = "time::serde::rfc3339")]
22 enqueued_at: OffsetDateTime,
23}
24
25impl OutboxMessage {
26 pub fn new(
27 event_id: EventId,
28 subject: OutboxSubject,
29 payload: Value,
30 enqueued_at: OffsetDateTime,
31 ) -> Result<Self, DomainError> {
32 if payload.is_null() {
33 return Err(DomainError::EmptyField {
34 field: "outbox_message.payload",
35 });
36 }
37 Ok(Self {
38 event_id,
39 subject,
40 payload,
41 enqueued_at,
42 })
43 }
44
45 #[must_use]
47 pub fn event_id(&self) -> &EventId {
48 &self.event_id
49 }
50
51 #[must_use]
52 pub fn subject(&self) -> &OutboxSubject {
53 &self.subject
54 }
55
56 #[must_use]
57 pub fn payload(&self) -> &Value {
58 &self.payload
59 }
60
61 #[must_use]
62 pub fn enqueued_at(&self) -> OffsetDateTime {
63 self.enqueued_at
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use serde_json::json;
71 use time::macros::datetime;
72
73 fn subject() -> OutboxSubject {
74 OutboxSubject::new("made.ceremony.completed").unwrap()
75 }
76
77 #[test]
78 fn a_null_payload_is_rejected() {
79 assert!(matches!(
80 OutboxMessage::new(
81 EventId::new("e1").unwrap(),
82 subject(),
83 Value::Null,
84 datetime!(2026-07-29 09:00:00 UTC),
85 ),
86 Err(DomainError::EmptyField {
87 field: "outbox_message.payload"
88 })
89 ));
90 }
91
92 #[test]
93 fn the_event_identity_survives_a_round_trip() {
94 let message = OutboxMessage::new(
95 EventId::new("e1").unwrap(),
96 subject(),
97 json!({ "ceremony_id": "c1" }),
98 datetime!(2026-07-29 09:00:00 UTC),
99 )
100 .unwrap();
101
102 let restored: OutboxMessage =
103 serde_json::from_value(serde_json::to_value(&message).unwrap()).unwrap();
104
105 assert_eq!(restored, message);
106 assert_eq!(restored.event_id().as_str(), "e1");
107 }
108}