The previous chapter defined ORA’s layered model and introduced the three technical pillars at a high level. This chapter examines each pillar in depth, covering the formal models, consistency boundaries, and design tradeoffs that underpin Observable Reflective Architecture.
These ideas are not new. They have been explored by some of the most influential voices in software architecture:
- Rich Hickey1 described the shift from place to value, where facts become the raw material of a system rather than mutable positions in memory.
- Greg Young2 coined the term CQRS (Command Query Responsibility Segregation), promoted event sourcing where all state is captured as a journal of facts, and wrote the definitive guide on event versioning3.
- Eric Evans4 and Vaughn Vernon5 laid the foundations of domain-driven design, emphasising that business logic should be modelled as an explicit representation of the real world.
- Martin Kleppmann6 proposed treating logs of state changes as primary rather than byproducts, reversing the typical flow of data to support better reasoning, traceability, and derived views.
Together, these ideas define the pillars we are about to explore. There are many other voices who have shaped this space, too many to name individually. The reactive systems community, the DDD community, and practitioners who have wrestled with these problems in production have all contributed patterns, warnings, and hard-won insights. What follows is a synthesis, not an invention. The goal is to bring these ideas together into a coherent architectural philosophy for the systems we need to build next.
All of this evolution can be summarised in a single shift.
These three pillars define how that works in practice:
- Event-Sourced: State is recorded as a sequence of durable events to be preserved indefinitely. Current state can always be derived from the event journal.
- Message-Driven: All intent is expressed through commands and results in emitted events. Side effects are described, never executed inline, and effect execution is properly encapsulated.
- Read-Write Decoupled: Query models and command models follow separate paths, scale independently, and evolve without entanglement.
Event-Sourced
Event sourcing means that every state change in the system is recorded as an immutable fact. Instead of storing just the latest result of a decision, the system keeps a durable journal of every decision that led to that result. This journal is append-only. Events are never updated, deleted, or overwritten. They are recorded once, with intent and context preserved.
This changes how we think about system state. In a traditional mutable system, state is a single point in time, backed by a database that silently overwrites the past. In an event-sourced system, state is derived by replaying a history of events. What matters is how the data got there.
At the centre of this is the consistency boundary, the unit of state that processes commands and emits events. It starts from an initial state, then applies each event in sequence to evolve that state. This allows the system to rebuild current state at any time, deterministically, from the same event history. It also ensures causal ordering within the boundary; events happen in the same order they were produced, by the same writer, under the same identity.
Different architectural styles model this boundary differently. In domain-driven design, the aggregate serves this role. In vertical slice architectures, a command handler folding over an event stream can achieve the same properties. The pattern matters less than the guarantees: a single writer, causal ordering, and deterministic state derivation from events.
This pattern also introduces causal metadata. Each event can reference the command that triggered it, the actor who issued the command, and the state it depended on. These links form the scaffolding of traceability.

The formula is simple but powerful. In event sourcing, we model the system as a function that takes a command and the current state, “state”, which emits an event and uses that event to produce the next state, “state prime”.
Command(C) × State(S) → Event(E)
State(S′) = apply(E, S)In pseudocode, that looks like:
event = handle(command, state)
state_prime = apply(event, state)This stands in contrast to mutable systems, where commands directly modify state, overwrite history, and discard the context that led to change. Once the state is altered, the reason for that change is typically lost. That erasure breaks explainability.
In event sourcing, even if a single field within the state changes, that transition must be captured as a meaningful event. The system doesn’t ever mutate state directly, it derives a new state, “state prime”, by applying the event. This structure enforces traceability by design. Every event is then recorded in an immutable journal, creating a durable history of decisions. If current state is ever lost, it can be fully rebuilt by replaying those events from the beginning.
For Observable Reflective Architecture, mutation as the primary persistence model is unacceptable. ORA requires a traceable path that explains how current state came to be. Event sourcing provides this path by design. It enables replayability, auditability, and the ability to understand and simulate behaviour over time. Without this foundation, there is no durable context and no way to build a system that truly understands itself.
One apparent conflict with immutability is regulatory requirements like GDPR, which mandate data deletion. But even here, we don’t delete events. The event happened. Instead, we separate the envelope (system-level metadata: timestamps, causality, event type) from the payload (user data subject to regulation). Payloads are encrypted with a key tied to the data subject. When a user exercises their right to be forgotten, we delete the key. The event remains in the journal, but its contents become unreadable. History from a system’s perspective is preserved, while personal data becomes unrecoverable. Crypto-shredding is a widely accepted practice.
It’s also worth noting that other regulations, such as financial record-keeping, anti-money laundering, and tax law, actually require immutability. GDPR explicitly exempts data retained for legal obligations, so in many domains, the immutable journal is not just permitted but mandated. Do your own legal due diligence.
The event sourcing formula above is a Moore projection. In a Moore machine, output depends only on the current state, not on the input that caused the transition. The journal records pure state derivation, nothing else. It doesn’t capture what the system did as a result of a transition (sent an email, called an API). It captures only what changed. That’s what makes replay safe.
Side effects are handled elsewhere, and different styles approach this differently. Sagas and process managers react to published events. Reactive handlers trigger actions on the read side. Some FSM-driven implementations model effects as typed data returned alongside the state transition (a Mealy machine pattern). The approach varies, but the principle is consistent: the journal records the Moore projection, and side effects are managed outside the consistency boundary.
Message-Driven
Commands and events form the communication fabric of an Observable Reflective Architecture. A command is a request to make a decision. It represents an intent, initiated by a user, system, or agent. An event is the recorded outcome of that decision. It represents something that has happened and cannot be undone.
What separates a message-driven system from a traditional one is the way these messages are handled. Commands are routed to a single handler, the consistency boundary that owns the state. That boundary validates the request, emits events, and ensures consistency. Events can then be published across an asynchronous boundary to inform other parts of the system.

The asynchronous boundary can take many forms: a dedicated event bus, a message broker, a shared log, or even a file system in the Unix tradition. The mechanism matters less than the principle. What’s essential is that there’s a clear separation between the consistency boundary, where decisions are made and invariants enforced, and the propagation layer, where outcomes flow to the rest of the system. This separation allows systems to scale out and scale back in cleanly. It also provides natural points for resilience. Messages can be retried, replayed, or redirected without losing meaning.
The distinction between the consistency boundary and the asynchronous boundary is essential. It allows decisions to remain consistent within the boundary where invariants are enforced, while outcomes can propagate across the system without introducing contention or global locks.
Read-Write Decoupled
In a traditional architecture, the same model is used to process writes and serve reads. This tightly couples the structure of the system to the shape of its database and forces compromises on both sides. In ORA, we separate the responsibility of updating state from the responsibility of querying it. This pattern is sometimes called Command Query Responsibility Segregation (CQRS), but here it’s part of the structural DNA.
The write path is responsible for handling commands and applying events. These state changes are persisted in event journals and passed through the message bus. The read path listens to those same events and builds projection models: optimized, often denormalized views that exist to answer specific questions.

Projections are disposable. They are not the source of truth. If lost or corrupted, they can be rebuilt at any time by replaying the event stream. This enables fault tolerance and simplifies recovery. It also allows for polyglot persistence; read models can be stored in whatever shape or format best fits the query workload.
This separation has deep consequences. Write models can be kept minimal and strict, enforcing business rules with precision. Read models can be fast, purpose-built, and eventually consistent without compromising correctness. Because projections are updated in the background, they allow systems to serve users with low-latency answers even when the underlying state is still catching up.
This also supports the other pillars. We can build explainable queries that surface the underlying events. We can compute derived values like trends, aggregates, or risk scores constantly in the background. And because writes and reads follow different flows, we can scale them independently to match real-world traffic and operational requirements.
ORA treats read models as a storytelling layer. They form the narrative surface of the system, which is what users see, what operators monitor, and what audits inspect. But that narrative is always grounded in the facts recorded in the journal. This is how we present complexity without hiding truth.
The next chapter puts ORA in context by contrasting it against other common architectural styles, then introduces CHAIN, the maturity model for measuring how well your system can explain itself.
Rich Hickey, The Value of Values (2012). Introduces the idea of place vs. value, and how immutability leads to better reasoning about system behaviour. https://www.infoq.com/presentations/Value-Values/ ↩︎
Greg Young, CQRS and Event Sourcing (2010). Explains how to model systems by capturing all state as a journal of events. https://cqrs.wordpress.com/documents/cqrs-and-event-sourcing-synergy/ ↩︎
Greg Young, Versioning in an Event Sourced System (2016). The definitive guide to handling event versioning over long periods of time. https://leanpub.com/esversioning ↩︎
Eric Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software (2003). Defines the principles of modelling software around the domain it supports. https://www.domainlanguage.com/ddd/ ↩︎
Vaughn Vernon, Implementing Domain-Driven Design (2013). Builds on Evans’ work with patterns for preserving logic and intent in executable models. https://www.oreilly.com/library/view/implementing-domain-driven-design/9780133039900/ ↩︎
Martin Kleppmann, Turning the Database Inside Out (2014). Proposes a model where logs of state changes become primary, enabling derived views, caches, and indexes to be rebuilt from a single source of truth. https://martin.kleppmann.com/2014/09/18/turning-database-inside-out-at-strange-loop.html ↩︎