# Making Decisions

In this step, you define the **commands** of the library and the **deciders** that decide on them, and **execute** commands against EventSourcingDB – including one that is rejected, because the book is already borrowed.

## Defining Commands

A command describes what someone wants to do. In ArchitectureKit, a command is a struct that implements **`Command`** with two functions: **`subject`**, which returns the **subject** the command acts on, and **`preconditions`**, which returns the conditions under which its events may be written.

Create a file `src/commands.rs`:

```rust
use std::sync::LazyLock;

use architecturekit::{Command, Precondition, Subject, SubjectScheme};

pub static BOOK_SUBJECT: LazyLock<SubjectScheme> =
  LazyLock::new(|| SubjectScheme::new("/books/{book}"));

#[derive(Clone)]
pub struct AcquireBook {
  pub book_id: String,
  pub title: String,
  pub author: String,
  pub isbn: String,
}

impl Command for AcquireBook {
  fn subject(&self) -> Subject {
    BOOK_SUBJECT.build(&[&self.book_id])
  }

  fn preconditions(&self) -> Vec<Precondition> {
    vec![Precondition::on_state_read()]
  }
}

#[derive(Clone)]
pub struct BorrowBook {
  pub book_id: String,
  pub reader_id: String,
  pub borrowed_until: String,
}

impl Command for BorrowBook {
  fn subject(&self) -> Subject {
    BOOK_SUBJECT.build(&[&self.book_id])
  }

  fn preconditions(&self) -> Vec<Precondition> {
    vec![Precondition::on_state_read()]
  }
}

#[derive(Clone)]
pub struct ReturnBook {
  pub book_id: String,
}

impl Command for ReturnBook {
  fn subject(&self) -> Subject {
    BOOK_SUBJECT.build(&[&self.book_id])
  }

  fn preconditions(&self) -> Vec<Precondition> {
    vec![Precondition::on_state_read()]
  }
}
```

Every book has a subject of its own, such as `/books/42`, which holds all events of that book. A **`SubjectScheme`** defines the structure of these subjects once, and **`build`** composes a subject from the ID of a book. The scheme is a `static`, which `LazyLock` creates on first use, so that all commands share it, and later on, the view takes subjects apart again with the same scheme. For details, see **[Composing Subjects](/docs/architecturekit/rust/composing-subjects)**. The commands derive `Clone`, which executing them requires.

The precondition **`Precondition::on_state_read`** makes sure that the events of a command are only written if nothing has been written to the book's subject since its state was read. If two readers try to borrow the same book at the same time, both commands are decided on the same state, but only the first one's events are written. The second one fails with a **conflict**, instead of lending the book twice. For the other preconditions, see **[Using Preconditions](/docs/architecturekit/rust/using-preconditions)**.

## Deciding on Commands

A **decider** connects the state with the decision made on it. Create it with **`Decider::new`**, from a closure that receives the command and the current state, and returns a future of the events to write, or of an error to **reject** the command. The decider takes the types of the command, the state, and the error from the closure, and the type of its events from the state.

Create a file `src/deciders.rs`:

```rust
use architecturekit::{Decider, DomainError};

use crate::book::{Book, BookEvent};
use crate::commands::{AcquireBook, BorrowBook, ReturnBook};
use crate::events::{BookAcquired, BookBorrowed, BookReturned};

pub fn acquire_book() -> Decider<AcquireBook, Book, DomainError> {
  Decider::new(|cmd: AcquireBook, book: Book| async move {
    if book.is_acquired {
      return Err(DomainError::new(format!("book {} has already been acquired", cmd.book_id)));
    }

    Ok(vec![BookEvent::Acquired(BookAcquired {
      title: cmd.title,
      author: cmd.author,
      isbn: cmd.isbn,
    })])
  })
}

pub fn borrow_book() -> Decider<BorrowBook, Book, DomainError> {
  Decider::new(|cmd: BorrowBook, book: Book| async move {
    if !book.is_acquired {
      return Err(DomainError::new(format!("book {} does not exist", cmd.book_id)));
    }
    if book.is_borrowed {
      return Err(DomainError::new(format!("book {} is already borrowed", cmd.book_id)));
    }

    Ok(vec![BookEvent::Borrowed(BookBorrowed {
      borrowed_by: cmd.reader_id,
      borrowed_until: cmd.borrowed_until,
    })])
  })
}

pub fn return_book() -> Decider<ReturnBook, Book, DomainError> {
  Decider::new(|_: ReturnBook, book: Book| async move {
    if !book.is_borrowed {
      return Ok(vec![]);
    }

    Ok(vec![BookEvent::Returned(BookReturned {})])
  })
}
```

Each decider checks the **business rules** of its command. A book can be acquired only once, and it can be borrowed only if it exists and is not borrowed yet. To reject a command, a decider returns a **`DomainError`**, which carries a message for the caller. The events are variants of `BookEvent`, so a decider can only return events that the state knows, which the compiler checks.

Returning a book that is not borrowed is not an error: there is simply **nothing to do**, so `return_book` returns no events, and no error.

The closure returns a future, so that a decider could load what else it needs to decide, such as the state of another book. These deciders need nothing else, and neither read nor write anything: ArchitectureKit reads the state before it calls a decider, and writes the events afterwards. That keeps the decisions free of any infrastructure, which makes them easy to test (see **[Testing Deciders](/learn/tutorials/first-steps-with-architecturekit/rust/testing-deciders)**). For details, see **[Making Decisions](/docs/architecturekit/rust/making-decisions)**.

## Executing Commands

Add the new modules to `src/lib.rs`, which now reads:

```rust
pub mod book;
pub mod commands;
pub mod deciders;
pub mod events;
pub mod store;
```

To execute a command, call **`execute`** on the store with the decider and the command. Replace the content of `src/main.rs` with the following program, which acquires a book, lends it to one reader, and then tries to lend it to another one:

```rust
use std::error::Error;

use architecturekit::{Categorized, Category, DomainError, ExecuteError, eventsourcingdb::Event};
use library::commands::{AcquireBook, BorrowBook};
use library::deciders::{acquire_book, borrow_book};
use library::store::new_store;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
  let store = new_store().await?;

  let executed = store
    .execute(
      &acquire_book(),
      AcquireBook {
        book_id: "42".to_owned(),
        title: "2001 – A Space Odyssey".to_owned(),
        author: "Arthur C. Clarke".to_owned(),
        isbn: "978-0756906788".to_owned(),
      },
    )
    .await;
  report(executed)?;

  let executed = store
    .execute(
      &borrow_book(),
      BorrowBook {
        book_id: "42".to_owned(),
        reader_id: "23".to_owned(),
        borrowed_until: "2026-10-24".to_owned(),
      },
    )
    .await;
  report(executed)?;

  let executed = store
    .execute(
      &borrow_book(),
      BorrowBook {
        book_id: "42".to_owned(),
        reader_id: "17".to_owned(),
        borrowed_until: "2026-10-31".to_owned(),
      },
    )
    .await;
  report(executed)?;

  Ok(())
}

fn report(
  executed: Result<Vec<Event>, ExecuteError<DomainError>>,
) -> Result<(), ExecuteError<DomainError>> {
  match executed {
    Ok(written) => {
      for event in written {
        println!("Written: {} {} {}", event.id(), event.ty(), event.subject());
      }
      Ok(())
    }
    Err(failure) if failure.category() == Some(Category::Domain) => {
      println!("Rejected: {failure}");
      Ok(())
    }
    Err(failure) => Err(failure),
  }
}
```

`execute` reads the events of the command's subject, evolves the state from them, calls the decider, and writes the events it returns, together with the preconditions of the command. It returns the written events as EventSourcingDB has stored them, as events of the client SDK.

The `report` function prints the written events, or the reason for a rejection. ArchitectureKit sorts its failures into **categories**, which **`category`** of the trait `Categorized` returns, and a `DomainError` belongs to the category **`Category::Domain`**. Everything else is treated as a failure here, which `report` hands back, so that `main` stops with it. For all categories, see **[Handling Errors](/docs/architecturekit/rust/handling-errors)**.

Run the program:

```shell
cargo run
```

The first two commands write an event each, while the third one is rejected, because the state that was built from the two events says that the book is borrowed:

```text
Written: 0 io.eventsourcingdb.library.book-acquired /books/42
Written: 1 io.eventsourcingdb.library.book-borrowed /books/42
Rejected: book 42 is already borrowed
```

Run it a second time. The events are still stored in EventSourcingDB, so now every command is rejected, and nothing is written:

```text
Rejected: book 42 has already been acquired
Rejected: book 42 is already borrowed
Rejected: book 42 is already borrowed
```

This also means that running the program again does no harm: the following steps expect exactly the two events with the IDs `0` and `1`, no matter how often you have run it.
