SOA for the Little Guys

My article titled "SOA for the Little Guys" was just published on RubySource. It covers breaking apart a monolithic app into services with testing and Sinatra as the driving forces.

Give it a read!

SOA for the Little Guys

Posted by Mike Pack on 02/13/2012 at 10:04AM

Tags: architecture, soa, sinatra, ruby

DCI Role Injection in Ruby

Injecting Roles into objects in Ruby has been a hot topic in the DCI community. What's the correct approach when augmenting an object at runtime? I want to explore some techniques around getting objects to behave properly. Jim Coplien, a huge proponent behind the DCI architecture, makes a great point:

“There are a million ways to map object roles to objects, to inject methods into classes, and to make the context available to the methodful object roles.”

I want to objectively pose Role injection options with supporting benchmarks. If there's any further techniques outside of those discussed or better ways to accomplish them, I would love to hear them in the comments or by email.

Final disclaimer: Objects should be augmented with Roles in the scope of Contexts as demonstrated in The Right Way to Code DCI In Ruby. This article will not express Contexts, but rather focus directly on the Role injection itself.

The Canonical #extend

This form of Role injection has been used widely. It's the only form of Role injection used by Jim Coplien in his book Lean Architecture.

data_object.extend RoleWithMethods

The concerns around this technique are justified. The primary issue is the inability to #unextend the data_object. As data objects are passed around numerous Contexts or as they play many Roles in a given Context, we could run into naming collisions between methods in those Roles. Further, if we forget to inject a Role, we could be producing logical errors where our data object contains the necessary method but it belongs to the wrong Role. Take the following example:

# Within Context A
data_object.extend RoleWithMethods

# Object enters a different Context
# We forgot to inject the following Role
# data_object.extend RoleWithMethodsTwo
data_object.injected_method #=> Calls injected_method from RoleWithMethods when we expected injected_method from RoleWithMethodsTwo

Logical errors aside, naming collisions using #extend exist in same form as including modules at class definition. That is, two modules included during class definition could suffer from naming collisions as well. The issues of long-lived objects, naming collisions and logical errors can be further alleviated by passing IDs to the Context class instead of the objects themselves, as demonstrated in The Right Way to Code DCI In Ruby. Further, even though methods are overwritten, you'll always invoke the one you're looking for. The invoked method will belong to the most recently #extended Role.

The other disadvantage of using #extend is the way it affects an object's hierarchy. #extend works similar to inheritance; it injects the Role as a singleton ancestor of the object. Any derivation of the object will then contain the #extended Role as an ancestor, something that won't occur if you include the module at class definition. #extending objects could lead to some hard-to-debug instances where it's difficult to track the point at which the Role entered the object's lookup table.

I benchmarked #extend in Benchmarking DCI in Ruby with Ruby 1.9.2. Here's a rerun:

        user       system     total       real
extend  8.780000   0.010000   8.790000 (  8.785185)

Here's the same benchmark in Ruby 1.9.3:

        user       system     total       real
extend  2.370000   0.000000   2.370000 (  2.377565)

A significant drop from 1.9.2 to 1.9.3.


This technique uses mixology, a library designed to alleviate Ruby's lack of #unextend. The last commit was in 2009 and the library appears to be effectively dead. It works in 1.9.2 but refuses to compile the native extensions in 1.9.3. 1.9.3 incompatibility aside, here's how it's used:

data_object.mixin RoleWithMethods
data_object.unmix RoleWithMethods

This fits the DCI mold better than #extend. It gives the Context the ability to #unmix the modules as the Context comes to a close, ideal for managing scope creep as objects move between Roles and potentially Contexts.

I've benchmarked mixology against #extend. I include benchmarks with and without #unmix. Realistically, the benchmark of focus should be with the use of #unmix as it's the core reason for introducing Mixology.

                    user       system     total       real
extend              8.710000   0.010000   8.720000 (  8.712797)
mixology w/o unmix  8.670000   0.000000   8.670000 (  8.671777)
mixology w/ unmix   12.570000   0.010000  12.580000 ( 12.567442)

It's clear from these benchmarks that Mixology's #mixin performs on par with #extend in Ruby 1.9.2. However, #unmixing modules incurs a significant performance hit.

SimpleDelegator, Forwardable, Facades and Wrappers

It's feasible to wrap data objects in wrappers instead of injecting Roles directly. The beauty of doing so is that it encapsulates the object being modified by #extend. All behavior and the object itself it isolated within the wrapper. This technique can be demonstrated as follows:

class DataObjectDelegator < SimpleDelegator
  def initialize(data_object)
    super data_object.dup.extend(RoleWithMethods)

data_object =

The idea is to make the delegator function exactly like a data object without affecting the object itself. In the initializer, we duplicate the object and #extend it with our Role. We must duplicate the object (potentially having side effects of its own) so we don't pollute the original object. Without duplication, the original data_object would contain the injected Role after the delegator has been removed. Using Forwardable works in a similar fashion to SimpleDelegator.

A more elegant solution is presented by Chris Bottaro called schizo which uses facades to wrap the data objects. It isolates the object containing the Role and allows you to act on it within block: do |object_with_role|

After the block executes, the data_object returns to normal, without the Role injected.

Wrappers can be a nice way to abstract object construction away from the core DCI implementation. They isolate and protect the extended object but they also add yet another layer of complexity. Managing delegators can feel cumbersome in comparision to natively supported #extend.

I benchmarked SimpleDelegator, Forwardable and Schizo to see how they perform in Ruby 1.9.2:

            user        system    total       real
Delegation  13.100000   0.010000  13.110000 ( 13.099318)
Forwardable 11.770000   0.010000  11.780000 ( 11.772542)
Schizo      52.900000   0.380000  53.280000 ( 53.227285)

Not ideal. Each of these performs quite poor compared to native #extend, especially in 1.9.3.

Wrappers can also come in the form of Role wrappers. In this instance, the data object would be passed as an argument to Role and the Role would then act on the argument rather than "self". For instance:

class RoleWithMethods
  def initialize(data_object)
    @data_object = data_object

  def injected_method
    # Instead of:
    # self.some_data_method

All wrapping techniques suffer from the same problem: they're not DCI. Read on to understand why.

DCI and Object Orientation

A founding concept behind DCI is object-orientation, though, not the object-orientation most of us are used to. Many of us think object-orientation is a means of instantiating classes and calling their methods. However, this is not "true OO". "True OO" involves manipulating objects at runtime. It involves objects with just enough methods to get the job done. Instantiating a modern-day Rails model means we're passing around objects that get the immediate job done plus all other jobs the model can perform. Joe Armstrong puts it nicely in Coders at Work:

“The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.”

"True OO" means constructing objects at runtime as needed, not constructing classes and instantiating them to perform a subset of tasks. That's class-oriented programming. "True OO" means capturing the end-user's mental model as objects in memory, akin to Simula and Smalltalk. I'll leave a strong comparison between class-oriented and object-oriented programming to another article.

The reason wrapper techniques can't fall under the DCI umbrella is they're not directly manipulating Data objects. Their duplicating and obfuscating objects to work around Ruby's inability to properly manage object augmentation. Having an #unextend method would alleviate the need for wrappers entirely. It really leads to the question: Is Ruby right for DCI? As it stands, the answer is no. Can we use tools and composition to enhance Ruby so it'll perform the functions we need for DCI? Yes, but we're not quite there. Tools in this space need more love, Mixology as a prime example.

Happy injecting!

Posted by Mike Pack on 02/08/2012 at 01:53PM

Tags: delegation, forwardable, extend, architecture, dci

The Right Way to Code DCI in Ruby

Many articles found in the Ruby community largely oversimplify the use of DCI. These articles, including my own, highlight how DCI injects Roles into objects at runtime, the essence of the DCI architecture. Many posts regard DCI in the following way:

class User; end # Data
module Runner # Role
  def run

user = # Context
user.extend Runner

There's a few flaws with oversimpilfied examples like this. First, it reads "this is how to do DCI". DCI is far more than just extending objects. Second, it highlights #extend as the go-to means of adding methods to objects at runtime. In this article, I would like to specifically address the former issue: DCI beyond just extending objects. A followup post will contain a comparison of techniques to inject Roles into objects using #extend and otherwise.

DCI (Data-Context-Interaction)

As stated previously, DCI is about much more than just extending objects at runtime. It's about capturing the end user's mental model and reconstructing that into maintainable code. It's an outside → in approach, similar to BDD, where we regard the user interaction first and the data model second. The outside → in approach is one of the reasons I love the architecture; it fits well into a BDD style, which further promotes testability.

The important thing to know about DCI is that it's about more than just code. It's about process and people. It starts with principles behind Agile and Lean and extends those into code. The real benefit of following DCI is that it plays nicely with Agile and Lean. It's about code maintainability, responding to change, and decoupling what the system does (it's functionality) from what the system is (it's data model).

I'll take a behavior-driven approach to implementing DCI within a Rails app, starting with the Interactions and moving to the Data model. For the most part, I'm going to write code first then test. Of course, once you have a solid understanding of the components behind DCI, you can write tests first. I just don't feel test-first is a great way of explaining concepts.

User Stories

User stories are an important feature of DCI although not distinct to the architecture. They're the starting point of defining what the system does. One of the beauties of starting with user stories is that it fits well into an Agile process. Typically, we'll be given a story which defines our end-user feature. A simplified story might look like the following:

"As a user, I want to add a book to my cart."

At this point, we have a general idea of the feature we'll be implementing.

Aside: A more formal implementation of DCI would require turning a user story into a use case. The use case would then provide us with more clarification on the input, output, motivation, roles, etc.

Write Some Tests

We should have enough at this point to write an acceptance test for this feature. Let's use RSpec and Capybara:


describe 'as a user' do
  it 'has a link to add the book to my cart' do
    @book = => 'Lean Architecture')
    visit book_path(@book)
    page.should have_link('Add To Cart')

In the spirit of BDD, we've started to identify how our domain model (our Data) will look. We know that Book will contain a title attribute. In the spirit of DCI, we've identified the Context for which this use case enacts and the Actors which play key parts. The Context is adding a book to the cart. The Actor we've identified is the User.

Realistically, we would add more tests to further cover this feature but the above suits us well for now.

The "Roles"

Actors play Roles. For this specific feature, we really only have one Actor, the User. The User plays the Role of a customer looking to add an item to their cart. Roles describe the algorithms used to define what the system does.

Let's code it up:


module Customer
  def add_to_cart(book)
    self.cart << book

Creating our Customer Role has helped tease out more information about our Data model, the User. We now know we'll need a #cart method on any Data objects which plays the Customer Role.

The Customer role defined above doesn't expose much about what #cart is. One design decision I made ahead of time, for the sake of simplicity, is to assume the cart will be stored in the database instead of the sesssion. The #cart method defined on any Actor playing the Customer Role should not be an elaborate implementation of a cart. I merely assume a simple association.

Roles also play nicely with polymorphism. The Customer Role could be played by any object who responds to the #cart method. The Role itself never knows what type of object it will augment, leaving that decision up to the Context.

Write Some Tests

Let's jump back into testing mode and write some tests around our newly created Role.


describe Customer do
  let(:user) { }
  let(:book) { }

  before do
    user.extend Customer

  describe '#add_to_cart' do
    it 'puts the book in the cart' do
      user.cart.should include(book)

The above test code also expresses how we will be using this Role, the Customer, within a given Context, adding a book to the cart. This makes the segway into actually writing the Context dead simple.

The "Context"

In DCI, the Context is the environment for which Data objects execute their Roles. There is always at least one Context for every one user story. Depending on the complexity of the user story, there may be more than one Context, possibly necessitating a story break-down. The goal of the Context is to connect Roles (what the system does) to Data objects (what the system is).

At this point, we know the Role we'll be using, the Customer, and we have a strong idea about the Data object we'll be augmenting, the User.

Let's code it up:


class AddToCartContext
  attr_reader :user, :book

  def, book), book).call

  def initialize(user, book)
    @user, @book = user, book
    @user.extend Customer

  def call

Update: Jim Coplien's implementation of Contexts uses AddToCartContext#execute as the context trigger. To support Ruby idioms, procs and lambdas, the examples have been changed to use AddToCartContext#call.

There's a few key points to note:

  • A Context is defined as a class. The act of instantiating the class and calling it's #call method is known as triggering.
  • Having the class method is simply a convenience method to aid in triggering.
  • The essence of DCI is in @user.extend Customer. Augmenting Data objects with Roles ad hoc is what allows for strong decoupling. There're a million ways to inject Roles into objects, #extend being one. In a followup article, I'll address other ways in which this can be accomplished.
  • Passing user and book objects to the Context can lead to naming collisions on Role methods. To help alleviate this, it would be acceptable to pass user_id and book_id into the Context and allow the Context to instantiate the associated objects.
  • A Context should expose the Actors for which it is enabling. In this case, attr_reader is used to expose @user and @book. @book isn't an Actor in this Context, however it's exposed for completeness.
  • Most noteably: You should rarely have to (impossibly) #unextend a Role from an object. A Data object will usually only play one Role at a time in a given Context. There should only be one Context per use case (emphasis: per use case, not user story). Therefore, we should rarely need to remove functionality or introduce naming collisions. In DCI, it is acceptable to inject multiple Roles into an object within a given Context. So the problem of naming collisions still resides but should rarely occur.

Write Some Tests

I'm generally not a huge proponent of mocking and stubbing but I think it's appropriate in the case of Contexts because we've already tested running code in our Role specs. At this point we're just testing the integration.


describe AddToCartContext do
  let(:user) { }
  let(:book) { }

  it 'adds the book to the users cart' do
    context =, book)

The main goal of the above code is to make sure we're calling the #add_to_cart method with the correct arguments. We do this by setting the expectation that the user Actor within the AddToCartContext should have it's #add_to_cart method called with book as an argument.

There's not much more to DCI. We've covered the Interaction between objects and the Context for which they interact. The important code has already been written. The only thing left is the dumb Data.

The "Data"

Data should be slim. A good rule of thumb is to never define methods on your models. This is not always the case. Better put: "Data object interfaces are simple and minimal: just enough to capture the domain properties, but without operations that are unique to any particular scenario" (Lean Architecture). The Data should really only consist of persistence-level methods, never how the persisted data is used. Let's look at the Book model for which we've already teased out the basic attributes.

class Book < ActiveRecord::Base
  validates :title, :presence => true

No methods. Just class-level definitions of persistence, association and data validation. The ways in which Book is used should not be a concern of the Book model. We could write some tests around the model, and we probably should. Testing validations and associations is fairly standard and I won't cover them here.

Keep your Data dumb.

Fitting Into Rails

There's not a whole lot to be said about fitting the above code into Rails. Simply put, we trigger our Context within the Controller.


class BookController < ApplicationController
  def add_to_cart, Book.find(params[:id]))

Here's a diagram illustrating how DCI compliments Rails MVC. The Context becomes a gateway between the user interface and the data model.


What We've Done

The following could warrant it's own article, but I want to briefly look at some of the benefits of structuring code with DCI.

  • We've highly decoupled the functionality of the system from how the data is actually stored. This gives us the added benefit of compression and easy polymorphism.
  • We've created readable code. It's easy to reason about the code both by the filenames and the algorithms within. It's all very well organized. See Uncle Bob's gripe about file-level readability.
  • Our Data model, what the system is, can remain stable while we progress and refactor Roles, what the system does.
  • We've come closer to representing the end-user mental model. This is the primary goal of MVC, something that has been skewed over time.

Yes, we're adding yet another layer of complexity. We have to keep track of Contexts and Roles on top of our traditional MVC. Contexts, specifically, exhibit more code. We've introduce a little more overhead. However, with this overhead comes a large degree of prosperity. As a developer or team of developers, it's your descretion on whether these benefits could resolve your business and engineering ailments.

Final Words

Problems with DCI exist as well. First, it requires a large paradigm shift. It's designed to compliment MVC (Model-View-Controller) so it fits well into Rails but it requires you to move all your code outside the controller and model. As we all know, the Rails community has a fetish for putting code in models and controllers. The paradigm shift is large, something that would require a large refactor for some apps. However, DCI could probably be refactored in on a case-by-case basis allowing apps to gradually shift from "fat models, skinny controllers" to DCI. Second, it potentially carries performance degradations, due to the fact that objects are extended ad hoc.

The main benefit of DCI in relation to the Ruby community is that it provides a structure for which to discuss maintainable code. There's been a lot of recent discussion in the vein of "'fat models, skinny controllers is bad'; don't put code in your controller OR your model, put it elsewhere." The problem is we're lacking guidance for where our code should live and how it should be structured. We don't want it in the model, we don't want it in the controller, and we certainly don't want it in the view. For most, adhering to these requirements leads to confusion, overengineering, and a general lack of consistency. DCI gives us a blueprint to break the Rails mold and create maintainable, testable, decoupled code.

Aside: There's been other work in this area. Avdi Grimm has a phenominal book called Objects on Rails which proposes alternative solutions.

Happy architecting!

This article is translated to Serbo-Croatian.

Further Reading

DCI: The King of the Open/Closed Principle
DCI With Ruby Refinements
DCI Role Injection in Ruby
Benchmarking DCI in Ruby

Posted by Mike Pack on 01/24/2012 at 12:58PM

Tags: testing, dci, ruby, rails, architecture