# Defining a View and a Query

In this step, you build the **reading side** of the library: a **view** that holds a catalog of all books, a **projection** that fills it from the events, and a **query** that reads it.

## Separating Reading from Writing

The state of a book from **[Defining Events and State](/learn/tutorials/first-steps-with-architecturekit/defining-events-and-state)** holds only what decisions need, and only for a single book. A list of all books with their titles needs a different shape, built from the same events. Keeping the two apart is the idea behind **[CQRS](/concepts/cqrs)**: the writing side decides on commands, and the reading side answers queries from **[read models](/concepts/read-models)**, which ArchitectureKit calls **views**.

## Defining the View

A view holds **items**, each under a **key** of its own. For the catalog, an item describes a book, and its key is the ID of the book. A **projection** turns events into items: it adds an item when a book is acquired, and changes it when the book is borrowed or returned.

Create a file `catalog.go`:

```go
package main

import (
  "context"

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

type BookItem struct {
  ID            string `json:"id"`
  Title         string `json:"title"`
  Author        string `json:"author"`
  IsBorrowed    bool   `json:"isBorrowed"`
  BorrowedUntil string `json:"borrowedUntil"`
}

func newCatalog() *architecturekit.InMemoryView[string, BookItem] {
  return architecturekit.NewInMemoryView(func(item BookItem) string {
    return item.ID
  })
}

func newCatalogProjection(catalog *architecturekit.InMemoryView[string, BookItem]) *architecturekit.TypedProjection {
  return architecturekit.NewProjection().
    On(func(ctx context.Context, event architecturekit.Envelope[BookAcquired]) error {
      return catalog.Insert(ctx, event.ID, BookItem{
        ID:     bookIDOf(event.Subject),
        Title:  event.Data.Title,
        Author: event.Data.Author,
      })
    }).
    On(func(ctx context.Context, event architecturekit.Envelope[BookBorrowed]) error {
      _, err := catalog.Update(ctx, bookIDOf(event.Subject), event.ID, func(item *BookItem) {
        item.IsBorrowed = true
        item.BorrowedUntil = event.Data.BorrowedUntil
      })
      return err
    }).
    On(func(ctx context.Context, event architecturekit.Envelope[BookReturned]) error {
      _, err := catalog.Update(ctx, bookIDOf(event.Subject), event.ID, func(item *BookItem) {
        item.IsBorrowed = false
        item.BorrowedUntil = ""
      })
      return err
    })
}

func bookIDOf(subject string) string {
  values, _ := bookSubject.Match(subject)
  return values["book"]
}
```

**`NewInMemoryView`** creates a view that holds its items in memory, and takes the key of an item from the given function. **`NewProjection`** creates a projection, and every call to **`On`** adds a handler for one event type. Each handler receives an **`Envelope`**, which holds the metadata of the event, such as its `ID` and `Subject`, and its data in `Data`, already decoded into the Go type of the event.

The handlers call **`Insert`** to add an item, and **`Update`** to change one. Both take the ID of the event they apply, so that the view can skip an event it has applied before. Since the events do not contain the ID of the book, `bookIDOf` takes it out of the subject, using the subject scheme from **[Making Decisions](/learn/tutorials/first-steps-with-architecturekit/making-decisions)**.

For details, see **[Defining Views](/docs/architecturekit/defining-views)** and **[Projections](/docs/architecturekit/projections)**.

## Defining the Query

A query describes what someone wants to know, and a function answers it by reading a view. Create a file `queries.go` with a query that lists books, optionally only those that are available:

```go
package main

import (
  "context"
  "slices"

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

type ListBooks struct {
  OnlyAvailable bool
}

func listBooks(catalog architecturekit.View[BookItem]) func(context.Context, ListBooks) ([]BookItem, error) {
  return func(ctx context.Context, q ListBooks) ([]BookItem, error) {
    items, err := catalog.All(ctx)
    if err != nil {
      return nil, err
    }

    if q.OnlyAvailable {
      items = query.Where(items, func(item BookItem) bool {
        return !item.IsBorrowed
      })
    }

    return slices.Collect(items), nil
  }
}
```

**`All`** returns an iterator over the items of the view, in the order in which they were added. The **`query`** package filters, orders, and pages such iterators, and **`Where`** keeps only the items that match. For everything else it offers, see **[Defining Queries](/docs/architecturekit/defining-queries)**.

## Running the Projection

Replace the content of `main.go` with a program that builds the catalog from the stored events and lists all books:

```go
package main

import (
  "context"
  "fmt"
  "log"

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

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

  ctx := context.TODO()

  catalog := newCatalog()

  err = architecturekit.CatchUpProjection(ctx, store, "/books", true, newCatalogProjection(catalog))
  if err != nil {
    log.Fatal(err)
  }

  books, err := listBooks(catalog)(ctx, ListBooks{})
  if err != nil {
    log.Fatal(err)
  }

  for _, book := range books {
    fmt.Printf("%+v\n", book)
  }
}
```

**`CatchUpProjection`** reads all events that are stored below the subject `/books`, since the fourth argument makes it read **recursively**, and applies them to the projection. It returns once all stored events have been applied.

Run the program:

```shell
go run .
```

It prints the book from **[Making Decisions](/learn/tutorials/first-steps-with-architecturekit/making-decisions)**, now with its title and author from the `BookAcquired` event, and the return date from the `BookBorrowed` event:

```
{ID:42 Title:2001 – A Space Odyssey Author:Arthur C. Clarke IsBorrowed:true BorrowedUntil:2026-10-24}
```

The view lives in memory, so it is gone once the program ends, and the projection builds it again from the events on the next start. That is fine, because the events are the **source of truth**, and a view can always be rebuilt from them. For views that keep their data and resume where they stopped, see **[Resuming Projections](/docs/architecturekit/projections#resuming-projections)**.
