ddd-toolkit-core 0.2.2

Traits and reference implementations for Domain-Driven Design building blocks: entities, value objects, aggregates, repositories, and event dispatch.
Documentation
use crate::domain::{DomainEvent, Entity};

/// An [`Entity`] that is a consistency boundary and the unit of persistence:
/// changes to it (and whatever it contains) are saved and loaded together,
/// and it records the domain events those changes produce.
///
/// # Examples
///
/// ```
/// use ddd_toolkit_core::domain::{AggregateRoot, DomainEvent, Entity, EntityId, ValueObject};
/// use std::fmt::Display;
///
/// #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
/// struct OrderId(u32);
///
/// impl ValueObject for OrderId {}
///
/// impl Display for OrderId {
///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
///         write!(f, "order-{}", self.0)
///     }
/// }
///
/// impl EntityId for OrderId {}
///
/// #[derive(Debug, PartialEq)]
/// enum OrderEvent {
///     Placed,
/// }
///
/// impl DomainEvent for OrderEvent {}
///
/// struct Order {
///     id: OrderId,
///     events: Vec<OrderEvent>,
/// }
///
/// impl Entity for Order {
///     type Id = OrderId;
///
///     fn id(&self) -> &Self::Id {
///         &self.id
///     }
/// }
///
/// impl AggregateRoot for Order {
///     type Event = OrderEvent;
///
///     fn record(&mut self, event: Self::Event) {
///         self.events.push(event);
///     }
///
///     fn take_events(&mut self) -> Vec<Self::Event> {
///         std::mem::take(&mut self.events)
///     }
/// }
///
/// let mut order = Order { id: OrderId(1), events: Vec::new() };
/// order.record(OrderEvent::Placed);
///
/// // draining hands ownership of the recorded events to the caller (e.g. a
/// // repository, to publish after a successful save) and clears the buffer
/// assert_eq!(order.take_events(), vec![OrderEvent::Placed]);
/// assert_eq!(order.take_events(), vec![]);
/// ```
pub trait AggregateRoot: Entity {
    /// The domain event type this aggregate records.
    type Event: DomainEvent;

    /// Records that `event` happened, to be drained later via
    /// [`AggregateRoot::take_events`].
    fn record(&mut self, event: Self::Event);

    /// Returns and clears every event recorded since the last call.
    fn take_events(&mut self) -> Vec<Self::Event>;
}

#[cfg(test)]
mod test {
    use std::fmt::Display;

    use crate::domain::{EntityId, ValueObject};

    use super::*;

    #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
    struct FooId(String);

    impl ValueObject for FooId {}

    impl Display for FooId {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            write!(f, "{}", self.0)
        }
    }

    impl EntityId for FooId {}

    #[derive(Debug, PartialEq)]
    struct FooEvent {
        name: String,
    }

    impl DomainEvent for FooEvent {}

    fn event(name: &str) -> FooEvent {
        FooEvent {
            name: name.to_string(),
        }
    }

    struct Foo {
        id: FooId,
        events: Vec<FooEvent>,
    }

    impl Foo {
        fn new(id: &str) -> Self {
            Self {
                id: FooId(id.to_string()),
                events: Vec::new(),
            }
        }
    }

    impl Entity for Foo {
        type Id = FooId;

        fn id(&self) -> &Self::Id {
            &self.id
        }
    }

    impl AggregateRoot for Foo {
        type Event = FooEvent;

        fn record(&mut self, event: Self::Event) {
            self.events.push(event);
        }

        fn take_events(&mut self) -> Vec<Self::Event> {
            std::mem::take(&mut self.events)
        }
    }

    #[test]
    fn record_single_event_take_events_returns_it() {
        let mut foo = Foo::new("foo-1");

        foo.record(event("created"));

        assert_eq!(foo.take_events(), vec![event("created")]);
    }

    #[test]
    fn record_multiple_events_take_events_returns_in_recorded_order() {
        let mut foo = Foo::new("foo-1");

        foo.record(event("created"));
        foo.record(event("updated"));
        foo.record(event("deleted"));

        assert_eq!(
            foo.take_events(),
            vec![event("created"), event("updated"), event("deleted")]
        );
    }

    #[test]
    fn take_events_clears_recorded_events() {
        let mut foo = Foo::new("foo-1");
        foo.record(event("created"));

        foo.take_events();

        assert_eq!(foo.take_events(), vec![]);
    }

    #[test]
    fn record_after_take_events_returns_only_new_events() {
        let mut foo = Foo::new("foo-1");
        foo.record(event("created"));
        foo.take_events();

        foo.record(event("updated"));

        assert_eq!(foo.take_events(), vec![event("updated")]);
    }

    #[test]
    fn aggregate_root_impl_satisfies_entity_supertrait() {
        let a = Foo::new("foo-1");
        let b = Foo::new("foo-1");
        let c = Foo::new("foo-2");

        assert_eq!(a.id(), &FooId("foo-1".to_string()));
        assert!(a.is_same_as(&b));
        assert!(!a.is_same_as(&c));
    }

    #[test]
    fn aggregate_root_impl_satisfies_send_and_sync() {
        fn assert_impl<T: Send + Sync>() {}
        assert_impl::<Foo>();
    }

    #[test]
    fn generic_fn_accepts_aggregate_root_impl() {
        fn drain<T: AggregateRoot>(aggregate: &mut T, event: T::Event) -> Vec<T::Event> {
            aggregate.record(event);
            aggregate.take_events()
        }

        let mut foo = Foo::new("foo-1");

        assert_eq!(drain(&mut foo, event("created")), vec![event("created")]);
    }

    #[test]
    fn take_events_on_fresh_aggregate_returns_empty_vec() {
        let mut foo = Foo::new("foo-1");

        assert_eq!(foo.take_events(), vec![]);
    }

    #[test]
    fn record_duplicate_events_are_both_returned() {
        let mut foo = Foo::new("foo-1");

        foo.record(event("created"));
        foo.record(event("created"));

        assert_eq!(foo.take_events(), vec![event("created"), event("created")]);
    }
}