Using the Current Time
Some rules depend on the current time. For example, a reader may borrow a book for at most four weeks, so the decider has to know what day it is.
Rather than calling Utc::now in the decider, hand a clock to a function that creates the decider. Here, the clock returns a DateTime<Utc> of the chrono crate, which you add with cargo add chrono. The closure reads the clock when a command arrives, outside of its future, so that the future only owns the date:
use architecturekit::{Decider, DomainError};
use chrono::{DateTime, Days, Utc};
fn borrow_book(
now: impl Fn() -> DateTime<Utc> + Send + Sync + 'static,
) -> Decider<BorrowBook, Book, DomainError> {
Decider::new(move |command: BorrowBook, book: Book| {
let today = now().date_naive();
async move {
// ...
if command.borrowed_until < today.to_string()
|| command.borrowed_until > (today + Days::new(28)).to_string()
{
return Err(DomainError::new(format!(
"{} can be borrowed for at most four weeks",
command.book
)));
}
Ok(vec![BookEvent::Borrowed(BookBorrowed {
borrowed_by: command.reader_id,
borrowed_until: command.borrowed_until,
})])
}
})
}
A test hands over a fixed clock, so that it does not have to compute around the actual date, and the application the real one:
use chrono::TimeZone;
let borrow_book_on_first_of_october =
borrow_book(|| Utc.with_ymd_and_hms(2026, 10, 1, 0, 0, 0).unwrap());
let borrow_book = borrow_book(Utc::now);
The parameter shows which deciders depend on the time, and the clock can return what the domain thinks in, such as a date in a particular time zone instead of a moment.
Keeping the Time in Events#
Only the decider reads the clock. Whatever it decides based on the time, such as the day a book was borrowed until, it writes into the event. Everything that comes after relies on the event, not on the clock:
evolvenever reads the clock. It runs again every time the state is read, so a state that depends on the clock would be different tomorrow from what it is today, for the very same events.- A projection never reads the clock either. It runs again whenever the read model is rebuilt, which would turn yesterday's events into today's answers.
To know when an event was written, use the time that the database records for every event, which a projection receives in the time field of the envelope. A question that depends on the moment it is asked, such as which books are overdue, belongs into the query, which gets the current time as a parameter (see Depending on More Than the Read Model).