Giter Site home page Giter Site logo

chekov's Introduction

Chekov

A CQRS/ES framework for building application in Rust

Actions Status Coverage Status dependency status Crates.io doc.rs doc-latest

Table of Contents


Features

  • Postgres EventStore backend
  • Dispatch Command to Aggregate or CommandHandler
  • Generate Event from Aggregate and persist them
  • Apply Event to Aggregate
  • Store and notify Event with subscriptions
  • Dispatch Event to EventHandler

Getting started

Choosing an EventStore backend

Chekov works only with Postgres backend for now (and InMemory for test purpose). The choice is easy to make!

But some more backends need to be implemented, see the related issue.

Defining Aggregates

An Aggregate is a struct that hold a domain state. Here's an example of a UserAggregate:

#[derive(Default, Aggregate)]
#[aggregate(identity = "user")]
struct User {
    user_id: Option<Uuid>,
    account_id: Option<Uuid>,
}

/// Define an Executor for the `CreateUser` command
/// The result is a list of events in case of success
impl CommandExecutor<CreateUser> for User {
  fn execute(cmd: CreateUser, _state: &Self) -> Result<Vec<UserCreated>, CommandExecutorError> {
    Ok(vec![UserCreated {
      user_id: cmd.user_id,
      account_id: cmd.account_id,
    }])
  }
}

/// Define an Applier for the `UserCreated` event
/// Applier is a mutation action on the aggregate
#[chekov::applier]
impl EventApplier<UserCreated> for User {
  fn apply(&mut self, event: &UserCreated) -> Result<(), ApplyError> {
    self.user_id = Some(event.user_id);
    self.account_id = Some(event.account_id);

    Ok(())
  }
}

Defining Commands

You need to create a struct per command, any type of struct can implement Command but we advise to use struct for a better readability.

A command can only produce (or not) one type of events and it targets a single Aggregate. A command must have a single and unique identifier that is used to route the command to the right target.

#[derive(Debug, Command)]
#[command(event = "UserCreated", aggregate = "User")]
struct CreateUser {
    #[command(identifier)]
    user_id: Uuid,
    account_id: Uuid,
}

Defining Events

An Event can be a struct or an enum.

#[derive(Event, Deserialize, Serialize)]
struct UserCreated {
    user_id: Uuid,
    account_id: Uuid,
}

Defining Saga

Not implemented yet

FAQ

Does Chekov is production ready ?

No its not. Some critical part of the project are still not implemented and a lot of code needs to be refactored before that.

Need Help?

Feel free to open issue in case of bugs or features requests. Discussions are also a great starts if you have issue that are not bugs nor features requests.

Contributing

The project is really early staged and have a lot of pending tasks, one major tasks is to produce a roadmap or some issues that can be used to expose the project vision. Feel free to open a Discussions around it if you want !

chekov's People

Contributors

freyskeyd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

chekov's Issues

[Proposal] EventStore backends

Here's a list of potential implementation of EventStore backends:

  • Better InMemory Store.
  • Greg Young's Event Store.
  • Sled Store

Feel free to suggest some!

[Listener] Improve configurability

The Listener is responsible of listening for events that are appended to a stream and forward them to the SubscriberManager to notify every EventHandler interested.

The fact is that the Listener isn't really linked to the Storage. For now we are using PGNotify as a notifier but it could be any message queuing components such as kafka, etc.

In order to be able to configure the Listener of an application we need to move it into the EventStore crate where it belongs. We don't need to have a strong coupling between Listener and Backend because they can be different engines.

Listeners could be:

  • A message broker
  • Websockets exchange between applications
  • HTTP calls between applications

The work on this is still in progress, feel free to share ideas or suggestions

[Command] Allow enum and newtype

Command can already be use with enum and newtype but the Command derive only support struct.

#[derive(Debug)]
struct AppendItem(i64);

impl Command for AppendItem {
    type Event = ItemAppended;

    type Executor = ExampleAggregate;

    type ExecutorRegistry = AggregateInstanceRegistry<ExampleAggregate>;

    fn identifier(&self) -> String {
        "example_aggregate".to_string()
    }
}

It could be possible to have an enum which can be dispatch to the CommandExecutor.

Some questions need to be resolved first:

  • How to deal with command's identity ?
  • Is it really necessary ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.