A few years ago I sat with a team that maintained an internal customer screen. It had something like 300 fields. Every field was editable. A user could click Edit, change any combination of those fields, and click Save. The system faithfully recorded the new values. When someone later asked, “Why did this customer’s address change?” nobody could answer. The data was perfect. The intention was gone.

The screen is the purest form of CRUD thinking. Create, read, update, delete. Put the data in, get the data out, change whatever is there. For a long time that felt like the whole job. The database had tables, the UI had screens that matched the tables, and the code shuffled rows back and forth. It worked until it didn’t.

crud-customer-screen.png

The failure is subtle. A customer calls to change a mailing address for a month-long vacation. In a CRUD screen, the user opens the customer record, overwrites the address, and saves. The system now shows the new address. It hides that the change is temporary, that the customer asked for it to return automatically, and that the original address still matters. If the business later needs to know why the address changed, or who requested it, or when to revert it, the screen has nothing to say.

A task-based UI handles the same call differently. Instead of editing a customer record, the user executes a command: “Temporarily change this customer’s mailing address.” The command carries the reason, the duration, and the expected return. The system can record the intent, enforce the rules, and surface the history. The data still changes, but the change is tied to a reason.

The shift took me years. I started building systems the way most people around me did. I designed the database first, then wrote screens that mapped directly to the tables, then added code to validate and save. The database was the truth, and the UI was a friendly face on top of it. That approach is fine for simple cases. It fails as soon as the business needs more than the latest snapshot.

The next step was to separate commands from queries. Instead of create, read, update, delete, everything is either a command or a question. I am placing an order. I am temporarily changing a mailing address. I am reporting that two boxes were damaged in the warehouse. Or I am asking the system to tell me about inventory, orders, or customers. The distinction has consequences. Commands carry intent and side effects. Queries carry no side effects and answer a question (that changes with AI, where systems change their behavior based upon questions asked). When you name things after what the user is trying to do, the shape of the system changes.

Event sourcing extends the same idea into storage. For years I thought the database should hold the current state. We have 80 units in stock because we bought 100, sold 10, had 2 damaged, and returned 8. If the database only stores 80, we lose the story. We cannot see how much we bought, how much was damaged, or why items were returned. Real businesses do not operate on static totals. People buy, return, and drop things. Recording events keeps the history intact and lets the system answer questions it was not originally designed to ask.

cqrs-event-sourcing-flow.png

The storage changed from DBF files on a PC to SQL Server in a closet to cloud databases whose names change every year. The underlying problem remains: the business is made of events, and the system should reflect that. My own thinking moved from people, because I was solving problems for humans, to databases, because that was how I learned to build, to code, when tools started generating tables from classes, to the domain, when I understood that the model should represent the business, and back to people, because you cannot define the domain without understanding the humans in it.

The previous post in this series looked at the inner shift: procedural to object-oriented to functional. The outer shift is structuring the system around what the user is trying to do. The inner shift and the outer shift are the same work. Both are uncomfortable. Both require admitting that there are other ways to build things.

I still meet teams who treat every system as CRUD until it stops fitting. Then they add if-statements. Then they add switches. Then nobody wants to touch the save button because nobody knows what will happen. The screen with 300 fields is a database view with buttons. Users wake up wanting to change a mailing address for vacation, place an order, or report damaged stock. Design for that.

The CRUD Is Solved, Focus on Workflows post explores this from the workflow angle. The idea is the same. When you ship tasks instead of data, the system matches the language of the business (which is made of people).

Stop designing around the data structure. Design around the task. The command captures intent. The table only stores the data.

Further reading

Pasted image 20260816133751.png

Leave a Reply

Trending

Discover more from Claudio Lassala's Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading