eventually/
message.rs

1//! This module contains the definition of a [Message] type, which
2//! can be used to describe some sort of domain value such as a [Domain Event][crate::event::Envelope],
3//! a [Domain Command][crate::command::Envelope], and so on.
4
5use std::collections::HashMap;
6
7use serde::{Deserialize, Serialize};
8
9/// Represents a piece of domain data that occurs in the system.
10///
11/// Each Message has a specific name to it, which should ideally be
12/// unique within the domain you're operating in. Example: a Domain Event
13/// that represents when an Order was created can have a `name()`: `"OrderWasCreated"`.
14pub trait Message {
15    /// Returns the domain name of the [Message].
16    fn name(&self) -> &'static str;
17}
18
19/// Optional metadata to attach to an [Envelope] to provide additional context
20/// to the [Message] carried out.
21pub type Metadata = HashMap<String, String>;
22
23/// Represents a [Message] packaged for persistance and/or processing by other
24/// parts of the system.
25///
26/// It carries both the actual message (i.e. a payload) and some optional [Metadata].
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct Envelope<T>
29where
30    T: Message,
31{
32    /// The message payload.
33    pub message: T,
34    /// Optional metadata to provide additional context to the message.
35    pub metadata: Metadata,
36}
37
38impl<T> Envelope<T>
39where
40    T: Message,
41{
42    /// Adds a new entry in the [Envelope]'s [Metadata].
43    #[must_use]
44    pub fn with_metadata(mut self, key: String, value: String) -> Self {
45        self.metadata.insert(key, value);
46        self
47    }
48}
49
50impl<T> From<T> for Envelope<T>
51where
52    T: Message,
53{
54    fn from(message: T) -> Self {
55        Envelope {
56            message,
57            metadata: Metadata::default(),
58        }
59    }
60}
61
62impl<T> PartialEq for Envelope<T>
63where
64    T: Message + PartialEq,
65{
66    fn eq(&self, other: &Envelope<T>) -> bool {
67        self.message == other.message
68    }
69}
70
71#[cfg(test)]
72pub(crate) mod tests {
73    use super::*;
74
75    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
76    pub(crate) struct StringMessage(pub(crate) &'static str);
77
78    impl Message for StringMessage {
79        fn name(&self) -> &'static str {
80            "string_payload"
81        }
82    }
83
84    #[test]
85    fn message_with_metadata_does_not_affect_equality() {
86        let message = Envelope {
87            message: StringMessage("hello"),
88            metadata: Metadata::default(),
89        };
90
91        let new_message = message
92            .clone()
93            .with_metadata("hello_world".into(), "test".into())
94            .with_metadata("test_number".into(), 1.to_string());
95
96        println!("Message: {message:?}");
97        println!("New message: {new_message:?}");
98
99        // Metadata does not affect equality of message.
100        assert_eq!(message, new_message);
101    }
102}