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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
//! Service API that promotes monitoring and interactivity
//!
//! See [`Service`].
use std::ops::Deref;
use crate::{
objects::{Object, Objects, WithHandle},
storage::Handle,
};
/// A service that controls access to some state
///
/// `Service` is a generic wrapper around some state, as well as code that knows
/// how to operate on that state. It processes commands, changes the state based
/// on those command, and produces events that capture these changes. These
/// events are stored, providing a log of all changes to the state, and can be
/// replayed later to re-create the state at any point in time.
///
/// The wrapped state must implement [`State`], which defines the type of
/// command that this service processes, and the type of event that captures
/// state changes. It also defines methods that operate on the state, commands,
/// and events.
///
/// Implementations of [`State`] might also define an extension trait for a
/// specific `Service<MyState>`, to provide a convenient API to callers.
///
/// This design takes inspiration from, and uses the nomenclature of, this
/// article:
/// <https://thinkbeforecoding.com/post/2021/12/17/functional-event-sourcing-decider>
pub struct Service<S: State> {
state: S,
events: Vec<S::Event>,
}
impl<S: State> Service<S> {
/// Create an instance of `Service`
pub fn new(state: S) -> Self {
Self {
state,
events: Vec::new(),
}
}
/// Execute a command
///
/// The command is executed synchronously. When this method returns, the
/// state has been updated and any events have been logged.
pub fn execute(&mut self, command: S::Command) {
let mut events = Vec::new();
self.state.decide(command, &mut events);
for event in &events {
self.state.evolve(event);
}
self.events.extend(events);
}
/// Access the events
pub fn events(&self) -> impl Iterator<Item = &S::Event> {
self.events.iter()
}
/// Replay the provided events on the given state
pub fn replay<'event>(
state: &mut S,
events: impl IntoIterator<Item = &'event S::Event>,
) where
<S as State>::Event: 'event,
{
for event in events {
state.evolve(event);
}
}
}
impl<S: State> Deref for Service<S> {
type Target = S;
fn deref(&self) -> &Self::Target {
&self.state
}
}
/// Implemented for state that can be wrapped by a [`Service`]
///
/// See [`Service`] for a detailed explanation.
pub trait State {
/// A command that relates to the state
///
/// Commands are processed by [`State::decide`].
type Command;
/// An event that captures modifications to this state
///
/// Events are produced by [`State::decide`] and processed by
/// [`State::evolve`].
type Event;
/// Convert this state into the service that wraps it
///
/// This is a convenience method that just calls [`Service::new`]
/// internally.
fn into_service(self) -> Service<Self>
where
Self: Sized,
{
Service::new(self)
}
/// Decide how to react to the provided command
///
/// If the command must result in changes to the state, any number of events
/// that describe these state changes can be produced.
fn decide(&self, command: Self::Command, events: &mut Vec<Self::Event>);
/// Evolve the state according to the provided event
///
/// This is the only method gets mutable access to the state, making sure
/// that all changes to the state are captured as events.
///
/// Implementations of this method are supposed to be relatively dumb. Any
/// decisions that go into updating the state should be made in
/// [`State::decide`], and encoded into the event.
fn evolve(&mut self, event: &Self::Event);
}
impl State for Objects {
type Command = InsertObject;
type Event = ObjectToInsert;
fn decide(&self, command: Self::Command, events: &mut Vec<Self::Event>) {
let event = ObjectToInsert {
object: command.object,
};
events.push(event);
}
fn evolve(&mut self, event: &Self::Event) {
// This operation being fallible goes against the spirit of the `evolve`
// method. The reason for that is, that `Objects` is not fully adapted
// to this new design yet. In the future, validation will most likely
// move into its own service, making this operation infallible.
event.object.clone().insert(self).unwrap();
}
}
/// Command for `Service<Objects>`
///
/// You might prefer to use [`ServiceObjectsExt::insert`], which is a convenient
/// wrapper around `Service<Objects>::execute`.
pub struct InsertObject {
/// The object to insert
pub object: Object<WithHandle>,
}
/// Event produced by `Service<Objects>`
pub struct ObjectToInsert {
/// The object to insert
pub object: Object<WithHandle>,
}
/// Convenient API for `Service<Objects>`
pub trait ServiceObjectsExt {
/// Insert an object
fn insert<T>(&mut self, handle: Handle<T>, object: T)
where
(Handle<T>, T): Into<Object<WithHandle>>;
}
impl ServiceObjectsExt for Service<Objects> {
fn insert<T>(&mut self, handle: Handle<T>, object: T)
where
(Handle<T>, T): Into<Object<WithHandle>>,
{
self.execute(InsertObject {
object: (handle, object).into(),
})
}
}