causality/
lib.rs

1//! Traits for implementing `Event Driven Architectures`.
2//!
3//! Borrowing with fervor from the `Theory of Causality` to conceptualize deterministic state.
4//!
5//! Ideas represented are often a reflection off of the work of others (`Causality`, `Event Sourcing`, `CQRS`, etc).
6//!
7//! **CAUTION:** Implementation hasn't had time to mature. Expect breaking changes.
8//!
9//! # Example
10//!
11//! ```
12//! use simple_error::SimpleError;
13//! use causality::{Actor, Cause, Effect};
14//!
15//! enum Command {
16//!     TestDoor {actor_id: u32, actor_version: u8},
17//! }
18//!
19//! impl Cause for Command {
20//!     type ActorId = u32;
21//!     type ActorVersion = u8;
22//!     fn actor_id(&self) -> Self::ActorId {
23//!         match self {
24//!             Command::TestDoor {actor_id, ..} => {
25//!                 *actor_id
26//!             }
27//!         }
28//!     }
29//!     fn actor_version(&self) -> Self::ActorVersion {
30//!         match self {
31//!             Command::TestDoor {actor_version, ..} => {
32//!                 *actor_version
33//!             }
34//!         }
35//!     }
36//! }
37//!
38//! enum Event {
39//!     Opened {version: u8, key: u32},
40//!     Closed {version: u8, key: u32}
41//! }
42//!
43//! impl Effect for Event {
44//!     type Version = u8;
45//!     type Key = u32;
46//!     fn version(&self) -> Self::Version {
47//!         match self {
48//!             Event::Opened {version, ..} |
49//!             Event::Closed {version, ..} => {
50//!                 *version
51//!             }
52//!         }
53//!     }
54//!     fn key(&self) -> Self::Key {
55//!         match self {
56//!             Event::Opened {key, ..} |
57//!             Event::Closed {key, ..} => {
58//!                 *key
59//!             }
60//!         }
61//!     }
62//! }
63//!
64//! struct Door {
65//!     id: u32,
66//!     version: u8
67//! }
68//!
69//! impl Actor<Command, Event, SimpleError> for Door {
70//!     type Id = u32;
71//!     type Version = u8;
72//!     fn handle(&self, command: Command) -> Result<Vec<Event>, SimpleError> {
73//!         match command {
74//!             Command::TestDoor {actor_id, actor_version} => {
75//!                 return Ok(vec![
76//!                     Event::Opened {version: 1, key: 1},
77//!                     Event::Closed {version: 1, key: 2}
78//!                 ]);
79//!             }
80//!         }
81//!         Err(SimpleError::new("command should be found due to enum type"))
82//!     }
83//!     fn apply(&mut self, effects: Vec<Event>) -> Result<(), SimpleError> {
84//!         for effect in effects {
85//!             match effect {
86//!                 Event::Opened {key, ..} |
87//!                 Event::Closed {key, ..} => {
88//!                     self.version = key as u8;
89//!                 }
90//!             }
91//!         }
92//!         Ok(())
93//!     }
94//! }
95//!
96//! let mut door = Door {
97//!     id: 1,
98//!     version: 1
99//! };
100//! let command = Command::TestDoor {
101//!     actor_id: 1,
102//!     actor_version: 1
103//! };
104//! let events = door.handle(command).unwrap();
105//! assert_eq!(events.len(), 2);
106//! let result = door.apply(events);
107//! assert!(result.is_ok());
108//! ```
109
110use std::error::Error;
111
112/// Entity that handles `Causes` producing one or more `Effects` upon success.
113///
114/// Implemented for `Root Aggregates` or `Aggregates` in `Event Sourcing`.
115pub trait Actor<C: Cause, E: Effect, Err: Error> {
116    /// Unique Id for `Actor`.
117    type Id;
118    /// Version of `Actor` dependent on `Effects` applied.
119    type Version;
120    /// Handle `Cause` returning vector of `Effects` or error.
121    fn handle(&self, cause: C) -> Result<Vec<E>, Err>;
122    /// Apply `Effects` on Actor.
123    fn apply(&mut self, effects: Vec<E>) -> Result<(), Err>;
124}
125
126/// Action performed on `Actor` that is expected to produce `Effects`.
127///
128/// Implemented for actions handled by `Actors`.
129pub trait Cause {
130    /// Unique `Actor` Id or aggregate key.
131    type ActorId;
132    /// Version of `Actor` handling `Cause` for ordering (optimistic concurrency or staleness).
133    type ActorVersion;
134    /// Returns unique `Actor` Id.
135    fn actor_id(&self) -> Self::ActorId;
136    /// Returns `Actor` version.
137    fn actor_version(&self) -> Self::ActorVersion;
138}
139
140/// Event produced from `Actor` handling `Cause`.
141///
142/// Implemented for events produced by `Actors` handling `Causes`.
143///
144/// Expected that `Effects` are applied to `Actors` or `Aggregates` to represent state.
145pub trait Effect {
146    /// Schema version use to maintain backwards compatibility.
147    type Version;
148    /// Unique key used for idempotency (duplicate detection).
149    type Key;
150    /// Returns version.
151    fn version(&self) -> Self::Version;
152    /// Returns unique key.
153    fn key(&self) -> Self::Key;
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    use simple_error::SimpleError;
161
162    type Id = String;
163    type Version = String;
164    type Key = String;
165
166    struct Command {
167        actor_id: Id,
168        actor_version: Version,
169    }
170    impl Cause for Command {
171        type ActorId = Id;
172        type ActorVersion = Version;
173        fn actor_id(&self) -> Self::ActorId { self.actor_id.clone() }
174        fn actor_version(&self) -> Self::ActorVersion { self.actor_version.clone() }
175    }
176
177    struct Event {
178        version: Version,
179        key: Key,
180    }
181    impl Effect for Event {
182        type Version = Version;
183        type Key = Key;
184        fn version(&self) -> Self::Version { self.version.clone() }
185        fn key(&self) -> Self::Key { self.key.clone() }
186    }
187
188    #[test]
189    fn actor_handles_cause_returning_effect() {
190        let command = Command {
191            actor_id: String::from("one"),
192            actor_version: String::from("two")
193        };
194        #[allow(dead_code)]
195        struct Aggregate {
196            id: Id,
197            version: Version
198        }
199        impl Actor<Command, Event, SimpleError> for Aggregate {
200            type Id = Id;
201            type Version = Version;
202            fn handle(&self, command: Command) -> Result<Vec<Event>, SimpleError> {
203                if command.actor_id() == String::from("one") {
204                    return Ok(vec![
205                        Event {
206                            version: String::from("1.0.0"),
207                            key: String::from("alpha-1234")
208                        }
209                    ]);
210                }
211                Err(SimpleError::new("should have actor id one"))
212            }
213            fn apply(&mut self, _effects: Vec<Event>) -> Result<(), SimpleError> {
214                Err(SimpleError::new("shouldn't be called"))
215            }
216        }
217        let aggregate = Aggregate {
218            id: String::from("alpha"),
219            version: String::from("one")
220        };
221        let events = aggregate.handle(command);
222
223        assert!(events.is_ok());
224        assert_eq!(events.unwrap().len(), 1);
225    }
226
227    #[test]
228    fn actor_handles_cause_returning_error() {
229        let command = Command {
230            actor_id: String::from("one"),
231            actor_version: String::from("two")
232        };
233        #[allow(dead_code)]
234        struct Aggregate {
235            id: Id,
236            version: Version
237        }
238        impl Actor<Command, Event, SimpleError> for Aggregate {
239            type Id = Id;
240            type Version = Version;
241            fn handle(&self, _command: Command) -> Result<Vec<Event>, SimpleError> {
242                Err(SimpleError::new("should have actor id one"))
243            }
244            fn apply(&mut self, _effects: Vec<Event>) -> Result<(), SimpleError> {
245                Err(SimpleError::new("shouldn't be called"))
246            }
247        }
248        let aggregate = Aggregate {
249            id: String::from("alpha"),
250            version: String::from("one")
251        };
252        let events = aggregate.handle(command);
253
254        assert!(events.is_err());
255    }
256
257    #[test]
258    fn actor_apply_effect_returning_ok() {
259        let event = Event {
260            version: String::from("1.0.0"),
261            key: String::from("alpha-1234")
262        };
263        #[allow(dead_code)]
264        struct Aggregate {
265            id: Id,
266            version: Version
267        }
268        impl Actor<Command, Event, SimpleError> for Aggregate {
269            type Id = Id;
270            type Version = Version;
271            fn handle(&self, _command: Command) -> Result<Vec<Event>, SimpleError> {
272                Err(SimpleError::new("shouldn't be called"))
273            }
274            fn apply(&mut self, effects: Vec<Event>) -> Result<(), SimpleError> {
275                if effects.len() == 1 {
276                    return Ok(());
277                }
278                Err(SimpleError::new("should have single effect"))
279            }
280        }
281        let mut aggregate = Aggregate {
282            id: String::from("alpha"),
283            version: String::from("one")
284        };
285        let result = aggregate.apply(vec![event]);
286
287        assert!(result.is_ok());
288    }
289
290    #[test]
291    fn actor_apply_effect_returning_error() {
292        #[allow(dead_code)]
293        struct Aggregate {
294            id: Id,
295            version: Version
296        }
297        impl Actor<Command, Event, SimpleError> for Aggregate {
298            type Id = Id;
299            type Version = Version;
300            fn handle(&self, _command: Command) -> Result<Vec<Event>, SimpleError> {
301                Err(SimpleError::new("shouldn't be called"))
302            }
303            fn apply(&mut self, effects: Vec<Event>) -> Result<(), SimpleError> {
304                if effects.len() == 1 {
305                    return Ok(());
306                }
307                Err(SimpleError::new("should have zero effects"))
308            }
309        }
310        let mut aggregate = Aggregate {
311            id: String::from("alpha"),
312            version: String::from("one")
313        };
314        let result = aggregate.apply(vec![]);
315
316        assert!(result.is_err());
317    }
318}