Most serious backend systems do not begin with an execution engine.
They begin with a practical service architecture.
Define protobuf messages. Receive commands. Decode them into request types. Build an in-memory domain model. Run business logic. Emit events. Project state into database tables, write checkpoints, and add recovery tools as the system grows.
That design is not naive. It works because it solves real problems.
Why the standard pattern works
The standard service pattern usually looks like this:
protobuf command
|
v
decode / validate
|
v
domain objects
|
v
business logic
|
+--> database projections
+--> checkpoint / snapshot
+--> protobuf events
+--> logs / metrics / debug viewsEach layer has a job.
Protobuf gives the system a stable wire contract. It is good at schema evolution: fields can be added over time, compatibility can be managed explicitly, and old and new services can coexist during upgrades. It is also compact and fast enough for many high-throughput systems, with mature tooling across languages.
Domain objects give developers a natural way to express business concepts. A trading system should be able to talk about orders, positions, risk limits, balances, fills, accounts, and workflows without forcing every rule to manipulate raw bytes or database rows.
Databases give teams persistence, query, indexing, migrations, backups, and operational tooling. In many engines, the database does not store the engine's raw in-memory state directly. It stores projections: tables shaped for query, operations, reporting, reconciliation, or recovery checkpoints. That is useful, but it also means the database view is often a materialized representation of engine state, not the execution model itself.
Events give downstream systems a clean integration surface. Risk, reporting, reconciliation, monitoring, and analytics often need to know what happened without being inside the command handler.
This structure is productive because it separates concerns. The wire format does not have to be the domain model. The domain model does not have to be the database layout. Events do not have to expose every internal detail.
That separation is one reason the pattern has lasted.
Why it scales far
This architecture also scales organizationally.
Different teams can own different boundaries. API schemas can evolve with compatibility rules. Database migrations can be planned. Events can be added for new consumers. Operational dashboards can be built from persisted rows and logs.
For a normal service, that is a good tradeoff. The system is flexible. Developers can move quickly. The business model can change without rewriting the whole platform.
Even in trading systems, this pattern can carry a lot of production traffic. Many matching, risk, order-management, and settlement workflows are built this way. They work because good engineers put strong discipline around schemas, transactions, idempotency, retries, and operations.
The point is not that this architecture is wrong.
The point is that its strengths come from having multiple representations of business state. And when the system becomes a critical state machine, those multiple representations become something the team has to manage very carefully.
Where the pressure appears
The pressure usually does not appear on the happy path.
It appears during replay, recovery, audit, migration, and incident response.
At that point, the team has to answer questions that are more precise than "what does the projection show now?"
- Which command produced this state?
- What prior state did it execute against?
- Was the outcome a success, a deterministic reject, or an operational failure?
- Which fields changed?
- Which events were emitted?
- Was the event emitted before or after durability?
- Can this range be replayed and produce the same result?
In a domain-object architecture, the answer may be distributed across several places:
wire message
domain object
database projection
checkpoint
event payload
operator log
migration scriptThat is manageable when the rules are simple. It becomes harder when the state is long-lived, the business rules are complex, and the operational cost of being wrong is high.
The issue is not that these representations exist. They should exist. The issue is that none of them is automatically the full execution record.
The hidden cost of conversion
Conversion code looks like plumbing, but in a critical state system it often carries business meaning.
A missing field becomes a default. A wire enum maps to an internal enum. A database migration fills a new column. A checkpoint loader rebuilds an object graph. An event builder chooses what downstream systems are allowed to see.
Each choice is reasonable locally.
Together, they create another layer of implicit logic around the command handler. The visible business logic mutates domain objects. The surrounding conversion logic decides how that mutation is represented, persisted, replayed, and explained later.
This is why replay and audit are hard to add after the fact. The system may know the final row. It may know the emitted event. It may have logs. But it may not have a single durable object that says:
this command
executed against this state version
produced this outcome
applied this state delta
emitted these events
advanced the committed frontier to this pointThat object is not a replacement for protobuf, domain objects, databases, or events.
It is the missing execution record between them.
How it could be better
The direction I care about is to make canonical state and execution results explicit.
Business code should still use domain concepts. A hedge engine should talk about risk settings, current positions, current orders, and order requests. A ledger should talk about accounts, transfers, balances, holds, and settlement. A matching engine should talk about instruments, sides, price levels, priority, orders, and fills.
But the runtime should provide a clearer foundation:
- schema-defined canonical records;
- controlled access to current state;
- deterministic command execution;
- explicit state delta;
- explicit events;
- explicit outcome;
- durable command/result history;
- replay and audit from committed history.
That gives the existing architecture a firmer center.
The wire schema remains useful. Domain code remains useful. Databases and projections remain useful. Events remain useful. But the critical transition is no longer something inferred from side effects across all of them.
It becomes a first-class execution result.
Closing thought
The protobuf/domain-object architecture is popular because it solves real engineering problems.
For business-critical state machines, we can do better by keeping those strengths and adding a stronger execution core: one canonical state model, one deterministic command path, and one durable history of what changed.
That is the design direction behind StateVec.