Docs / ArchitectureKit / Rust / Fundamentals / Executing Commands

Executing Commands

Executing a command reads the state of its subject, has the decider decide on it, and writes the events the decider returns.

To execute a command, call the execute method of the store with the decider and the command, and await it:

let written = store
  .execute(
    &acquire_book(),
    AcquireBook {
      book: Subject::new("/books/42"),
      title: "2001 – A Space Odyssey".to_owned(),
      author: "Arthur C. Clarke".to_owned(),
      isbn: "978-0756906788".to_owned(),
    },
  )
  .await?;

execute reads the events of the command's subject, evolves the state from them, calls the decider, and writes the events it returns, under the preconditions the command declares. It returns the written events, including the fields added by the server, as events of the client SDK, eventsourcingdb::Event. If the decider returns no events, nothing is written, and it returns an empty vector. If it fails, it fails with an ExecuteError, which has exactly the cases executing can end in, from a refusal of the decider to a write whose outcome is unknown (see Handling Errors).

Every event the decider returns is a variant of the enum of the state, so the state has a rule for every event that is written. execute writes the events to the subject that the same state reads for the next command, and the database keeps every event, so an event without a rule would leave a subject that the state could not read any more. Since the decider can not return such an event, the compiler rules it out before the program runs.

execute does not write any of the events if the data of one of them can not be encoded as JSON, for example because it holds a map whose keys are tuples, which JSON has no object keys for. It then fails with a permanent failure that names the event type, the subject, and the reason, since trying again would fail the same way, as in permanent failure: refusing to write an event of type "io.eventsourcingdb.library.shelf-measured" to "/shelves/1", since its data can not be encoded as JSON. The test fixture reports such an event the same way (see Testing Deciders).

The written events come as they are stored, with their data as JSON. To read the data of one of them, call the decode function with the type of the event. It returns an Envelope, the same a projection gets (see Defining Projections), with the data in its data field:

let mut titles = Vec::new();

for event in &written {
  if event.ty() == BookAcquired::EVENT_TYPE {
    let acquired = decode::<BookAcquired>(event)?;
    titles.push(acquired.data.title);
  }
}

Note that decode fails with a permanent failure for an event of another type, rather than leaving the fields of the wrong struct empty, and for data that does not fit the type. No upcasters run, since the events that were just written have the current types.

Note that execute only reads the events of the command's subject itself, not those of nested subjects.