Projection Introduction
PHP Event Sourcing Projections
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.
Demo and Materials
You will find extensive article series on Ecotone's Projection System on the blog. This will help understand essentials from the very beginning up to running Projections in production:
Runnable demo implementations:
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.
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.
The Git Mental Model
If you are new to projections, this analogy carries you a long way:
Commit history
Event Stream
Working directory
Read Model
git checkout
Projection running
A second clone of the same repo
A second Read Model from the same events
git reset --hard
#[ProjectionReset]
The Event Stream is the authoritative history. Read Models are views of that history — you can have many of them, each one optimized for a different question (a ticket list, a counter, a search index). Throwing one away never destroys data, because the history is still there. Building a second one from scratch is just replaying the commits into a new working directory.
This is also why projections are safe to evolve aggressively. When you fix a bug in a Read Model, you have not corrupted any "truth" — only a view of it. Rebuild the view from the events and it is correct again.

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

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.
That's all you need. Let's break down what each part does:
#[ProjectionV2('ticket_list')]— marks this class as a Projection with nameticket_list#[FromAggregateStream(Ticket::class)]— tells the Projection to read events from the Ticket aggregate's stream#[ProjectionInitialization]— called when the Projection is first set up (creates the table)#[EventHandler]— subscribes to specific event types. Ecotone routes events by the type-hint.#[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.
Global (non-partitioned) projection
Yes
Yes
Custom extensions (StreamSource, StateStorage, PartitionProvider)
—
Yes
What's Next
Event Streams and Handlers — control which events reach your projection
Execution Modes — sync, async, and when to use each
Lifecycle Management — CLI commands, initialization, reset
Projections with State — keep state between events without external storage
Emitting Events — notify after the projection is up to date
Backfill and Rebuild — populate with historical data
Failure Handling — transactions, rollback, self-healing
Gap Detection — how Ecotone guarantees no events are lost
Scaling and Advanced — partitioned, streaming, polling (Enterprise)
Blue-Green Deployments — zero-downtime projection changes (Enterprise)
Last updated
Was this helpful?