# Making Decisions

A decider connects a state with the decision made on it.

To create one, call the `NewDecider` function with the state and a function that receives the command and the current state, and returns the events to write:

```go
var ErrBookAlreadyAcquired = architecturekit.NewDomainError("book has already been acquired")

var acquireBook = architecturekit.NewDecider(bookState,
  func(ctx context.Context, cmd AcquireBook, book Book) ([]architecturekit.Event, error) {
    if book.IsAcquired {
      return nil, fmt.Errorf("%w: %s", ErrBookAlreadyAcquired, cmd.BookID)
    }

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

The types of the command and the state are taken from the function, so they do not have to be repeated. `NewDecider` returns a `Decider`. Its `State` function returns the state, and its `Decide` function decides on a command, given the state, with the function you handed over.

*Note that calling `NewDecider` with `nil` as the state or as the function panics. A `Decider` that was declared but never created with `NewDecider` has neither, so calling its `Decide` function, or handing it to `Execute`, to the test fixture, or to `Route` panics as well, and `Handle` fails with an error (see **[Handling Commands over HTTP](/docs/architecturekit/v2.0/handling-commands-over-http)**). Either way, the text names the mistake.*

To reject a command, return an error created with the `NewDomainError` function. It takes a format string and arguments, like `fmt.Errorf`, and returns an error of the type `*DomainError`, whose message is exactly the formatted text, and which belongs to the category `ErrDomain` (see **[Handling Errors](/docs/architecturekit/v2.0/handling-errors)**).

To let a caller or a test tell a rejection apart from the others, create it once, as a variable such as `ErrBookAlreadyAcquired`, and return it, or wrap it with `fmt.Errorf` and `%w` to add details, such as the ID of the book. `errors.Is` then finds the variable in the error, as well as the category `ErrDomain` (see **[Expecting Rejections](/docs/architecturekit/v2.0/testing-deciders#expecting-rejections)**).

A decider may check several rules:

```go
var borrowBook = architecturekit.NewDecider(bookState,
  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
  })
```

If there is nothing to do, return neither events nor an error:

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

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