A few weeks ago, I wrote about why we don’t start software development from the database. The short version: the database is at the back of the building, and your users are at the front door. Start where they are.
That post focused on the why. This one goes into the how: what does outside-in design actually look like once you move past the UI and start thinking about the API, the data shape, and how events flow through the system?
The API Contract Comes First
Once you’ve thought through the user experience and what the interface should look like, the next question is: what endpoints do we need to support that experience?
For a feature driven mostly by queries, those are GET endpoints. The job at this stage is to design the response shape in a way that best enables the frontend to deliver what the user needs. That means thinking about what fields are needed, how they’re structured, what level of detail to surface at each point.
What you’re not thinking about at this stage: how data is stored, what the schema looks like, or whether any of it already exists. That comes later. Right now, you’re designing a contract.
From Contract to Read Model
Once the endpoint contract is defined, you work backwards: what model do we need to read to return that response?
That’s the read model. It’s shaped around the question the endpoint answers, not around the underlying storage structure. There may be some mapping and formatting between the read model and the API response. You might have a C# class with strongly typed properties, an anonymous object serialized at runtime, or in systems like Marten, you can read directly from a data stream and pipe it straight through the HTTP response.
The read model’s job is to hold data optimized for reading. For example, it might store dates using a specific internal type that gets mapped to a string format the frontend expects at serialization time. The read model doesn’t care what the API consumer wants; the API model handles that translation.
Event Sourcing and Projections
In an event-sourced system, the read model isn’t updated by running queries across multiple tables. It gets updated by projections.
Here’s the flow: a command comes in, runs business rules, and produces one or more domain events that are appended to an event stream. A projection listens to those events and populates the read model with whatever slice of data it needs. By the time someone asks a question, the answer is already there.
The calculation happens on the way in, not on the way out.
Consider a system that processes customer invoices and payments. Multiple events happen: an invoice is issued, a payment is received, the payment is applied to the invoice, a credit is issued. Each of those events updates the read model. When a user wants to see outstanding receivables for a customer, the system doesn’t join tables and recalculate balances. It reads from a read model that already has the answer: total invoices due, available credits, payments received, grand total balance.
If the user drills down into the invoices making up that balance, another read model, or maybe the same one, already has that information ready. It stays ready until the next event arrives and updates it.

A Different Kind of Thinking
This is a shift. When you’re used to designing from the database up, projections feel backwards. Why would you pre-compute things? Why not just query when needed?
Because the user experience doesn’t wait for joins. Because the answer to “what does this customer owe me?” should be immediate, not the result of a multi-table calculation that runs every time the page loads.
If you need to start with a direct query and refactor into a read model later, that’s a valid path, provided the experience on the outside doesn’t change. But if you start outside-in, you’re already designing for the shape of the answer. The implementation follows from there.
My Preferred Flow
Define the experience. Stub the endpoints that support it. Validate with stakeholders. Once it’s confirmed, implement the data retrieval and iterate as needed for performance.
What I’m learning is that this approach keeps the focus where it belongs. The read model and the projections are implementation details. The user experience is the target. When you design in that order, you end up building things that actually answer the question, rather than surfacing whatever the schema happened to produce.





Leave a Reply