Skip to main content

sourcery_core/
event.rs

1//! Domain event marker.
2//!
3//! `DomainEvent` is the lightweight trait every concrete event struct
4//! implements. It intentionally avoids persistence concerns; serialisation is
5//! handled by the event stores.
6
7use thiserror::Error;
8
9/// Error returned when deserialising a stored event fails.
10#[derive(Debug, Error)]
11pub enum EventDecodeError<StoreError> {
12    /// The event kind was not recognised by this event enum.
13    #[error("unknown event kind `{kind}`, expected one of {expected:?}")]
14    UnknownKind {
15        /// The unrecognised event kind string.
16        kind: String,
17        /// The list of event kinds this enum can handle.
18        expected: &'static [&'static str],
19    },
20    /// The store failed to deserialise the event data.
21    #[error("store error: {0}")]
22    Store(#[source] StoreError),
23}
24
25/// Marker trait for events that can be persisted by the event store.
26///
27/// Each event carries a unique [`Self::KIND`] identifier so the repository can
28/// route stored bytes back to the correct type when rebuilding aggregates or
29/// projections.
30///
31/// Most projects implement this trait by hand, but the `#[derive(Aggregate)]`
32/// macro generates it automatically for the aggregate event enums it creates.
33pub trait DomainEvent {
34    const KIND: &'static str;
35}
36
37/// Extension trait for getting the event kind from an event instance.
38///
39/// This trait has a blanket implementation for all types that implement
40/// [`DomainEvent`], ensuring that the `kind()` method always returns the
41/// same value as the `KIND` constant.
42///
43/// # Relationship to `DomainEvent`
44///
45/// You should implement [`DomainEvent`] on your event types. This trait
46/// (`EventKind`) is automatically derived from `DomainEvent` and provides
47/// an instance method `kind()` that returns the same value as the `KIND`
48/// constant.
49///
50/// **You never need to implement this trait yourself** - it's automatically
51/// available on any type that implements `DomainEvent`.
52pub trait EventKind {
53    fn kind(&self) -> &'static str;
54}
55
56impl<T: DomainEvent> EventKind for T {
57    fn kind(&self) -> &'static str {
58        T::KIND
59    }
60}
61
62/// Trait for event sum types that can deserialise themselves from stored
63/// events.
64///
65/// Implemented by event enums to deserialise stored events.
66///
67/// Deriving [`Aggregate`](crate::aggregate::Aggregate) includes a
68/// `ProjectionEvent` implementation for the generated sum type. Custom enums
69/// can opt in manually using the pattern illustrated below.
70pub trait ProjectionEvent: Sized {
71    /// The list of event kinds this sum type can deserialise.
72    ///
73    /// This data is generated automatically when using the derive macros.
74    const EVENT_KINDS: &'static [&'static str];
75
76    /// Deserialise an event from stored representation.
77    ///
78    /// # Errors
79    ///
80    /// Returns [`EventDecodeError::UnknownKind`] if the event kind is not
81    /// recognised, or [`EventDecodeError::Store`] if deserialisation fails.
82    fn from_stored<S: crate::store::EventStore>(
83        stored: &crate::store::StoredEvent<S::Id, S::Position, S::Data, S::Metadata>,
84        store: &S,
85    ) -> Result<Self, EventDecodeError<S::Error>>;
86}