# Defining Events and State

In this step, you define the **events** of a book, the **state** that is built from them, and register the **schemas** of the events with EventSourcingDB.

## Defining Events

An event describes something that has happened. In the library, a book goes through three of them: it is **acquired**, **borrowed**, and **returned**.

In ArchitectureKit, an event is a struct that derives **`Event`**, together with serde's `Serialize` and `Deserialize`, and names its **event type** in the attribute `event`. Create a file `src/events.rs`:

```rust
use architecturekit::Event;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Event)]
#[event(type = "io.eventsourcingdb.library.book-acquired")]
pub struct BookAcquired {
  pub title: String,
  pub author: String,
  pub isbn: String,
}

#[derive(Serialize, Deserialize, Event)]
#[event(type = "io.eventsourcingdb.library.book-borrowed")]
#[serde(rename_all = "camelCase")]
pub struct BookBorrowed {
  pub borrowed_by: String,
  pub borrowed_until: String,
}

#[derive(Serialize, Deserialize, Event)]
#[event(type = "io.eventsourcingdb.library.book-returned")]
pub struct BookReturned {}
```

The struct becomes the **data** of the event, as serde writes it, so `rename_all` names the fields of `BookBorrowed` `borrowedBy` and `borrowedUntil`. Other parts of an event are taken from elsewhere: the **subject**, which names the book an event belongs to, comes from the command that causes it, and the **source** from the store that you create below. The event types are written as **reverse domain names**, as EventSourcingDB requires (see **[Event Types](/docs/eventsourcingdb/event-types)**).

EventSourcingDB can check every event against a **JSON schema** of its type. The derive also derives that schema from the fields that serde writes: it describes exactly these fields, requires all of them, and allows no others. `BookReturned` carries no data, so its schema describes an empty object. It is written with braces, since serde writes a unit struct such as `struct BookReturned;` as `null`, which is no object, so the derive refuses it.

To constrain a value further than its Rust type does, for example to require the return date to be a **date**, give the value a type of its own that implements **`Schema`** by hand, or change the derived schema with `schema_with` (see **[Describing Events with Schemas](/docs/architecturekit/rust/describing-events-with-schemas)**). For details on events, see **[Defining Events](/docs/architecturekit/rust/defining-events)**.

## Defining the State

To decide whether a book can be borrowed, you need to know whether it exists and whether it is borrowed right now. That is the **state** of a book, and it is built from the book's events.

A state evolves by an enum of its events, with one variant per event type, which derives **`Events`**. Create a file `src/book.rs` with the enum and the state:

```rust
use architecturekit::{Events, State};

use crate::events::{BookAcquired, BookBorrowed, BookReturned};

#[derive(Events)]
pub enum BookEvent {
  Acquired(BookAcquired),
  Borrowed(BookBorrowed),
  Returned(BookReturned),
}

#[derive(Clone, Default)]
pub struct Book {
  pub is_acquired: bool,
  pub is_borrowed: bool,
}

impl State for Book {
  type Event = BookEvent;

  fn evolve(mut self, event: &BookEvent) -> Self {
    match event {
      BookEvent::Acquired(_) => self.is_acquired = true,
      BookEvent::Borrowed(_) => self.is_borrowed = true,
      BookEvent::Returned(_) => self.is_borrowed = false,
    }

    self
  }
}
```

A state implements **`State`**: `Event` names the enum of its events, and **`evolve`** says how an event changes the state. Every time the state is read, it starts from its **`Default`**, and `evolve` takes it one event further, event by event. Since `evolve` matches on the enum, the compiler checks that it handles every event type.

Note that the state holds only what decisions need: neither the title nor the author of a book decides whether it can be borrowed, so the state leaves them out. Listing books with their titles is a job for the reading side, which you build in **[Defining a View and a Query](/learn/tutorials/first-steps-with-architecturekit/rust/defining-a-view-and-a-query)**. For details, see **[Defining State](/docs/architecturekit/rust/defining-state)**.

## Registering the Schemas

EventSourcingDB checks events against a schema only once the schema is registered. The enum knows the schemas of all its events, and **`register_schemas`** registers them through the **store**, which reads events from and writes them to EventSourcingDB.

Create a file `src/store.rs` that connects to EventSourcingDB, creates the store, and registers the schemas:

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

use architecturekit::{Events, Store, eventsourcingdb::Client};

use crate::book::BookEvent;

pub async fn new_store() -> Result<Store, Box<dyn Error>> {
  let client = Client::new("http://localhost:3000".parse()?, "secret");
  let store = Store::builder(client, "https://library.eventsourcingdb.io").build();

  store.register_schemas(BookEvent::schemas()).await?;

  Ok(store)
}
```

**`Client::new`** creates a client of the client SDK, from the address of EventSourcingDB and its API token. The second argument of **`Store::builder`** is the **source** of all events that the store writes (see **[Sources](/docs/eventsourcingdb/sources)**), and **`build`** creates the store. **`schemas`**, which the derive of `Events` provides, returns the schemas of all events of the enum, and **`register_schemas`** registers them with EventSourcingDB.

The modules become part of the library crate once `src/lib.rs` lists them. Create it:

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

To try it, replace the content of `src/main.rs`:

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

use library::store::new_store;

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

  println!("Registered the event schemas.");

  Ok(())
}
```

**`#[tokio::main]`** runs `main` on Tokio, so that it can wait for the store with `.await`. If `new_store` fails, `main` returns the error, and the program stops.

Run the program:

```shell
cargo run
```

The first time, Cargo downloads and compiles the dependencies, which takes a moment. After Cargo's messages about building and running it, the program prints:

```text
Registered the event schemas.
```

Run it a second time. The output is the same, because `register_schemas` is meant to be called on every start: for an event type that EventSourcingDB knows already, it checks that the registered schema is exactly the one from the code.

To see what EventSourcingDB knows now, list its event types:

```shell
curl \
  -X QUERY \
  -H "authorization: Bearer secret" \
  http://localhost:3000/api/v2/data/read-event-types
```

The response contains the three event types, each with its schema, followed by an `end` item:

```json
{"type":"eventType","data":{"type":"io.eventsourcingdb.library.book-acquired","hasEvents":false,"schema":{"additionalProperties":false,"properties":{"author":{"type":"string"},"isbn":{"type":"string"},"title":{"type":"string"}},"required":["title","author","isbn"],"type":"object"}}}
{"type":"eventType","data":{"type":"io.eventsourcingdb.library.book-borrowed","hasEvents":false,"schema":{"additionalProperties":false,"properties":{"borrowedBy":{"type":"string"},"borrowedUntil":{"type":"string"}},"required":["borrowedBy","borrowedUntil"],"type":"object"}}}
{"type":"eventType","data":{"type":"io.eventsourcingdb.library.book-returned","hasEvents":false,"schema":{"additionalProperties":false,"properties":{},"type":"object"}}}
{"type":"end","data":{}}
```

All three are **phantoms**, with `hasEvents` set to `false`, since no events of these types have been written yet (see **[A Word on Phantom Event Types](/learn/tutorials/first-steps-with-eventsourcingdb/listing-event-types#a-word-on-phantom-event-types)**).

## Keeping Schemas Stable

A registered schema **can not change**. Suppose you added a field `pages` to `BookAcquired` – its schema would then change, too, and the program would refuse to start. It prints the error that `main` returns, in the form Rust shows it for debugging, which names the error as **permanent**, since starting again would fail the same way:

```text
Error: Permanent(Other(Failure { message: "the schema of \"io.eventsourcingdb.library.book-acquired\" differs from the registered one, which cannot change; introduce a new event type and an upcaster instead", cause: None }))
```

So leave the events as they are for this tutorial. To change the shape of an event in a real application, introduce a new event type, and translate the stored events of the old type with an upcaster (see **[Versioning Events](/docs/architecturekit/rust/versioning-events)**). For details on schemas, see **[Registering Event Schemas](/docs/architecturekit/rust/registering-event-schemas)**.
