# 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 **queries** that read it.

## Separating Reading from Writing

The state of a book from **[Defining Events and State](/learn/tutorials/first-steps-with-architecturekit/go/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/v2/architecturekit"
)

type BookItem struct {
  ID            string
  Title         string
  Author        string
  IsBorrowed    bool
  BorrowedUntil string
}

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.NewTypedProjection().
    On(func(ctx context.Context, event architecturekit.Envelope[BookAcquired]) error {
      _, err := catalog.Insert(ctx, event.ID, BookItem{
        ID:     bookIDOf(event.Subject),
        Title:  event.Data.Title,
        Author: event.Data.Author,
      })
      return err
    }).
    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 {
  bookID, _ := bookSubject.Value(subject, "book")
  return bookID
}
```

**`NewInMemoryView`** creates a view that holds its items in memory, and takes the key of an item from the given function. **`NewTypedProjection`** 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. Both also return an **outcome**, which tells what they did, for example whether they skipped the event. The catalog does not need it, so the handlers return only the error. Since the events do not contain the ID of the book, `bookIDOf` takes it out of the subject with the **`Value`** function of the subject scheme from **[Making Decisions](/learn/tutorials/first-steps-with-architecturekit/go/making-decisions)**, which returns the value of the placeholder `book`.

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

## Defining the Queries

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, and one that gets a single book by its ID:

```go
package main

import (
  "context"

  "github.com/thenativeweb/architecturekit-golang/v2/architecturekit"
  "github.com/thenativeweb/architecturekit-golang/v2/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 := catalog.All(ctx)

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

    return query.Collect(items)
  }
}

type GetBook struct {
  BookID string
}

func getBook(catalog architecturekit.KeyedView[string, BookItem]) func(context.Context, GetBook) (BookItem, error) {
  return func(ctx context.Context, q GetBook) (BookItem, error) {
    book, isFound, err := catalog.Get(ctx, q.BookID)
    if err != nil {
      return BookItem{}, err
    }
    if !isFound {
      return BookItem{}, query.ErrNoItems
    }

    return book, nil
  }
}
```

**`All`** returns an iterator over the items of the view, in the order in which they were added, and hands out each item together with an error, since a view that keeps its items in a database may fail while it reads them. The **`query`** package filters, orders, and pages such iterators, **`Where`** keeps only the items that match, and **`Collect`** turns them into a slice, or returns the error if the view has failed. For everything else it offers, see **[Defining Queries](/docs/architecturekit/go/defining-queries)**.

`getBook` does not run over all items, since the catalog can find a book by its key. So it takes a **`KeyedView`**, a view that offers a **`Get`** function, which returns the item with the given key, and whether there is one. If there is none, `getBook` returns **`query.ErrNoItems`**, so that a missing book can be told apart from a failure. `InMemoryView` is a `KeyedView`, so the catalog serves both queries (see **[Defining Views](/docs/architecturekit/go/defining-views)**). You put `getBook` to use in **[Serving Commands and Queries over HTTP](/learn/tutorials/first-steps-with-architecturekit/go/serving-commands-and-queries-over-http)**.

## 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/v2/architecturekit"
)

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

  ctx := context.TODO()

  catalog := newCatalog()

  err = architecturekit.CatchUpProjection(ctx, store, architecturekit.SubjectTree("/books"), 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 **`SubjectTree`** stands for `/books` together with every subject below it, 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/go/making-decisions)**, now with its title and author from the `BookAcquired` event, and the return date from the `BookBorrowed` event:

```text
{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/go/projections#resuming-projections)**.
