# Projection Introduction

## The Problem

Once you start storing storing events instead of updating rows — you will quickly find out your users still need a ticket list page, a dashboard, a report. How do you turn a stream of "what happened" into a table you can query?

In traditional applications, when a ticket is created you run an `INSERT`, when it's closed you run an `UPDATE`. The database always holds the current state. But with Event Sourcing, you store **what happened** — `TicketWasRegistered`, `TicketWasClosed` — as an append-only log of events.

Think of it like a bank account: instead of storing "balance = 500", you store every deposit and withdrawal. The balance is derived by replaying the history.

But your users don't want to replay history every time they load a page. They need a ready-to-query table. That's what **Projections** do.

## What is a Projection?

A **Projection** reads events from an **Event Stream** (the append-only log) and builds a read-optimized view from them — a database table, a document, a cache entry. Think of it as a **materialized view** built from events.

Another analogy: the Event Stream is like your **Git history** — every commit ever made. The Projection is like your **working directory** — the current state of the files, derived from that history.

The views built by Projections are called **Read Models**. They exist only for reading and can be rebuilt at any time from the Event Stream.

<figure><img src="https://1452285857-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LmAUnBnyZgZuLF2eWLn%2Fuploads%2Fgit-blob-3ea0314690966a341613baa424e2654a4909599c%2Fticket_event_stream_2.png?alt=media" alt=""><figcaption><p>Events stored in the Event Stream</p></figcaption></figure>

From these events, we want to build a list of all tickets with their current status:

<figure><img src="https://1452285857-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LmAUnBnyZgZuLF2eWLn%2Fuploads%2Fgit-blob-5e2c01c66953eb38eed62b33177a5119efd462ce%2Fticket-list%20(1).png?alt=media" alt=""><figcaption><p>Read Model: list of tickets with current status</p></figcaption></figure>

## Building Your First Projection

Let's say we have a `Ticket` Event Sourced Aggregate that produces two events — `TicketWasRegistered` and `TicketWasClosed`. We want to build a read model table showing all in-progress tickets.

```php
#[ProjectionV2('ticket_list')]
#[FromAggregateStream(Ticket::class)]
class TicketListProjection
{
    public function __construct(private Connection $connection) {}

    #[ProjectionInitialization]
    public function init(): void
    {
        $this->connection->executeStatement(<<<SQL
            CREATE TABLE IF NOT EXISTS ticket_list (
                ticket_id VARCHAR(36) PRIMARY KEY,
                ticket_type VARCHAR(25),
                status VARCHAR(25)
            )
        SQL);
    }

    #[EventHandler]
    public function onTicketRegistered(TicketWasRegistered $event): void
    {
        $this->connection->insert('ticket_list', [
            'ticket_id' => $event->ticketId,
            'ticket_type' => $event->type,
            'status' => 'open',
        ]);
    }

    #[EventHandler]
    public function onTicketClosed(TicketWasClosed $event): void
    {
        $this->connection->update(
            'ticket_list',
            ['status' => 'closed'],
            ['ticket_id' => $event->ticketId]
        );
    }

    #[ProjectionDelete]
    public function delete(): void
    {
        $this->connection->executeStatement('DROP TABLE IF EXISTS ticket_list');
    }

    #[ProjectionReset]
    public function reset(): void
    {
        $this->connection->executeStatement('DELETE FROM ticket_list');
    }
}
```

That's all you need. Let's break down what each part does:

1. `#[ProjectionV2('ticket_list')]` — marks this class as a Projection with name `ticket_list`
2. `#[FromAggregateStream(Ticket::class)]` — tells the Projection to read events from the Ticket aggregate's stream
3. `#[ProjectionInitialization]` — called when the Projection is first set up (creates the table)
4. `#[EventHandler]` — subscribes to specific event types. Ecotone routes events by the type-hint.
5. `#[ProjectionDelete]` and `#[ProjectionReset]` — called when the projection is deleted or reset

There is no additional configuration needed. Ecotone takes care of delivering events, initializing, and triggering the Projection.

## Position Tracking

Each Projection remembers **where it left off** in the Event Stream — like a bookmark in a book. When a new event triggers the Projection, it fetches only the events after its last position.

This means:

* **New Projections** start from the beginning of the stream and catch up to the present
* **Existing Projections** only process new events they haven't seen yet
* **After a failure**, the Projection resumes from its last successfully committed position

This is what makes it possible to deploy a new Projection at any point in time and have it automatically build up from the full event history.

## Feature Overview

Ecotone Projections come in two editions. The open-source edition covers the full projection lifecycle for globally tracked projections. Enterprise adds scaling, advanced operations, and deployment strategies.

| Feature                                                                                                                                                            | Open Source | Enterprise |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :---------: | :--------: |
| **Global (non-partitioned) projection**                                                                                                                            |     Yes     |     Yes    |
| [Synchronous event-driven execution](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/execution-modes)                                    |     Yes     |     Yes    |
| [Asynchronous event-driven execution](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/execution-modes)                                   |     Yes     |     Yes    |
| [Lifecycle management](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/lifecycle-management) (init, delete, reset, trigger)              |     Yes     |     Yes    |
| [Multiple event streams](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/event-streams-and-handlers)                                     |     Yes     |     Yes    |
| [Projection state](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/projections-with-state)                                               |     Yes     |     Yes    |
| [Event emission](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/emitting-events) (EventStreamEmitter)                                   |     Yes     |     Yes    |
| [Sync backfill](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/backfill-and-rebuild)                                                    |     Yes     |     Yes    |
| [Batch size configuration](https://docs.ecotone.tech/modelling/event-sourcing/execution-modes#batch-size-and-flushing)                                             |     Yes     |     Yes    |
| [Gap detection](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/gap-detection-and-consistency)                                           |     Yes     |     Yes    |
| [Self-healing / automatic recovery](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/failure-handling)                                    |     Yes     |     Yes    |
| [Polling execution](https://docs.ecotone.tech/modelling/event-sourcing/scaling-and-advanced#polling-projections)                                                   |      —      |     Yes    |
| [Partitioned projections](https://docs.ecotone.tech/modelling/event-sourcing/scaling-and-advanced#partitioned-projections)                                         |      —      |     Yes    |
| [Streaming projections](https://docs.ecotone.tech/modelling/event-sourcing/scaling-and-advanced#streaming-projections) (Kafka, RabbitMQ)                           |      —      |     Yes    |
| [Async backfill](https://docs.ecotone.tech/modelling/event-sourcing/backfill-and-rebuild#async-backfill-enterprise) (parallel workers for partitioned)             |      —      |     Yes    |
| [Rebuild](https://docs.ecotone.tech/modelling/event-sourcing/backfill-and-rebuild#rebuild--reset-and-replay-enterprise) (sync and async with parallel workers)     |      —      |     Yes    |
| [Blue-green deployments](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/blue-green-deployments)                                         |      —      |     Yes    |
| [High-performance flush state](https://docs.ecotone.tech/modelling/event-sourcing/projections-with-state#high-performance-projections-with-flush-state-enterprise) |      —      |     Yes    |
| [Multi-tenant projections](https://docs.ecotone.tech/modelling/event-sourcing/scaling-and-advanced#multi-tenant-projections)                                       |      —      |     Yes    |
| Custom extensions (StreamSource, StateStorage, PartitionProvider)                                                                                                  |      —      |     Yes    |

## What's Next

* [Event Streams and Handlers](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/event-streams-and-handlers) — control which events reach your projection
* [Execution Modes](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/execution-modes) — sync, async, and when to use each
* [Lifecycle Management](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/lifecycle-management) — CLI commands, initialization, reset
* [Projections with State](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/projections-with-state) — keep state between events without external storage
* [Emitting Events](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/emitting-events) — notify after the projection is up to date
* [Backfill and Rebuild](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/backfill-and-rebuild) — populate with historical data
* [Failure Handling](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/failure-handling) — transactions, rollback, self-healing
* [Gap Detection](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/gap-detection-and-consistency) — how Ecotone guarantees no events are lost
* [Scaling and Advanced](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/scaling-and-advanced) — partitioned, streaming, polling (Enterprise)
* [Blue-Green Deployments](https://docs.ecotone.tech/modelling/event-sourcing/setting-up-projections/blue-green-deployments) — zero-downtime projection changes (Enterprise)
