Stepping Through States
Sometimes the states before and after an event are needed, not only the latest one, for example for a history that tells what each event changed. To advance a state by a single stored event, call the step_stored function with the state so far and the event.
It runs the upcasters and evolve exactly as reading from the database does, and returns the next state. A projection that tells what each event changed keeps the state before an event, and steps it. The projection gets the events of every subject, but the state has variants only for the events of its own subjects, and an event of a type without a variant makes step_stored fail, and with it the projection. So it steps only the events of the subjects of the state, which a SubjectScheme tells apart from the others:
use std::{collections::HashMap, future, sync::Mutex};
use architecturekit::{Envelope, eventsourcingdb, projection_fn, step_stored};
let books: Mutex<HashMap<String, Book>> = Mutex::default();
let history = projection_fn(move |event: Envelope<eventsourcingdb::Event>| {
let Some(book_id) = BOOKS.value(&event.subject, "book_id") else {
// An event of another subject, such as one of a reader.
return future::ready(Ok(()));
};
let mut books = books.lock().unwrap();
let before = books.get(book_id).cloned().unwrap_or_default();
let after = step_stored(&before, &event.data).map(|after| {
if !before.is_borrowed && after.is_borrowed {
// The book was borrowed by this event.
}
books.insert(book_id.to_owned(), after);
});
future::ready(after)
});
Note that asking Events::has_rule whether the state has a variant for the type of an event, instead of asking the subject scheme, would skip the events of an older type as well, since has_rule knows nothing of upcasters.
For typed events, for example in a test, call the step function instead, with the state so far and a variant of the enum of the state.
Both take the state by reference and evolve a clone of it, so the given state stays unchanged, and both states are at hand afterwards. Every state is Clone, so this holds for a state that holds collections as well (see Caching States).
Note that a stored event that the upcasters turn into several events is applied in full, and that from_latest has no effect on a single step.