# 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 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 `commands.go`:

```go
package main

import "github.com/thenativeweb/architecturekit-golang/architecturekit"

var bookSubject = architecturekit.NewSubjectScheme("/books/{book}")

type AcquireBook struct {
  BookID string
  Title  string
  Author string
  ISBN   string
}

func (c AcquireBook) Subject() string {
  return bookSubject.Build(c.BookID)
}

func (c AcquireBook) Preconditions() []architecturekit.Precondition {
  return []architecturekit.Precondition{
    architecturekit.OnStateRead(),
  }
}

type BorrowBook struct {
  BookID        string
  ReaderID      string
  BorrowedUntil string
}

func (c BorrowBook) Subject() string {
  return bookSubject.Build(c.BookID)
}

func (c BorrowBook) Preconditions() []architecturekit.Precondition {
  return []architecturekit.Precondition{
    architecturekit.OnStateRead(),
  }
}

type ReturnBook struct {
  BookID string
}

func (c ReturnBook) Subject() string {
  return bookSubject.Build(c.BookID)
}

func (c ReturnBook) Preconditions() []architecturekit.Precondition {
  return []architecturekit.Precondition{
    architecturekit.OnStateRead(),
  }
}
```

Every book has a subject of its own, such as `/books/42`, which holds all events of that book. **`NewSubjectScheme`** defines the structure of these subjects once, and **`Build`** composes a subject from the ID of a book. Later on, the view takes subjects apart again with the same scheme. For details, see **[Composing Subjects](/docs/architecturekit/composing-subjects)**.

The precondition **`OnStateRead`** 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/using-preconditions)**.

## Deciding on Commands

A **decider** connects the state with the decision made on it. Its `Decide` function receives the command and the current state, and returns the events to write, or an error to **reject** the command.

Create a file `deciders.go`:

```go
package main

import (
  "context"

  "github.com/thenativeweb/architecturekit-golang/architecturekit"
)

var acquireBook = architecturekit.Decider[AcquireBook, Book]{
  State: bookState,
  Decide: func(ctx context.Context, cmd AcquireBook, book Book) ([]architecturekit.Event, error) {
    if book.IsAcquired {
      return nil, architecturekit.NewDomainError("book %s has already been acquired", cmd.BookID)
    }

    return []architecturekit.Event{
      BookAcquired{
        Title:  cmd.Title,
        Author: cmd.Author,
        ISBN:   cmd.ISBN,
      },
    }, nil
  },
}

var borrowBook = architecturekit.Decider[BorrowBook, Book]{
  State: bookState,
  Decide: func(ctx context.Context, cmd BorrowBook, book Book) ([]architecturekit.Event, error) {
    if !book.IsAcquired {
      return nil, architecturekit.NewDomainError("book %s does not exist", cmd.BookID)
    }
    if book.IsBorrowed {
      return nil, architecturekit.NewDomainError("book %s is already borrowed", cmd.BookID)
    }

    return []architecturekit.Event{
      BookBorrowed{
        BorrowedBy:    cmd.ReaderID,
        BorrowedUntil: cmd.BorrowedUntil,
      },
    }, nil
  },
}

var returnBook = architecturekit.Decider[ReturnBook, Book]{
  State: bookState,
  Decide: func(ctx context.Context, cmd ReturnBook, book Book) ([]architecturekit.Event, error) {
    if !book.IsBorrowed {
      return nil, nil
    }

    return []architecturekit.Event{
      BookReturned{},
    }, nil
  },
}
```

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 an error created with **`NewDomainError`**, which takes a format string and arguments, like `fmt.Errorf`.

Returning a book that is not borrowed is not an error: there is simply **nothing to do**, so `returnBook` returns neither events nor an error.

Note that the deciders 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/testing-deciders)**). For details, see **[Making Decisions](/docs/architecturekit/making-decisions)**.

## Executing Commands

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

```go
package main

import (
  "context"
  "errors"
  "fmt"
  "log"

  "github.com/thenativeweb/architecturekit-golang/architecturekit"
  "github.com/thenativeweb/eventsourcingdb-client-golang/eventsourcingdb"
)

func main() {
  store, err := newStore()
  if err != nil {
    log.Fatal(err)
  }

  ctx := context.TODO()

  writtenEvents, err := architecturekit.Execute(ctx, store, acquireBook, AcquireBook{
    BookID: "42",
    Title:  "2001 – A Space Odyssey",
    Author: "Arthur C. Clarke",
    ISBN:   "978-0756906788",
  })
  report(writtenEvents, err)

  writtenEvents, err = architecturekit.Execute(ctx, store, borrowBook, BorrowBook{
    BookID:        "42",
    ReaderID:      "23",
    BorrowedUntil: "2026-10-24",
  })
  report(writtenEvents, err)

  writtenEvents, err = architecturekit.Execute(ctx, store, borrowBook, BorrowBook{
    BookID:        "42",
    ReaderID:      "17",
    BorrowedUntil: "2026-10-31",
  })
  report(writtenEvents, err)
}

func report(writtenEvents []eventsourcingdb.Event, err error) {
  if errors.Is(err, architecturekit.ErrDomain) {
    fmt.Println("Rejected:", err)
    return
  }
  if err != nil {
    log.Fatal(err)
  }

  for _, event := range writtenEvents {
    fmt.Println("Written:", event.ID, event.Type, event.Subject)
  }
}
```

`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.

The `report` function prints the written events, or the reason for a rejection. Every error that ArchitectureKit returns belongs to a **category**, which you check with `errors.Is`: a rejection by a decider belongs to **`ErrDomain`**, and everything else is treated as a failure here. For all categories, see **[Handling Errors](/docs/architecturekit/handling-errors)**.

Run the program:

```shell
go 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:

```
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:

```
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.
