Sunday, July 30, 2006

Why CRUD use cases are intrinsically evil


When performing analysis for a new system, many people find useful to shrink several use cases in a single one, and tagging it with the CRUD stereotype. This choice has a clear advantage in terms of readability of the use case diagram, while the stereotype (often associated with a different color) just triggers a multiplication factor when defining estimates for development time.

Readability and estimates are just a small part of the game when it comes to actually design the overall system. A Use Case is intended not only to be a measurement of the overall complexity of the system, but also a useful tool to capture the real goal (for a good exploration on the matter, please read Alistair Cockburn’s Writing Effective Use Cases) of the different parties involved in the use case. Just a simple order-invoice combination cannot be well represented by two CRUDs due to the many interactions possible between the two data types.

A system made up by a pile of CRUD usually ends up in a data-centric application, where part of the complexity of the interaction between different data types becomes a burden on the user, instead of a system’s responsibility. A user-focused use case (or an XP-like user story) should instead capture the flow of the interactions between different data types. If you’re not modeling what lies beneath a CRUD and another one, you’ll simply have to face it later on in terms of cluttered implementation or a not-so-usable application.

Separating use cases concerns
In nontrivial systems I found useful to separate the different concerns by defining two layers of use cases:
  • Business Level use cases focus on the user interaction with the system, they provide a detailed description of the user’s goal as well as other party’s, and span across multiple data types;

  • Implementation Level use cases focus instead on the artifacts needed to realize the use case, such as web pages, persistence methods, complex data manipulation and so on, generally tied to one or few data types.
The first layer depends on the second one for the implementation, and in trivial cases there is no need to separate them.

Testing the resulting system
A not-so-small difference in the two styles is that we can choose which use case layer is best suited to provide the specifications for building our test suite: at the business case level we can probably test more with less effort, and catch some awkward combination of data that tests based on the implementation level couldn’t catch. Of course the better combination is to define (unit) tests for the implementation level to be used as bricks for the business level tests.

If acceptance test are to be run by a real user, they’ll probably follow some path defined in business level use cases, instead of merely trying to add or modify some data and see what happens.

Enter the domain model
Such a modeling style, prepares the background for a domain model type of system (as opposed to the data-centric one). If we are to model something which lies in the correlation between two or more different data types, the best place to define it is in the domain model, be it in the component behavior or in the use case controller class. The default choice for data centric systems I’ve seen so far, has always been presentation layer… :-(

A significant exception
Apart from very simple systems, data centric applications (and thus CRUD-like use case modeling) are the best choice when the overall business processes aren’t completely defined, so instead of having a complex, but incomplete, system driving the user (possibly to a dead end) it’s probably better to have the expert users in control, and let them the possibility to tweak the system as they need (a possibility greatly appreciated in call centers, for example). However, this situation it’s not a great symptom of the whole organization’s health (but might be the driving force in a startup, for example), more often it is a sign of a poorly done analysis.


Tags: , ,

5 comments:

Anonymous said...

Hello

Can you elaborate on the following situation. There is an application where users need to configure certain things. For example, managing different tax options available for different charges -> In this case, one option might be to define different tax options at a global basis and then apply these tax options to different types of transactions. e.g., food might have one tax, services might have a nother tax, household items mayhave another tax.

So legitimately, a user should be able to create tax defintions, view tax definitions, modify tax definitions, and remove tax definitions. I would think that at the most granular level you would have 4 use cases that would be called a CRUD use case. I would think there is a service (in the service oriented world) called maintain tax which supports the business need for managing taxes.

Are you saying the above situations I gave is not worth documenting in a CRUD use case? How else would I document the functional requirements?

Unknown said...

When designing a new system, I am concerned about the reasons why the users need to perform certain actions. That tells a lot more about how the system should be to be really useful.

It is right to say "a user should be able to create tax defintions, view tax definitions, modify tax definitions, and remove tax definitions." but with this approach you might miss some important details on the way your system will be used. For example I expect charges to be read-only and their validity to be related to a given government act or law, from a given date, overriding previous existing charges. In our system that would mean that once entered, the tax levels should not be updated (in a DDD paradigm I'd say they make perfect candidates for being value objects). ...Or they might not even be entered: they might be looked up in an external service. Also, depending on the goal of your system it might be useful to have a direct link to the paper ruling the tak discipline, for example if the boundaries between tax categories are blurred and user are frequently checking the rules to apply the correct charge. I am speculating, so I may be completely off track, the right answer in your specific context depends on the whys and on user's needs.

More generally, there are cases where the whys behind Create, Read, Update and Delete are really different. By collapsing everything into a single CRUD you might lose some important detail.

Sometimes this approach might be overkill, some other times it's what makes your systems worth doing. In general, it is okay to have some CRUD use cases, for system entities that are not particularly meaningful, but I'd consider a smell a system made up primarily of CRUD use cases: that's a shortcut for seeing the system only with the eyes of the builder and not with the eyes of the user. When I ask myself the whys, sometimes I find a deeper reason that lead me to an unforeseen design choice, some other times I discover that there's really nothing more that it appears. So a CRUD might be fine.

Anonymous said...

Not agree with you!If you find your use case more granular way with caution then what is the problem to combined related CRUD operation? I think you are missing something and should explain clearly!

Unknown said...

Could you please explain a little more? I might agree that this post needs some refinement (it's 6 years old, and now I would probably phrase it differently. But I can't grasp your question... :-(

Unknown said...

BTW there's a lot of spam on blogger lately. Commenting anonymously is not a great idea: your comment might be lost in the spam.