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
//! # FModel Rust
//!
//! When you’re developing an information system to automate the activities of the business, you are modeling the business.
//! The abstractions that you design, the behaviors that you implement, and the UI interactions that you build all reflect
//! the business — together, they constitute the model of the domain.
//!
//! 
//
//! ## `IOR<Library, Inspiration>`
//!
//! This crate can be used as a library, or as an inspiration, or both. It provides just enough tactical Domain-Driven
//! Design patterns, optimised for Event Sourcing and CQRS.
//!
//!## Decider
//!
//! `Decider` is a datatype/struct that represents the main decision-making algorithm. It belongs to the Domain layer. It
//! has three
//! generic parameters `C`, `S`, `E` , representing the type of the values that `Decider` may contain or use.
//! `Decider` can be specialized for any type `C` or `S` or `E` because these types do not affect its
//! behavior. `Decider` behaves the same for `C`=`Int` or `C`=`YourCustomType`, for example.
//!
//! `Decider` is a pure domain component.
//!
//! - `C` - Command
//! - `S` - State
//! - `E` - Event
//!
//! ```rust
//! pub type DecideFunction<'a, C, S, E> = Box<dyn Fn(&C, &S) -> Vec<E> + 'a + Send + Sync>;
//! pub type EvolveFunction<'a, S, E> = Box<dyn Fn(&S, &E) -> S + 'a + Send + Sync>;
//! pub type InitialStateFunction<'a, S> = Box<dyn Fn() -> S + 'a + Send + Sync>;
//!
//! pub struct Decider<'a, C: 'a, S: 'a, E: 'a> {
//! pub decide: DecideFunction<'a, C, S, E>,
//! pub evolve: EvolveFunction<'a, S, E>,
//! pub initial_state: InitialStateFunction<'a, S>,
//! }
//! ```
//!
//! Additionally, `initialState` of the Decider is introduced to gain more control over the initial state of the Decider.
//!
//! ### Event-sourcing aggregate
//!
//! [aggregate::EventSourcedAggregate] is using/delegating a `Decider` to handle commands and produce new events.
//! It belongs to the
//! Application layer. In order to
//! handle the command, aggregate needs to fetch the current state (represented as a list/vector of events)
//! via `EventRepository.fetchEvents` async function, and then delegate the command to the decider which can produce new
//! events as
//! a result. Produced events are then stored via `EventRepository.save` async function.
//!
//! It is a formalization of the event sourced information system.
//!
//! ### State-stored aggregate
//!
//! [aggregate::StateStoredAggregate] is using/delegating a `Decider` to handle commands and produce new state. It
//! belongs to the
//! Application layer. In order to
//! handle the command, aggregate needs to fetch the current state via `StateRepository.fetchState` async function first,
//! and then
//! delegate the command to the decider which can produce new state as a result. New state is then stored
//! via `StateRepository.save` async function.
//!
//! ## View
//!
//! `View` is a datatype that represents the event handling algorithm, responsible for translating the events into
//! denormalized state, which is more adequate for querying. It belongs to the Domain layer. It is usually used to create
//! the view/query side of the CQRS pattern. Obviously, the command side of the CQRS is usually event-sourced aggregate.
//!
//! It has two generic parameters `S`, `E`, representing the type of the values that `View` may contain or use.
//! `View` can be specialized for any type of `S`, `E` because these types do not affect its behavior.
//! `View` behaves the same for `E`=`Int` or `E`=`YourCustomType`, for example.
//!
//! `View` is a pure domain component.
//!
//! - `S` - State
//! - `E` - Event
//!
//! ```rust
//! pub type EvolveFunction<'a, S, E> = Box<dyn Fn(&S, &E) -> S + 'a + Send + Sync>;
//! pub type InitialStateFunction<'a, S> = Box<dyn Fn() -> S + 'a + Send + Sync>;
//!
//! pub struct View<'a, S: 'a, E: 'a> {
//! pub evolve: EvolveFunction<'a, S, E>,
//! pub initial_state: InitialStateFunction<'a, S>,
//! }
//! ```
//!
//! ### Materialized View
//!
//! [materialized_view::MaterializedView] is using/delegating a `View` to handle events of type `E` and to maintain
//! a state of denormalized
//! projection(s) as a
//! result. Essentially, it represents the query/view side of the CQRS pattern. It belongs to the Application layer.
//!
//! In order to handle the event, materialized view needs to fetch the current state via `ViewStateRepository.fetchState`
//! suspending function first, and then delegate the event to the view, which can produce new state as a result. New state
//! is then stored via `ViewStateRepository.save` suspending function.
//!
//! ## FModel in other languages
//!
//! - [FModel Kotlin](https://github.com/fraktalio/fmodel/)
//! - [FModel TypeScript](https://github.com/fraktalio/fmodel-ts/)
//! - [FModel Java](https://github.com/fraktalio/fmodel-java/)
//!
//! ## Credits
//!
//! Special credits to `Jérémie Chassaing` for sharing his [research](https://www.youtube.com/watch?v=kgYGMVDHQHs)
//! and `Adam Dymitruk` for hosting the meetup.
//!
//! ---
//! Created with `love` by [Fraktalio](https://fraktalio.com/)
pub mod aggregate;
pub mod decider;
pub mod materialized_view;
pub mod view;
/// The [DecideFunction] function is used to decide which events to produce based on the command and the current state.
pub type DecideFunction<'a, C, S, E> = Box<dyn Fn(&C, &S) -> Vec<E> + 'a + Send + Sync>;
/// The [EvolveFunction] function is used to evolve the state based on the current state and the event.
pub type EvolveFunction<'a, S, E> = Box<dyn Fn(&S, &E) -> S + 'a + Send + Sync>;
/// The [InitialStateFunction] function is used to produce the initial state.
pub type InitialStateFunction<'a, S> = Box<dyn Fn() -> S + 'a + Send + Sync>;