#aggregate #event-sourcing #cqrs #ddd #pattern #architecture

macro eventmill_derive

Derive macros for convient implementation of some traits of the eventmill crate

3 releases (breaking)

0.3.0 May 17, 2020
0.2.0 May 16, 2020
0.1.0 May 10, 2020

#39 in #ddd


Used in eventmill

MIT/Apache

20KB
253 lines

eventmill-derive  

Latest Release Documentation License

Derive macros for convenient use of the eventmill crate

You can bring in the macros in two ways. The recommended way by using the derive feature of the main crate:

[dependencies]
eventmill = { version = "0.3", features = ["derive"] }

or the alternative way:

[dependencies]
eventmill = "0.3"
eventmill_derive = "0.3"

The examples below assume you bring in the macros the recommended way using the derive feature of the eventmill crate.

#[derive(EventType)]

The #[derive(EventType)] macro implements the EventType trait for your events. An Event can be a struct or enum. We can configure the generated implementation by using the optional attributes event_type, event_type_version and event_source.

Here are some examples:

Implementing an event using a struct and specifying all available attributes.

use eventmill::EventType;

#[derive(EventType, Debug)]
#[event_type_version("V2")]
#[event_source("https://github.com/innoave/eventmill/examples/turtle")]
#[event_type("turtle-turned")]
pub struct TurtleTurned {
    angle: f32,
}

fn main() {
    let turtle = TurtleTurned { angle: 0.42 };

    assert_eq!(turtle.event_type_version(), "V2");
    assert_eq!(
        turtle.event_source(),
        "https://github.com/innoave/eventmill/examples/turtle"
    );
    assert_eq!(turtle.event_type(), "turtle-turned");
}

Implementing an event using an enum and specifying all available attributes.

use eventmill::EventType;

#[derive(EventType, Debug)]
#[event_type_version("V2")]
#[event_source("https://github.com/innoave/eventmill/examples/turtle")]
pub enum Turtle {
    #[event_type("turtle-turned")]
    Turned(f32),
    #[event_type("turtle-moved")]
    Moved { x: i32, y: i32 },
    #[event_type("turtle-stopped")]
    Stopped,
}

fn main() {
    let turtle = Turtle::Stopped;

    assert_eq!(turtle.event_type_version(), "V2");
    assert_eq!(
        turtle.event_source(),
        "https://github.com/innoave/eventmill/examples/turtle"
    );
    assert_eq!(turtle.event_type(), "turtle-stopped");

    let turtle = Turtle::Turned(0.42);
    assert_eq!(turtle.event_type(), "turtle-turned");

    let turtle = Turtle::Moved { x: 4, y: 2 };
    assert_eq!(turtle.event_type(), "turtle-moved");
}

If we omit any or all of the attributes the macro uses default values. Note the default names returned by the event_type() function.

use eventmill::EventType;

#[derive(EventType, Debug)]
pub enum Turtle {
    Turned(f32),
    Moved { x: i32, y: i32 },
    Stopped,
}

fn main() { 
    let turtle = Turtle::Turned(0.42);
    assert_eq!(turtle.event_type(), "Turtle::Turned");
    
    let turtle = Turtle::Moved { x: 4, y: 2 };
    assert_eq!(turtle.event_type(), "Turtle::Moved");
    
    let turtle = Turtle::Stopped;
    assert_eq!(turtle.event_type(), "Turtle::Stopped");
}

We can use any expression that evaluates to &str as the values of attributes. E.g. use a const for defining the event_source attribute.

use eventmill::EventType;

const EVENT_NAMESPACE: &str = "https://github.com/innoave/eventmill/examples/turtle";

#[derive(EventType, Debug)]
#[event_source(EVENT_NAMESPACE)]
pub struct TurtleTurned {
    angle: f32,
}

fn main() {
    let turtle = TurtleTurned { angle: 0.42 };

    assert_eq!(turtle.event_type_version(), "V0");
    assert_eq!(turtle.event_source(), EVENT_NAMESPACE);
    assert_eq!(turtle.event_type(), "TurtleTurned");
}

#[derive(AggregateType)]

The #[derive(AggregateType)] macro implements the AggregateType trait for your aggregate types. Currently, this macro can be used only on struct types. We can configure the macro with the two optional attributes #[id_field] and #[initialize_with_defaults].

When the #[id_field] attribute is specified the macro will additionally implement the WithAggregateId trait. This attribute has one parameter which is the identifier of the id field in the struct.

The #[initialize_with_defaults] attribute tells the macro to implement the InitializeAggregate trait using the default values for each field in the struct. This assumes that types used in fields of the struct implement the Default trait. The #[initialize_with_defaults] attribute requires that the #[id_field] attribute is specified for the struct as well.

Here are some examples:

Derive implementations for the traits AggregateType, WithAggregateId and InitializeAggregate using the AggregateType macro with all optional attributes:

use eventmill::{AggregateType, InitializeAggregate, WithAggregateId};

#[derive(AggregateType, Debug, PartialEq)]
#[id_field(id)]
#[initialize_with_defaults]
pub struct Turtle {
    id: String,
    x: f32,
    y: f32,
    direction: f32,
    speed: f32,
    pen: bool,
}

#[test]
fn main() {
    let expected_turtle = Turtle {
        id: "4711".to_string(),
        x: Default::default(),
        y: Default::default(),
        direction: Default::default(),
        speed: Default::default(),
        pen: Default::default(),
    };

    let new_turtle = Turtle::initialize("4711".to_string());

    assert_eq!(new_turtle, expected_turtle);
    assert_eq!(new_turtle.aggregate_id(), "4711");
    assert_eq!(Turtle::aggregate_type(), "Turtle");
}

Only derive the implementation for AggregateType. No additional attributes are to be specified:

use eventmill::AggregateType;


#[derive(AggregateType, Debug)]
pub struct Turtle {
    id: String,
    x: f32,
    y: f32,
    direction: f32,
    speed: f32,
    pen: bool,
}

#[test]
fn main() {
    assert_eq!(Turtle::aggregate_type(), "Turtle");
}

Derive the implementation of the traits AggregateType and WithAggregateId:

use eventmill::{AggregateType, WithAggregateId};

#[derive(AggregateType, Debug)]
#[id_field(id)]
pub struct Turtle {
    id: String,
    x: f32,
    y: f32,
    direction: f32,
    speed: f32,
    pen: bool,
}

#[test]
fn main() {
    let turtle = Turtle {
        id: "0815".to_string(),
        x: -0.5,
        y: 0.3,
        direction: 0.42,
        speed: 1.0,
        pen: true,
    };

    assert_eq!(turtle.aggregate_id(), "0815");
    assert_eq!(Turtle::aggregate_type(), "Turtle");
}

Dependencies

~1.5MB
~36K SLoC