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.
//!
//! ![event-modeling](https://github.com/fraktalio/fmodel-ts/raw/main/.assets/event-modeling.png)
//
//! ## `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>;