# 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 with JSON annotations that implements two functions: **`EventType`**, which returns the **event type**, and **`Schema`**, which describes the event's data as a **JSON schema**. Create a file `events.go`:

```go
package main

type BookAcquired struct {
  Title  string `json:"title"`
  Author string `json:"author"`
  ISBN   string `json:"isbn"`
}

func (BookAcquired) EventType() string {
  return "io.eventsourcingdb.library.book-acquired"
}

func (BookAcquired) Schema() map[string]any {
  return map[string]any{
    "type": "object",
    "properties": map[string]any{
      "title":  map[string]any{"type": "string"},
      "author": map[string]any{"type": "string"},
      "isbn":   map[string]any{"type": "string"},
    },
    "required":             []string{"title", "author", "isbn"},
    "additionalProperties": false,
  }
}

type BookBorrowed struct {
  BorrowedBy    string `json:"borrowedBy"`
  BorrowedUntil string `json:"borrowedUntil"`
}

func (BookBorrowed) EventType() string {
  return "io.eventsourcingdb.library.book-borrowed"
}

func (BookBorrowed) Schema() map[string]any {
  return map[string]any{
    "type": "object",
    "properties": map[string]any{
      "borrowedBy":    map[string]any{"type": "string"},
      "borrowedUntil": map[string]any{"type": "string", "format": "date"},
    },
    "required":             []string{"borrowedBy", "borrowedUntil"},
    "additionalProperties": false,
  }
}

type BookReturned struct{}

func (BookReturned) EventType() string {
  return "io.eventsourcingdb.library.book-returned"
}

func (BookReturned) Schema() map[string]any {
  return map[string]any{
    "type":                 "object",
    "properties":           map[string]any{},
    "additionalProperties": false,
  }
}
```

The struct becomes the **data** of the event. 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)**).

`BookReturned` carries no data, so its schema describes an empty object. The schema of `BookBorrowed` requires the return date to be a **date**, such as `2026-10-24`.

The schema is not optional: an event without a `Schema` function does not compile. For details, see **[Defining Events](/docs/architecturekit/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.

Create a file `book.go`:

```go
package main

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

type Book struct {
  IsAcquired bool
  IsBorrowed bool
}

var bookState = architecturekit.NewState(Book{}).
  Evolve(func(book Book, event BookAcquired) Book {
    book.IsAcquired = true
    return book
  }).
  Evolve(func(book Book, event BookBorrowed) Book {
    book.IsBorrowed = true
    return book
  }).
  Evolve(func(book Book, event BookReturned) Book {
    book.IsBorrowed = false
    return book
  })
```

**`NewState`** takes the initial value, and every call to **`Evolve`** says how an event changes the state. The event type is taken from the event's `EventType` function, so it does not have to be repeated.

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/defining-a-view-and-a-query)**. For details, see **[Defining State](/docs/architecturekit/defining-state)**.

## Registering the Schemas

EventSourcingDB checks events against a schema only once the schema is registered. The state knows the schemas of all events it evolves by, and the **store**, which reads events from and writes them to EventSourcingDB, registers them.

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

```go
package main

import (
  "net/url"

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

func newStore() (*architecturekit.Store, error) {
  baseURL, err := url.Parse("http://localhost:3000")
  if err != nil {
    return nil, err
  }

  client, err := eventsourcingdb.NewClient(baseURL, "secret")
  if err != nil {
    return nil, err
  }

  store := architecturekit.NewStore(client, "https://library.eventsourcingdb.io")

  err = store.RegisterSchemas(bookState.Schemas())
  if err != nil {
    return nil, err
  }

  return store, nil
}
```

The second argument of **`NewStore`** is the **source** of all events that the store writes (see **[Sources](/docs/eventsourcingdb/sources)**). **`Schemas`** returns the schemas of all events the state evolves by, and **`RegisterSchemas`** registers them with EventSourcingDB.

To try it, create a file `main.go`:

```go
package main

import (
  "fmt"
  "log"
)

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

  fmt.Println("Registered the event schemas.")
}
```

Run the program:

```shell
go run .
```

It prints:

```
Registered the event schemas.
```

Run it a second time. The output is the same, because `RegisterSchemas` 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 POST \
  -H "authorization: Bearer secret" \
  http://localhost:3000/api/v1/read-event-types
```

The response contains the three event types, each with its schema:

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

All three are **phantoms**, 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 **cannot change**. Suppose you added a property `pages` to the schema of `BookAcquired` – the program would then refuse to start, with a message that begins with the current date and time:

```
2026/09/29 21:57:37 architecturekit: permanent failure: 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
exit status 1
```

So leave the schemas 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/versioning-events)**). For details on schemas, see **[Registering Event Schemas](/docs/architecturekit/registering-event-schemas)**.
