# Library

The **library** is a domain of **copies of books**: a **librarian** acquires and withdraws them, and **readers** borrow, renew, and return them, sometimes late, and pay the fees that come with that. In a request, the domain is named **`library`**.

Its names and data follow the library example of the EventSourcingDB documentation, which **[First Steps with EventSourcingDB](/learn/tutorials/first-steps-with-eventsourcingdb)** builds step by step. Unlike there, IDs are **UUIDs** rather than short numbers, and an ISBN is written as **13 digits** without separators.

## Subjects

- **A copy of a book** is a subject of its own, **`/books/<uuid>`**. A book is a copy, not a work: the work is named only by the title, the author, and the ISBN in the data. If the library acquires the same title twice, it has two copies, and so two subjects.
- **A loan** is not a subject of its own, but a phase in the life of a copy, because every rule about a loan asks about the state of the copy: whether it is there, who has it, and until when.
- **Readers** have no events of their own. They appear only as references in the data, such as **`/readers/<uuid>`** in `borrowedBy`, and only where the history of the copy does not already say who it is.

For more on subjects, see **[Subjects](/docs/eventsourcingdb/subjects)**.

## Events

| Event type | What it records | Data |
|---|---|---|
| `io.synthevents.library.book-imported` | A copy that the library had before its records were kept here was taken over into them. | `title`, `author`, `isbn` |
| `io.synthevents.library.book-acquired` | The library acquired a new copy. | `title`, `author`, `isbn` |
| `io.synthevents.library.book-withdrawn` | A copy left the stock for good. | `withdrawnAt` |
| `io.synthevents.library.book-borrowed` | A copy was lent to a reader. | `borrowedBy`, `borrowedAt`, `borrowedUntil` |
| `io.synthevents.library.loan-renewed` | The loan of a copy runs on, until a new day. | `renewedAt`, `borrowedUntil` |
| `io.synthevents.library.book-returned` | A copy came back. | `returnedAt` |
| `io.synthevents.library.late-fee-issued` | A reader owes a fee for keeping a copy beyond its due date. | `daysOverdue`, `amount` |
| `io.synthevents.library.late-fee-paid` | A reader paid everything they owed for a copy. | `paidBy`, `paidAt`, `amount` |

`book-imported` and `book-acquired` carry the same data. A library that existed before its records were kept here brings its stock along, and the type tells a copy that was there already from one acquired since, so that an account of acquisitions does not begin with the whole stock.

## Data

- **`title`** and **`author`** name the work. **`isbn`** is its ISBN as **13 digits without separators**, such as `9780756906788`: hyphens and spaces only help to read an ISBN, and data keeps it without them.
- **`borrowedBy`** and **`paidBy`** name a reader as **`/readers/<uuid>`**.
- **`borrowedAt`**, **`renewedAt`**, **`returnedAt`**, **`withdrawnAt`**, and **`paidAt`** are **business times**, in UTC to the **millisecond**, such as `2025-01-01T17:12:14.331Z`. They give the moment of the event, written the way an application usually captures it, which keeps them apart from the event's own `time`, which has nanoseconds.
- **`borrowedUntil`** is the **due date** of the loan, as a day such as `2025-01-22`, in the time zone of the library. A renewal gives the new due date under the same name the loan had from the start.
- **`daysOverdue`** is the number of days a loan was overdue.
- **`amount`** is a sum of money in **cents**, with the unit and the currency written out, so that nobody takes the cents for euros: `{"currency": "EUR", "unit": "cent", "value": 350}`.

Every field is present in every event of its type, and no other field is. The **[backup for EventSourcingDB](/docs/synthevents/formats#backup-for-eventsourcingdb)** carries the schemas that say so.

A `book-borrowed` event, formatted for reading, looks like this:

```json
{
  "specversion": "1.0",
  "id": "48",
  "source": "tag:synthevents.io,2026:synthevents;version=golden;domain=library;scenario=everyday;from=2025-01-01T00:00:00Z;to=2026-01-01T00:00:00Z;eventCount=500;seed=golden",
  "subject": "/books/dd9ceeee-febe-436b-9b66-17334b5a7abc",
  "type": "io.synthevents.library.book-borrowed",
  "time": "2025-01-01T17:12:14.331645957Z",
  "datacontenttype": "application/json",
  "data": {
    "borrowedAt": "2025-01-01T17:12:14.331Z",
    "borrowedBy": "/readers/6e682d89-e9e9-4d55-8672-2da67f8bf38a",
    "borrowedUntil": "2025-01-22"
  }
}
```

*Note that this event comes from a test of SynthEvents, which generates its data sets with the version `golden` and the seed `golden`. In a data set of the public service, `version` names the version of SynthEvents that generated it.*

## Rules

The events follow the **rules of the library**. A command that breaks one of them is refused and leaves no event.

### Stock

- **Importing** and **acquiring** a copy create it. Neither of them creates a copy that exists already. A copy that was imported counts as acquired for every rule.
- **Withdrawing** a copy takes it out of the stock. Only a copy that exists, is not withdrawn already, and is not lent can be withdrawn.

### Loans

- **Borrowing** lends a copy that exists, is not withdrawn, and is not lent.
- A loan runs for the **loan period** from the day it starts. **Renewing** it lets it run for another loan period from the day of the renewal, as often as the library allows. A loan that is overdue can be renewed too.
- Only the **reader the copy is lent to** can renew or return it. This is why `loan-renewed` and `book-returned` name nobody: the history of the copy already says who it is.

### Late Fees

- Every **started day** a loan is overdue costs a fee. Returning or renewing an overdue loan **issues the fee** right away: `late-fee-issued` follows `book-returned` or `loan-renewed`, with the same time, as what follows from it. So a loan has a fee for every overdue renewal and for an overdue return.
- A fee is owed by the **reader of that loan**, which is the reader named in the latest `book-borrowed` of the copy.
- **Paying** settles everything a reader owes for a copy at once, with a single `late-fee-paid`. It names the reader in `paidBy`, since fees of two readers can be owed for the same copy at the same time.
- A copy can be withdrawn while fees are still owed for it, and they can be paid afterwards, since a debt is the reader's, not the shelf's. So `late-fee-paid` can follow `book-withdrawn`.

### The Library's Policy

The **moment**, the **due date**, and the **fee** are the library's to set, not the reader's. The rules go by a **policy**: the loan period, how often a loan may be renewed, the fee per day, the time zone of the library, and its clock. They take every business time from the clock and reckon every day in the time zone. The policy is set by the **scenario**.

Every command except importing and acquiring decides on the **state of the copy as it is now**, as a desk does: a copy is refused because of what is true of it, not because something happened to it in between.

## Scenarios

- **[Library in Everyday Operation](/docs/synthevents/library-everyday)** (`everyday`) is a library with its readers and its librarian.
