After my previous post about DCI and Rails some people contacted me regarding best DCI patterns. As the topic of DCI is still new I didn't want to engage into creating patterns - I think they should emerge from existing projects. The idea of DCI seems to propagate in the Ruby/Rails circles - there was a RailsConf talk on this topic and more DCI blog posts appear. If you haven't heard about DCI yet, please read my last post first: DCI and Rails.
Recently I got an email from Pavel Mitin in which he describes his pattern for creating DCI contexts in Ruby. As this pattern matches exactly with my approach I think it's worth sharing and recommending as a pattern.
This example consists of two base classes - Human and Address. Objects of the Human class can play two roles: Policeman and Thief, while the Address can sometime play the role of FakeAddress.
Splitting classes into roles and injecting roles runtime is only a small part of DCI. The bigger thing is the idea of Contexts, which you can also think like a User Story or a Use Case (as a big simplification). In Pavel's example we have the context of the Policeman arresting the Thief. In this case the thief object has the address object extended with a FakeAddress.
This example shows some interesting concepts. As you see we're not replacing the thief's address - as in this world we think that humans have one address. What we're doing is extending the address object and "overwrite" the to_s method runtime in the context of arresting. I assume here the to_s method abstracts the thing that the thief is "saying" to the policeman.
The most important part, however is the way the context class is written. ArrestContext is a class with a constructor and the execute method. It's the constructor job to wire all the relevant roles to the actors. Once the roles are injected the execute method can start the "procedure".
There are still questions remaining regarding the contexts. Who should access the context classes? Let's assume that we have an application object in our project which represents the whole system and that it's a web Rails app. Should the application object (a model) know about the contexts?
Here are some options:
1. the application object has access to all the possible contexts. The controller just calls methods like: application.register_new_user(..) and it's the application that initializes the context.
2. controller has the knowledge about the specific "global" context it handles: NewUserContext.new(..).execute
3. controllers as we know them (Rails) disappear, the wiring between actions and context takes place in a configuration file.
All of the Ruby DCI examples so far seem a little bit verbose. It's because they are direct translation from languages that are not as dynamic as Ruby. It's not my goal at the moment to create a nicer API but I'm pretty sure we can come up with nicer syntax. Can you help? Especially, I think that the context classes can become some kind of configuration files - what role to inject to which objects, then which method starts the "procedure".
I'd love to know your opinion on this topic. Does it make sense to you? Is the DCI concept clear to you?