← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Event Sourcing

architecture PHP 7.0+ Advanced
debt(d9/e9/b9/t5)
d9 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'silent in production until users hit it' (d9). The detection_hints field explicitly states 'automated: no' and describes a business-requirements-level code pattern. There is no linter, SAST, or compiler check that can detect misapplication of event sourcing — the consequences (diverged state, missing audit history, performance degradation from no snapshots) only surface in production under real load or during an incident investigation.

e9 Effort Remediation debt — work required to fix once spotted

Closest to 'architectural rework' (e9). The quick_fix describes 'rebuild current state by replaying events, never update in place' — this is not a one-line patch. Adopting or correcting event sourcing requires redesigning storage (append-only event store), rebuilding all state-derivation logic, implementing snapshot strategies, and potentially introducing CQRS projections. Removing it from an existing system is equally demanding. This is a full architectural rework.

b9 Burden Structural debt — long-term weight of choosing wrong

Closest to 'defines the system's shape' (b9). The applies_to field spans web, cli, and queue-worker contexts, meaning the choice permeates the entire application. Every aggregate, every read model, every deployment concern, every operational practice (replay, snapshot, compaction) is shaped by this decision. The tags (architecture, cqrs, ddd) confirm it is a load-bearing architectural choice that defines the system's fundamental structure — you rewrite or you live with it.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap' (t5). The misconception field explicitly documents the canonical trap: developers believe event sourcing and CQRS must always be used together, conflating two independent patterns. The common_mistakes also list storing current state alongside events (causing divergence) and modifying past events. These are well-documented gotchas that most developers encounter when first adopting the pattern, matching the 't5 — documented gotcha most devs eventually learn' anchor.

About DEBT scoring →

Also Known As

event store append-only log event-sourced architecture

TL;DR

Storing state as an immutable sequence of domain events rather than the current snapshot — the current state is derived by replaying events.

Explanation

Event Sourcing persists every state change as an immutable domain event (OrderPlaced, ItemAdded, PaymentProcessed) in an append-only event store. Current state is reconstructed by replaying the event stream. Benefits: complete audit trail, ability to reconstruct state at any point in time, temporal debugging, and natural fit with CQRS (events feed read model projections). Challenges: eventual consistency, snapshot strategies for long-lived aggregates, event schema evolution, and increased operational complexity. PHP implementations include EventSauce, Broadway, and Prooph.

Diagram

flowchart TD
    CMD[Command] --> AGG[Aggregate]
    AGG --> E1[OrderPlaced event]
    AGG --> E2[PaymentReceived event]
    AGG --> E3[OrderShipped event]
    E1 & E2 & E3 --> STORE[(Event Store)]
    STORE -->|Replay| STATE[Current State]
    STORE -->|Project| READ[(Read Models)]
style STORE fill:#6e40c9,color:#fff
style STATE fill:#238636,color:#fff
style READ fill:#238636,color:#fff

Common Misconception

Event sourcing and CQRS must always be used together. They complement each other well but are independent. You can use CQRS without event sourcing (separate read/write models backed by the same DB) and event sourcing without CQRS (single model rebuilt from events).

Why It Matters

Event sourcing stores every state change as an immutable event — the current state is derived by replaying events, providing a complete audit trail and enabling time-travel debugging.

Common Mistakes

  • Storing current state alongside events — the two will eventually diverge; current state must be derived from events.
  • Modifying or deleting past events — events are immutable; use compensating events for corrections.
  • No snapshot strategy for aggregates with thousands of events — replaying from the beginning becomes too slow.
  • Using event sourcing for simple CRUD — the complexity is only justified when audit history, replay, or temporal queries add real value.

Avoid When

  • Simple CRUD applications — event sourcing adds enormous complexity for no benefit when there is no need for history.
  • Teams unfamiliar with the pattern — incorrect event schema design is very hard to reverse once events are stored.
  • High-frequency write workloads where replaying thousands of events to rebuild state is too slow.
  • When eventual consistency is unacceptable and reads must always reflect the latest write instantly.

When To Use

  • Audit-critical domains (finance, healthcare, legal) where every state change must be recorded and replayable.
  • Systems that need time-travel debugging — replaying events to reproduce bugs at any point in history.
  • Collaborative editing or conflict resolution where the sequence of changes matters.
  • Decoupled read/write models (CQRS) where projections are built from the event stream.

Code Examples

✗ Vulnerable
// Mutable event store — breaks event sourcing guarantees:
class EventStore {
    public function update(int $id, array $data): void {
        $this->db->update('events', $data, ['id' => $id]); // Events must be immutable!
    }
}
✓ Fixed
// Events are the source of truth — state is derived by replaying them
class BankAccount {
    private int $balance = 0;
    private array $events = [];

    public function deposit(int $amount): void {
        $this->apply(new MoneyDeposited($amount));
    }

    private function apply(DomainEvent $event): void {
        $this->events[] = $event;
        match($event::class) {
            MoneyDeposited::class  => $this->balance += $event->amount,
            MoneyWithdrawn::class  => $this->balance -= $event->amount,
        };
    }

    public function getEvents(): array { return $this->events; }
    public function getBalance(): int  { return $this->balance; }
}

Added 15 Mar 2026
Edited 25 Mar 2026
Views 33
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 1 ping S 0 pings M 0 pings T 0 pings W 2 pings T 0 pings F 1 ping S 0 pings S 0 pings M 1 ping T 1 ping W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S
No pings yet today
No pings yesterday
Amazonbot 6 Perplexity 6 SEMrush 3 Ahrefs 2 Unknown AI 2 ChatGPT 2 Majestic 1 Google 1
crawler 22 crawler_json 1
DEV INTEL Tools & Severity
🔵 Info ⚙ Fix effort: High
⚡ Quick Fix
Store every state change as an immutable event (OrderPlaced, PaymentReceived) — rebuild current state by replaying events, never update in place
📦 Applies To
PHP 7.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Business requirements need full audit history, temporal queries, or retroactive corrections that UPDATE-based storage cannot provide
Auto-detectable: ✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: High Context: File Tests: Update

✓ schema.org compliant