DDD & co., part 1: What's wrong with CRUD

tl;dr: CRUD is simple but limited: the artificial restriction to four verbs and the destructive actions UPDATE and DELETE cause numerous problems. Typically, you will realize these problems early on, even in small applications.

At first glance, TodoMVC seems to be innocent: it's a simple task list where you can add new entries and edit existing ones. Entries that have been completed can be checked off. You can also remove entries to clean up the list.

The TodoMVC application

All in all, the application is so trivial that it's hard to not think about implementing it using CRUD: a list where you can add, change and delete entries - why should you solve this any differently?

CRUD is obvious, isn't it?

A closer look, however, reveals a number of questions which at least create doubt that the application is really as trivial as it seemed at first glance. As an example, one such question is whether there is a difference between removing an entry that has already been completed and removing an entry that has not yet been completed.

First of all, it should be recognized that both are actually different actions. They may be represented using the same UI controls, and the result may look the same, but from the state of the application, the two actions are fundamentally different. This becomes obvious when you introduce a rule that limits removal to those entries that have already been completed.

From a user's point of view, the two actions also differ from each other. To recognize this, you have to ask about the user's intention: why do users remove an entry in one case and why do they do it in the other?

What's the intention?

You can probably assume that users remove entries that have not yet been completed because they are no longer needed, i.e. they are discarded. In contrast, entries that have already been completed will be removed to clean up the list. Nevertheless, these entries may still be of interest, e.g. to find out how many tasks were completed within a given period of time.

And there are more differences: TodoMVC allows users to change the description of an entry. When using CRUD, this would be an UPDATE - as well as checking off a completed entry. But the intention of both actions differ: in the first case it is a matter of correcting or adjusting an entry, in the second case it's a matter of marking an entry as completed.

The existence of these distinctions is also noticeable when you imagine doing TodoMVC using only a pen and some paper. You would prefer Check off the third entry! over UPDATE the third entry!. The former is much more natural and clearer at the same time. The point here is that the first sentence uses the domain's language and takes your intention into account, while the second one uses technical wording only.

The term check off provides semantics, which you automatically associate with an appropriate action in reality. The word update, on the other hand, only describes the purely technical process of data manipulation at table level.

Domain language vs technical language

This is one of the largest issues in everyday software development: the domain language and the technical language differ from each other. You can also say that the technical language is much less expressive than the domain language. This makes communication between domain experts and developers extremely difficult, as it constantly requires mapping and translating terminology in both directions.

Additionally, this leads to the problem that the domain language is not clearly defined: since you always have to translate forth and back, you use various words for the same semantics, e.g. check the box, strike off and forget. You don't think about whether the three terms actually mean the same thing or whether they maybe describe different concepts.

As a result, implicit assumptions are made all the time. More often than not, this leads to code that does not fulfill the requirements of the domain. Unfortunately, you typically realize this only when it's too late. At that point in time changes are very expensive and highly complex, and hence often fail.

CRUD loses history

As if this wasn't enough, there is another problem: CRUD loses the history of your application. E.g., if a user changes an entry multiple times over the course of time, the individual changes can no longer be traced. What's even worse, it is not even possible to determine whether the entry has ever been changed at all.

Of course, this can be handled by adding a field for a timestamp of the last update. But this only helps you with the latest update. If you are interested in the entire history, things start to get complicated: you must either introduce a collection of fields or create a new table right from the start.

The problem here is that since you don't know which questions will be asked about your data in the future, you can't optimize the tables for the corresponding answers. So you either collect too much or too little data, but definitely always the wrong one. If domain experts then ask questions such as the following ones, they can only be answered accidentally - if at all:

  • How much time passes between adding and completing an entry?
  • How often is an entry edited before it gets completed?
  • How many entries are discarded even though they have been edited at least three times?
  • How often is completing an entry reverted within 30 seconds?

Usually the answer to each of these questions will be to first write some code that collects and stores the relevant data. Afterwards, you have to wait a few weeks or months, until you can extract the desired answer. It is obvious that this is anything but satisfactory.

What's wrong with CRUD?

If you put it all together, CRUD's picture is frightening. Since the UPDATE and DELETE actions irreversibly destroy data, historical data cannot be evaluated without additional effort. Furthermore, the artificial limitation to just four verbs makes communication between domain experts and developers very difficult, and hence it fails most of the times.

Then why do we use CRUD all the time? The answer is simple: it's because we have learned it that way. CRUD's approach is so simple and fundamental that every developer knows it at an early stage - and since everyone uses it, hardly anyone ever questions whether it's reasonable or not.

However, the fact that CRUD causes a lot of problems after a quick start is often forgotten. Even a simple example such as TodoMVC is already enough to demonstrate the problems you will discover sooner or later.

The next episode of DDD & co. will present the concept of domain-driven design (DDD) as an alternative to model an application, which helps us to leave the idea of the relational database schema as the base of your application, and which focuses on the actually relevant domain aspects instead.

Twitter Facebook LinkedIn

Golo Roden

Founder, CTO, and managing partner

Since we want to deliver elegant yet simple solutions of high quality for complex problems, we care about details with love: things are done when they are done, and we give them the time they need to mature.