fj_kernel/services/service.rs
1use std::ops::Deref;
2
3/// A service that controls access to some state
4///
5/// `Service` is a generic wrapper around some state, as well as code that knows
6/// how to operate on that state. It processes commands, changes the state based
7/// on those command, and produces events that capture these changes. These
8/// events are stored, providing a log of all changes to the state, and can be
9/// replayed later to re-create the state at any point in time.
10///
11/// The wrapped state must implement [`State`], which defines the type of
12/// command that this service processes, and the type of event that captures
13/// state changes. It also defines methods that operate on the state, commands,
14/// and events.
15///
16/// Implementations of [`State`] might also define an extension trait for a
17/// specific `Service<MyState>`, to provide a convenient API to callers.
18///
19/// This design takes inspiration from, and uses the nomenclature of, this
20/// article:
21/// <https://thinkbeforecoding.com/post/2021/12/17/functional-event-sourcing-decider>
22pub struct Service<S: State> {
23 state: S,
24}
25
26impl<S: State> Service<S> {
27 /// Create an instance of `Service`
28 pub fn new(state: S) -> Self {
29 Self { state }
30 }
31
32 /// Execute a command
33 ///
34 /// The command is executed synchronously. When this method returns, the
35 /// state has been updated and any events have been logged.
36 pub fn execute(&mut self, command: S::Command, events: &mut Vec<S::Event>) {
37 self.state.decide(command, events);
38
39 for event in events {
40 self.state.evolve(event);
41 }
42 }
43
44 /// Replay the provided events on the given state
45 pub fn replay<'event>(
46 state: &mut S,
47 events: impl IntoIterator<Item = &'event S::Event>,
48 ) where
49 <S as State>::Event: 'event,
50 {
51 for event in events {
52 state.evolve(event);
53 }
54 }
55}
56
57impl<S: State> Deref for Service<S> {
58 type Target = S;
59
60 fn deref(&self) -> &Self::Target {
61 &self.state
62 }
63}
64
65impl<S: State> Default for Service<S>
66where
67 S: Default,
68{
69 fn default() -> Self {
70 Self::new(S::default())
71 }
72}
73
74/// Implemented for state that can be wrapped by a [`Service`]
75///
76/// See [`Service`] for a detailed explanation.
77pub trait State {
78 /// A command that relates to the state
79 ///
80 /// Commands are processed by [`State::decide`].
81 type Command;
82
83 /// An event that captures modifications to this state
84 ///
85 /// Events are produced by [`State::decide`] and processed by
86 /// [`State::evolve`].
87 type Event;
88
89 /// Decide how to react to the provided command
90 ///
91 /// If the command must result in changes to the state, any number of events
92 /// that describe these state changes can be produced.
93 fn decide(&self, command: Self::Command, events: &mut Vec<Self::Event>);
94
95 /// Evolve the state according to the provided event
96 ///
97 /// This is the only method gets mutable access to the state, making sure
98 /// that all changes to the state are captured as events.
99 ///
100 /// Implementations of this method are supposed to be relatively dumb. Any
101 /// decisions that go into updating the state should be made in
102 /// [`State::decide`], and encoded into the event.
103 fn evolve(&mut self, event: &Self::Event);
104}