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}