The Command Pattern: A Lightweight Alternative to DCI

February 09, 2012 - 11 Comments - ruby rails patterns command pattern dci

Recently the Rails community has seen a flurry of activity regarding a new approach to encapsulating business logic in Rails called Data, Context, Interaction, or, DCI. And rightfully so! The old mantra of “fat models, skinny controllers” has cracked with wisdom as models in larger and more complicated applications have become not just fat but in fact rather obese. Furthermore, the question of which model some piece of business logic should live in has always been vague regarding operations involving multiple models of different kinds.

It is a problem. The results have been sub-optimal design, testability, and readability, coupled with huge model files bloating into thousands of lines of code. The DCI approach has been explored at length as a possible solution. Mike Pack wrote a great article both explaining the benefits of DCI and an example implementation.

The DCI Pattern: Pros and Cons

There are some clear benefits of the DCI approach, especially compared to the old way of just shoving business logic into the nearest relevant model:

  • Skinny models. DCI effectively cleans up your models, and should leave them with nothing but declarative associations, validations, and callbacks, plus any convenient accessor methods and query scopes. Models are left dealing strictly with the access and persistence of your data. In this way, they are dumb, knowing nothing about the business logic beyond how to find, validate, and persist the data for which they are responsible.
  • Explicit definition of user roles. In the Agile development methodology, user stories are defined as being the goal a user who represents a given “role”, such as a “Customer”, “Client”, or “Administrator”. In DCI, abilities tied to these roles are defined explicitly in their own Role modules which extend the actor instances at runtime.
  • Clarity. As mentioned before, when working with the fat models approach, it’s often ambiguous as to which model a given set of business logic should live in, especially when that business logic involves manipulating a number of different kinds of models. With DCI, each business goal lives in its own Context class, regardless of the types of models it requires, so there is no confusion.
  • Testability. With the clean separation of logic, it’s very easy to test each user story in isolation.

Sounds great, right? It turns out that DCI is just the kind of scalpel Rails needs to divvy up responsibilities around complicated business logic, and its nearly lock-step fit with Agile user stories is a great extra feature. That said, here are some of the drawbacks that I can see:

  • Performance. One of the main bogeymen regarding DCI right now is performance. The “correct” approach to DCI is to extend the actors with their appropriate Roles at runtime, which can be a significant performance hit if done enough times. In larger applications with a high request volume, this can be significant. Mike Pack has written another article addressing different approaches to injecting Roles into actors, each with its own trade-offs of performance and purity. It’s definitely worth a look.
  • Extra classes/modules and boilerplate. Each Role requires its own Module, and each Context must begin by extending the given actors with their appropriate Roles. It’s just additional overhead during development.
  • More moving parts. Each Context requires an intimate knowledge of the actions available to the Roles involved.

I still think DCI is an excellent solution, but it occurs to me that it might be a little heavyweight for some applications. That’s why I’m proposing a lightweight alternative which has been a steadfast design pattern for nearly two decades.

The Command Pattern: Pros and Cons

The Command pattern has long been the go-to pattern for encapsulating undoable/redoable actions in GUI applications. I used it in the development of my game, Elite Command, to great effect. My original intent was to add multiple undo to the game, but then it had the great side-benefit of removing a lot of game logic from my models and separating possible player commands into their own easily-testable classes. The result: skinny controllers, skinny models, and relatively skinny Command classes each concerned with an individual feature of the game.

Here are some of the advantages to the Command approach, many of which are the same as for DCI:

  • Skinny models. Like DCI, the Command pattern encapsulates individual business goals into their own classes, leaving models concerned purely with access, validation, and persistence.
  • Clarity. Like with DCI, there is no question as to where the logic for a given business role should go: it goes into its own Command class.
  • Testability. Like with DCI, since each business goal lives in its own class, it is supremely easy to test each scenario in isolation.
  • Peformance. Unlike DCI, the Command pattern requires no extension of objects at runtime. Commands are merely Plain Old Ruby Objects hiding a business goal behind a simple interface. Indeed, the Command pattern can in many ways be regarded as DCI without the Roles.
  • Fewer classes, less boilerplate. Without Roles or runtime extension of objects, one only needs to look in one place for a complete step-by-step view of a given feature’s functionality.
  • Undo and redo support. The Command pattern was originally designed with undo support in mind, so it’s a natural fit if you plan to add undo or redo to your Rails application. Of course, it’s basically just DCI without Roles, so I don’t see why DCI couldn’t also do this fairly easily.
  • A natural fit with the Composite pattern. The Composite pattern allows a tree of objects to be treated as a single object. An advanced implementation of the Command pattern might include a CompositeCommand class which allows multiple Commands to be executed in sequence. This can be useful for things like minimizing Ajax requests by bundling multiple Commands into a single CompositeCommand, a technique I used to great effect in Elite Command. DCI may be capable of this as well, although I’ve never seen an implementation.

I like the Command pattern for its simplicity. Of course, there are always trade-offs. Here are some of the cons to the Command pattern versus DCI:

  • No explicit Roles. You don’t get the one-to-one mapping of Agile user roles to Role modules that you do with DCI. This can be a nice thing to have, depending on the relative complexity of the application
  • Not necessarily as DRY. I can imagine some circumstances where Roles might be a good place for code which is reused between Commands.
  • Coupling between Commands and model interfaces. Where DCI has coupling between Roles and models, the Command classes have coupling between Commands and models, with no intermediary. This might not be a big deal, but it’s something to keep in mind.

The real disadvantage to the Command pattern versus DCI is that Roles are not present. This results in fewer classes and less boilerplate, but as a trade-off you may need to place some logic which would go into Roles into either a Command superclass or into one of the models themselves in order to keep the code DRY. In Elite Command, I opted to include shared logic as modules into the Command class, which has generally worked fine.

The main advantages over DCI are the performance gains from not having to extend anything at runtime and the fewer classes and lines of code required to support the pattern.

An example of the Command pattern in action

Here is a simple implementation of adding an item to a shopping cart using the Command pattern. Elite Command actually uses a much more sophisticated, fully-featured implementation which I may cover in a future article, but for now here is a quick demonstration.

First, our simple models:

And now, the Command superclass:

And finally, a spec and implementation for adding an item to a cart:

Simple, no? Of course, because it’s an exceedingly simple example. However, Commands really shine when they encapsulate more complex algorithms involving interactions between multiple models. And with a dash of persistence and the addition of #unexecute! methods, we have really easy undo support ready to go!

As mentioned above, there are more sophisticated features which can be attained with the Command pattern and which proved to be a great boon for Elite Command. If there’s enough interest, I may write on this further in the future.

Conclusion

As always, design decisions come with trade-offs. I am not advocating the use of the Command pattern instead of DCI. Rather, I am suggesting an alternative which shares many of DCI’s benefits but with a slightly different set of drawbacks. Before going with one or the other, ask yourself whether the explicitly defined Roles are worth it to your application, or whether the lighter conceptual overhead and smaller performance footprint of the Command pattern are of greater concern.

Comments

The Web is collaborative. Isn't it great?

D9ae6a14325712c09678c711ce2010a0?s=50

Mike Pack said:

Good article. I like how you addressed the pros-cons of each implementation. IMHO the real benefit of the command pattern is the intrinsic ability to chain and undo commands. I would love to see how you use this in Elite Command.

Also, do you ever find your command classes growing too large? Is there one command class per use case?

11611e595f8866809b075a8e718e7600?s=50

Chris Vincent said:

Hey Mike -

I’m glad you enjoyed the article! Applying the Composite pattern to the Command pattern to get the chainable “CompoundCommand” is almost certainly distinct from DCI, although I also suspect that it could be worked into the DCI approach. I think I will write an article about how I applied this to Elite Command to cut down on Ajax requests.

So far, my Command classes have stayed very small, with the exception of the EndTurn Command. This Command is responsible for setting up the next turn of the game, resetting unit stats, granting the new player their credits, and determining any new losers or winners. I approached this by breaking it up into smaller methods for each step. It’s essentially a procedural algorithm, so I don’t fret too much about it. Comprehensive unit testing is definitely my friend for that one.

A little off-topic here, but the Commands for Elite Command are actually so succinct and so uniform that I’m planning on generating their JavaScript counterparts (which look almost exactly the same) from the Ruby classes by traversing the syntax tree. The JavaScript counterpart Commands do the exact same steps, except they’re manipulating the client-side rather than server-side state, using the same method names to do it. This allows for a snappier UI because the game board doesn’t need to wait for the server to update itself, but it also results in me writing each Command once in Ruby and once again in JavaScript in almost the exact same fashion. Once I get the free time, I’m definitely going to generate the JavaScript from the Ruby versions. It’s going to be a fun project!

D9ae6a14325712c09678c711ce2010a0?s=50

Mike Pack said:

Whoa. That last part of your response is freaking awesome! Sounds like a fun project for sure.

5923e70879e9586f2bf1c301e3f80e22?s=50

Kris Leech said:

Really good article, the command pattern sounds like a good choice for many applications which do not need a full DCI. I feel many Rubyists may well use this pattern without knowing its official name. One thing I’ve never seen is how errors within DCI/Command are reported back to the controller. Most seem to return true/false but I would guess the objects within the command need exposing as attr_reader for the controller to grab the errors. Or maybe the command should package up all the errors on error. Any thoughts?

11611e595f8866809b075a8e718e7600?s=50

Chris Vincent said:

Hey Kris -

When I constructed my Commands, I simply raised exceptions as needed. I had a CommandError class which was raised with a message and rescued by the controller action. The action would then trigger an email to myself and render a JSON-formatted error message to be handled by the client. I would assume the same approach could be taken with DCI. Of course, some exceptions could be handled by the Command or DCI Context without the controller ever having to to know; it all depends on your situation.

833fbd3f58a8ebbeb43756e8634334ed?s=50

Nicholas Henry said:

Great article, Chris—thank you for writing this up.

I wanted to clarify some statements from the article:

“Explicit definition of user roles. In the Agile development methodology, user stories are defined as being the goal a user who represents a given “role”, such as a “Customer”, “Client”, or “Administrator”. In DCI, abilities tied to these roles are defined explicitly in their own Role modules which extend the actor instances at runtime.”

“You don’t get the one-to-one mapping of Agile user roles to Role modules that you do with DCI.”

I believe roles in DCI do not necessary have to be user roles as the article suggests. The “classic” DCI example of a transfer defines a TransferMoney context (or user case/command) where two account objects play either a SourceAccount or a DestinationAccount. That is, in the TransferMoney context an amount is transferred from the SourceAccount to the DestinationAccount. As you can see, this example doesn’t involve a user role, but rather the roles that the domain/data objects play.

833fbd3f58a8ebbeb43756e8634334ed?s=50

Nicholas Henry said:

Regarding:

“I still think DCI is an excellent solution, but it occurs to me that it might be a little heavyweight for some applications.”

I think it’s important to keep in mind that DCI is not an all or nothing proposition. As Gertrud Bjørnvig and Jim Coplien points out in their book Lean Architecture>

“DCI applies only when we have use cases that describe a sequence of tasks that are directed to some end-user goal. If the “use case” is a simple, atomic action, then use cases are the methodological equivalent of shooting a fly with an elephant gun.”

Also Jim Gay, a Rubyist who has been studying this in-depth, suggests that “a role is a variable”. Meaning that it’s not always necessary to “extend” the data object in a context.

833fbd3f58a8ebbeb43756e8634334ed?s=50

Nicholas Henry said:

Regarding:

“Like DCI, the Command pattern encapsulates individual business goals into their own classes, leaving models concerned purely with access, validation, and persistence.”

That’s not to say a model has no methods at all. Atomic actions that act on a model should be represented as a method on that model. Again I refer to the DCI example of MoneyTransfer. The Account object has methods to decrease_balance and increase_balance by a specific amount. The atomic actions are independent of what role they play and the context should not be adjusting the balance attribute of the Account. We certainly shouldn’t be throwing out good practices such as encapsulation and information hiding with these alternative paradigms.

11611e595f8866809b075a8e718e7600?s=50

Chris Vincent said:

Hey Nicholas -

Great feedback. Especially interesting is the more abstract definition of “Roles” you provided. And as always, choosing an approach is always a balancing act of trade-offs; the point at which a user end-goal becomes less than “atomic” and should be represented by anything other than a method on a model is a grey area. More than, say, five lines of code? Or perhaps a sizable chunk of code in which it is unclear to which model it should really belong? Perhaps when more than one write to the database is necessary, an end-goal is no longer atomic. These are all open questions, and as always, there is no one-size-fits-all solution in good engineering.

At any rate, DCI and the Command pattern are both excellent approaches to encapsulating such end-goals clearly. I think the balance lies in which advantages are more important to the given action. Is it helpful to be able to compose end-goals as chains of actions, or perhaps of chains of actions? If so, the Command pattern might be my wieldy. Does adding Roles into the mix bring a lot of value through clarity and reusability into the code’s purpose? If so, DCI might just be the right tool for the job.

259bee8652a45c76358945941a9bc0c8?s=50

Diego said:

Great article! I think DCI is a very nice pattern but it has its drawbacks. Apart from implications on performance, boilerplate and maintainability, the thing I dislike the most is the fact that it separates data from behavior. OOP is about bundling together data and behavior. It allows to maximize information hiding, encapsulation and modularity. These three are, in my opinion, the most important aspect of software design. In this aspect, DCI suffers at the same way as J2EE does. I have written an article about the topic on my blog. Hope you can find some time to read and tell me what you think.

259bee8652a45c76358945941a9bc0c8?s=50

Diego said:

Regarding the Command pattern, I think it can also be very useful but I still would not place any domain logic in it. It can be a good place for application logic but I would still keep the domain logic separated.

Say Your Peace

Textile is allowed. Be polite. Be rude. I don't really give a fuck.

Your email will be kept safe and will never be publicly displayed.

 

Wha...?

11611e595f8866809b075a8e718e7600

Chris Vincent is a 20-something drummer, producer, and engineer from the Bay Area. This is where he writes whatever the hell he wants whenever the hell he wants to write it. Check your expectations at the home page.

Obligatory tag cloud

me san francisco bicycling ruby tdd css tools iphone games rails facebooker queue facebooker facebook arduino activerecord sql css tools development process company culture spam akismet bluepill god programming elite command analytics active record patterns command pattern dci testing rspec

Recent posts

Feed me

Atom is cool.

Get in touch

Questions, comments, ideas?
Let's talk.

Unabashed self-promotion

Recommend Me