1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
#![allow(clippy::module_inception)]
//! `Chekov` is a CQRS/ES Framework built on top of Actix actor framework.
//!
//! ## Getting started
//!
//! ### Installation
//!
//! ```toml
//! chekov = "0.1"
//! ```
//!
//! `Chekov` is using [`::event_store`] in background to interact with backend.
//!
//! ### Application
//!
//! `Chekov` is using `Application` as a way to separate usecases. You can defined multiple
//! applications on the same runtime which can be useful to synchronise multiple domains.
//!
//! But first, let's define our first `Application`:
//!
//! ```rust
//! #[derive(Default)]
//! struct DefaultApp {}
//!
//! // Application trait is here to preconfigure your chekov runtime.
//! // It tells that you want this `Application` to use a PostgresStorage.
//! impl chekov::Application for DefaultApp {
//! type Storage = chekov::prelude::PostgresStorage;
//! }
//! ```
//!
//! If you want to know more about [`Application`] check the module documentation.
//!
//! ### Defining our first `Aggregate`
//!
//! One can create an aggregate that will be able to hold a state and execute commands on demand.
//! Each `Aggregate` is unique and can't be duplicated meaning that you can't have the same
//! aggregate instance running twice.
//!
//! ```rust
//! #[derive(chekov::Aggregate, Default, Clone)]
//! #[aggregate(identity = "user")]
//! struct User {
//! user_id: uuid::Uuid,
//! name: String
//! }
//!
//! ```
//!
//! If you want to know more about [`aggregate`] check the module documentation.
//!
//! ### Creating our first `Event`
//!
//! An `Aggregate` produces events based on command execution. Defining an `Event` is pretty
//! straightforward.
//!
//! ```rust
//! # use serde::{Deserialize, Serialize};
//! #[derive(Clone, chekov::Event, Deserialize, Serialize)]
//! struct UserCreated{}
//!
//! #[derive(Clone, chekov::Event, Deserialize, Serialize)]
//! enum UserUpdated {
//! NamedChanged(uuid::Uuid, String, String),
//! Disabled {
//! reason: String
//! }
//! }
//!
//! ```
//!
//! If you want to know more about [`Event`] check the module documentation.
//!
//! ### Defining our first `Command`
//!
//! The purpose of a command is to been executed on an `Aggregate` (or a `CommandHandler` but we
//! will see that later). Each commands need to provide some basic information on how to deal with
//! them. But let's keep that simple for now:
//!
//! ```rust
//! # use chekov::prelude::*;
//! # use chekov::prelude::CommandHandler;
//! # use uuid::Uuid;
//! # use serde::{Deserialize, Serialize};
//! #
//! # #[derive(chekov::Aggregate, Default, Clone)]
//! # #[aggregate(identity = "user")]
//! # struct User {
//! # user_id: uuid::Uuid,
//! # name: String
//! # }
//! # #[derive(Clone, chekov::Event, Deserialize, Serialize)]
//! # struct UserCreated{}
//! # #[derive(Clone, chekov::Event, Deserialize, Serialize)]
//! # enum UserUpdated {
//! # NamedChanged(uuid::Uuid, String, String),
//! # Disabled {
//! # reason: String
//! # }
//! # }
//! # impl<T> CommandExecutor<T> for User where T: Command {
//! # fn execute(cmd: T, state: &Self) -> Result<Vec<T::Event>, CommandExecutorError> {
//! # Ok(vec![])
//! # }
//! # }
//! # impl<T> EventApplier<T> for User where T: chekov::Event {
//! # fn apply(&mut self, event: &T) -> Result<(), ApplyError> {
//! # Ok(())
//! # }
//! # }
//! # #[derive(Default, CommandHandler)]
//! # pub struct UserValidator{}
//! # use futures::future::BoxFuture;
//! # use futures::FutureExt;
//! # impl<T> chekov::command::Handler<T, User> for UserValidator where T: Command {
//! # fn handle(&mut self, command: T, state: chekov::aggregate::StaticState<User>) -> BoxFuture<'static, ExecutionResult<T::Event>> {
//! # let events = User::execute(command, &state);
//!
//! # async { events }.boxed()
//! # }
//! # }
//! #[derive(Clone, Debug, chekov::Command, Serialize, Deserialize)]
//! #[command(event = "UserCreated", aggregate = "User", handler = "UserValidator")]
//!struct CreateUser {
//! #[command(identifier)]
//! pub user_id: Uuid,
//! pub name: String,
//! }
//!
//! #[derive(Clone, Debug, chekov::Command, Serialize, Deserialize)]
//! #[command(event = "UserUpdated", aggregate = "User")]
//! struct UpdateUser {
//! #[command(identifier)]
//! pub user_id: Uuid,
//! pub name: String,
//! }
//!
//! ```
pub mod aggregate;
pub mod application;
pub mod command;
pub mod error;
pub mod event;
pub mod event_store;
#[doc(hidden)]
pub mod message;
pub mod prelude;
mod router;
mod subscriber;
#[doc(hidden)]
pub use lazy_static::lazy_static;
pub use ::event_store::prelude::RecordedEvent;
#[doc(inline)]
pub use aggregate::Aggregate;
#[doc(inline)]
pub use application::Application;
#[doc(inline)]
pub use application::ApplicationBuilder;
#[doc(inline)]
pub use command::Command;
#[doc(inline)]
pub use command::CommandHandler;
use error::CommandExecutorError;
#[doc(inline)]
pub use event::Event;
#[doc(inline)]
pub use event::EventApplier;
#[doc(inline)]
pub use event::EventHandler;
use message::Dispatch;
use router::Router;
pub use subscriber::SubscriberManager;
pub use chekov_macros::applier;
pub use chekov_macros::command_handler;
pub use chekov_macros::event_handler;
pub use chekov_macros::Aggregate;
pub use chekov_macros::Command;
pub use chekov_macros::CommandHandler;
pub use chekov_macros::Event;
pub use chekov_macros::EventHandler;
#[doc(hidden)]
pub use async_trait;
#[doc(hidden)]
pub use inventory;
#[cfg(test)]
mod tests;