4 releases (breaking)

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

#879 in Rust patterns

MIT/Apache

62KB
1.5K SLoC

eventmill  

Latest Release Documentation License Rustc Support 1.39+ Build Status

Event sourcing and CQRS for Rust applications

very much work in progress!

[dependencies]
eventmill = "0.4"

to use the derive macros from the eventmill-derive crate activate the derive feature:

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

Usage example

Define your domain events:

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

#[derive(EventType, Debug, Clone, PartialEq)]
#[event_source(EVENT_NAMESPACE)]
#[event_type_version("V1")]
#[event_type("Incremented")]
struct Incremented;

Define your aggregate:

#[derive(AggregateType, Debug)]
#[id_field(id)]
#[initialize_with_defaults]
struct Counter {
    id: i32,
    hits: u64,
}

Implement the business logic for applying events to the aggregate:

impl Aggregate<Incremented> for Counter {
    fn apply_event(&mut self, _event: &DomainEvent<Incremented, Self>) {
        self.hits += 1;
    }
}

Define a command:

#[derive(Debug, PartialEq)]
struct Increment;

Implement the business logic so that the aggregate is able to handle the command:

impl HandleCommand<Increment, Self> for Counter {
    type Event = Incremented;
    type Error = Infallible;
    type Context = ();

    fn handle_command(
        &self,
        _command: Increment,
        _context: &Self::Context,
    ) -> Result<Vec<NewEvent<Self::Event, Counter>>, Self::Error> {
        Ok(vec![NewEvent {
            aggregate_id: self.id,
            data: Incremented,
        }])
    }
}

Bringing it all together using the Core dispatcher:

fn main() {
    let event_store = InMemoryStore::new();
    let core = Core::new(event_store);

    let aggregate_id = 4711;

    let increment_command = DomainCommand {
        aggregate_id,
        aggregate_generation: Generation::default(),
        data: Increment,
    };

    let versioned_counter: VersionedAggregate<Counter> = core
        .dispatch_command(increment_command, &())
        .expect("counter incremented");

    assert_eq!(versioned_counter.state().hits, 1);
}

These code samples are taken from the counter example. Take a look at eventmill-examples for more insights.

TODO

  • define basic abstractions and API
  • provide a first example on how it looks like to use the API
  • make more examples to polish the API
  • write rust-doc for the API
  • support async/await or switch to async as the only option
  • consider providing default implementations for eventstores and other building blocks
  • ...

Dependencies

~1.3–2.2MB
~40K SLoC