We talked about Representers earlier last week. Today, I wanna describe DCI and how it fits perfectly together with representers in Roar.

What is DCI?

For years we’ve been taught that “Fat Models, Skinny Controllers” is the way to organize code. Push all your domain code into your ActiveRecord classes. Many people, including me, agree that this is bullshit. This leads to bloated classes, usually combined with one or two object instances taking care of an entire workflow (aka request).

DCI is an architectural pattern that makes the model “fat-on-demand”: Data objects are enhanced with roles at runtime, but only when needed.

The DCI pattern consists of three concepts.

  1. Data objects are supposed to contain application data. Nothing else. This would be your ActiveRecord row instance, but without any predefined behaviour. Just data.
  2. Context objects define what is done in the current workflow. In Rails, a controller running an action could be a context object.
  3. Roles are behaviour for data objects in a particular context. In Ruby, a Rails controller could mixin a module (a “role”) into an empty model row and thus let it process something.

Hooray for DCI – But Why?

Now the thing is, I absolutely hate to hype something (except stuff I did, of course). However, DCI makes code better maintainable, easier to follow and way easier to test. Period.

BTW, did you already check out my new Roar gem? It’s hip!

Think of a classical model found in many Rails applications with maybe one hundred methods. Or fifty. Fifty methods to interfere with each other. In a DCI setup, an object has a limited behaviour scope. The limited knowledge makes it easier to debug and predictable.

One Model, Multiple Faces

Since representers are modules they fit perfectly into the DCI approach, making them roles. Let’s consider the fruit representer from last week, again.

require 'roar/representer/json'
 
module FruitRepresenter
  include Roar::Representer::JSON
 
  property :title
  collection :colors
end

You still can include this representer into its “host” class directly.

class Fruit
  include Roar::Representer
  include FruitRepresenter
end

The problem now is that the Fruit class is limited to one representation, only. Forever and a day you may only render one particular representation. The key about representations, however, is that one model may be represented by multiple, yeah, representations.

Now, back to the start, let’s assume the Fruit class is some empty ActiveRecord class without any prior knowledge of representations at all.

class Fruit < ActiveRecord::Base
end
 
lemon = Fruit.from_json("{\"title\":\"Lemon\"}")
#=> undefined method `from_json' for Fruit:Class

Yeah, there is no #from_json class method.

Representers On DCI

In order to use the DCI approach, we first need an instance. Here’s how the lemon instance, and only this instance, can be extended with the fruit representer.

lemon = Fruit.new.extend(FruitRepresenter)
lemon.from_json("{\"title\":\"Lemon\"}")
lemon.title #=> "Lemon"

Awesome! The built-in Ruby #extend method makes the lemon instance aware of its representation and mixes in #from_json and friends.

Naturally, this works in both ways.

orange = Fruit.find_by_title("Orange")
orange.extend(FruitRepresenter).to_json
#=> {"title":"Apple","colors":["green"]}

The mixed-in representer also lets us render the JSON document.

Gimme Complexity!

On a flat level with single objects, this works out-of-the-box with recent Roar/representable versions. Problems arise when we have nested setups.

module BowlRepresenter
  include Roar::Representer::JSON
  include Roar::Representer::Feature::Hypermedia
 
  property :location
  collection :items, :as => Fruit,
    :extend => FruitRepresenter
 
  link :self do
    "http://bowl/#{location}"
  end
end

Using the :extend option we can give representable a hint about the representer to use when rendering/parsing contained objects.

And, hey, this works.

empty_bowl = Bowl.new
empty_bowl.extend(BowlRepresenter)
emtpy_bowl.from_json("{\"location\":\"kitchen\", \
  \"items\":[{\"title\":\"Lemon\",\"colors\":[]}]}")
 
puts empty_bowl.items.first.title #=> "Lemon"

Discussion Needed!

Having representers on an object level makes the whole thing really clean and versatile. You can test document rendering and parsing without polluting classes. And you can have a FruitRepresenter, a RottenVegetableRepresenter and what else you like for your orange instance. BTW, oranges ain’t no vegetables.

We still discuss about the API and need your opinion. Right now, we got this

collection :items, :as => Fruit,
    :extend => FruitRepresenter

Here, :as describes the class that encapsulates the contained data. The :extend option helps representable to find the correct module for the nested item. Now, what about this?

collection :items, :class => Fruit,
    :module => FruitRepresenter

Does that make sense? These options really just describe what’s on the right side of the hash rocket. Thanks to Scott for discussion here. Now, tell us your story!

Tags: , , ,

26 Responses to “Ruby On REST 2: Representers and the DCI Pattern”

  1. Great examples!

    A small thing:

    “Data objects are supposed to contain application data. Nothing else. This would be your ActiveRecord row instance, but without any predefined behaviour. Just data.”

    I’ve tried with starting with AR objects as the data layer. It didn’t work very well, because there was too much persistence stuff coupled with the data layer. I think we need to distinct data from persistence.

    In the ideal world, we would start with simple PORO objects, extend it with DCI roles. Persistence could just become a role. It’s not possible with ActiveRecord, though. Unless anyone can force an object to change it’s super class runtime. Anyone?

  2. The problem I am having with DCI is, it slows the whole process, here’s a gist to compare “Object Oriented Programming” vs “Class Oriented Programming”, though we prefer OOP, but to extend the object at the runtime make it insanely slow.

  3. @Hao
    First make it work, then make it right, then make it fast. It seems that your problem is with the last part of that, which is to say that it’s not a DCI problem.

  4. @Jim, but it’s how the DCI is used in the real world, isn’t it? First, i need to initialize an instance of the class, then extend the instance with the “role” i’d like it to play. The problem I’m concerning with is the “extending” part, as long as it’s “extending”, it’s slow. Correct me if i am wrong, thanks a lot.


  5. nick

    I agree with Jim that you first should worry about architecture, then about performance since computers tend to get faster and faster anyway. However, my friend Konstantin pointed out that object extending at runtime seems to be a severe problem in the MRI: https://twitter.com/#!/konstantinhaase/status/144420650310832131


  6. Justin

    The idea seems interesting, but I’m confused as to how this is different from a traditional presenter or decorator?


  7. Curtis Carter

    The issue is with the method lookup cache. In MRI it apparently clears the entire cache. Jruby might be better as it has per class method lookup caches.


  8. nick

    @Justin: A representer wraps an object just like a decorator, provides rendering and parsing for representations and supports compositions. I could also call it “Configurable Document Parsing and Rendering Decorator”, but Representer sounds cooler.
    @Curtis: Good to know, maybe we can see some comparing benchmark to weaken the performance arguments against DCI.

  9. @nick, @jim, thanks a lot for pointing the problem out. I do agree with that we should focus on the domain objects (PORO), to make it work without worry about the lower layers of the whole stack, i do think DCI is designed for that purpose. https://gist.github.com/1486911, here’s my workaround for the “extending” problem, it just works, and we’ll see if it really keeps the promise to the SRP within the following discussion

  10. @Hao, how about a SimpleDelegator? Here is a gist that compares it to your results (with output from my machine):https://gist.github.com/1487108

  11. For the nested case, it seems that the :as => Fruit makes it harder to distribute representers separately from the ActiveRecord models they use to represent. Would the approach be to make sure the clients define the same class in the same module so the representer resolves the class fine?

    In generally, I really like this approach to the separation of concerns.


  12. Chris

    What’s wrong with just using plain old simple inheritance in this case? Have a separate class, inherited from the main class, for each type of situation?

  13. Runtime extension is certainly slower than *not* doing runtime extension (kinda obviously), but I wouldn’t exactly call it “slow.” Even at 15 seconds, you’re doing 1M extensions and that comes down to 0.015 milliseconds per extension. There are certainly applications where that might add up because you’re doing a zillion object extensions in a very short period of time (as the benchmark does), but there are also huge swatch of domain spaces where that kind of time is a blip on a blip on a blip.


  14. Kevin Triplett

    If I read the gist from Konstantin correctly, it seems that the extend is slower but not significant? Maybe under extremely heavy load?

    But I have been looking to make my models less fat, some of mine are incredibly fat and it hurts maintenance. DCI looks like a good solution, thanks for rocking the boat, Nick!

    The only thing I worry about is the nested case. Is there a way to dynamically nest the Representers or is that too complex? Like having a bowl of fruit or a bowl of vegetables, mix in the fruit or vegetable after we know whether it is oranges or carrots.

  15. If your object have longer life cycle, it has been passed through several method, You need care about whether your object has a method so you need extend your object every time you use it. It is tedious!

    I still like include modules in model class, like this: https://gist.github.com/1488989

  16. for the benchmark, I read a comment in another article(http://andrzejonsoftware.blogspot.com/2011/02/dci-and-rails.html?showComment=1297420265571#c2685425907614135938) said:

    I’ve benchmarked #extend indeed. We have to take various aspects into account. No doubt already defined method call is way faster than extend + method call. Approximately 800-1200%.

    But we can’t benchmark that way, that’s true only for very thin classes. We have to look for overall application performance. Usually classes are fat and initialization process is more complex for ‘monolithic’ objects.

    Giving objects only needed capabilities significantly decreases initialization process thus we have some performance benefits.

    GC is even more important. Simpler objects = less memory consumption = less garbage collector. Average rails application wastes 20%-30% time in GC.

    In my opinion one can’t straight said that using .extend is faster or not. It will be slower when application uses very simple objects and may be faster for very complex ones.

    Basing on my observations overall performance is unaffected and as Andrzej mentioned it may even perform better.

  17. +1 to what Allen Wei said. Looking at an approach first from the standpoint of micro-optimization is putting the cart before the horse. Simple benchmarks are nice, but complex applications are complex.

    @Nick great work with roar!


  18. nick

    @Gabe: Hey thanks for that benchmark example. Really helpful to see how extend is significantly slower – in a 1,000,000 iterations loop ;-) But I guess we need some real life experiences with this.

    @Pete: Very good point. Indeed, the client has to provide empty “host” classes so the representer can fill them with data. Probably the cleanest solution as the framework itself doesn’t worry what particular class it uses.

    @Chris: If you want multiple representations of one model, you can’t use inheritance. You can still create a dedicated representer class, include the module and then inherit if you like that. Roar was like that a couple of weeks ago but it turned out to be very limited. Now, people can use either inheritance, class or object module extension.

    @Bill: Agreed, we’re talking about micro-micro-seconds here.

    @Kevin: Yupp, the represented properties are stored on instance level, so you could theoretically add and remove properties for particular objects.

    @Allen, @Jim: Absolutely. I guess people should worry about concepts and not about speed in the first place. If your code performs great but is a mess and unmaintainable, well, great, what you get from that? A running system. Not to be touched. ;-)

  19. [...] about how DCI and Representers in Roar work together. The post briefly describes DCI in Ruby and Rails and how this can be used to write REST [...]


  20. Kevin Triplett

    Hey Nick — how about a blog post on how to use apotomo/cells without ActionController? That would be sweet! :D

  21. AFAIK, extending objects at runtime clears the method cache that ruby has which results in performance. We should not optimise prematurely yet keep our mind open when writing code. No pointing writing code that we know could lead to performance issues later on.

  22. [...] jQuery("#errors*").hide(); window.location= data.themeInternalUrl; } }); } nicksda.apotomo.de – Today, 10:56 [...]


  23. Ginkgochris

    How would you implement a list resource, where the list has no class on its own:

    {
    “friends” : {
    “href” : “http://example.org/users/32gu/friends”,
    “rel” : “friends”,
    “total” : “2″,
    “elements”: [
    {"name" : "joe", "href" : "http://example.org/users/rzh4028"},
    {"name" : "susan", "href" : "http://example.org/users/gv5333g"}
    ]
    }

  24. A bit late for Ginkgochris, but I ran into the same issue. My solution is to use an anonymous class:

    klass = Class.new do
        attr_accessor :elements
    end
    d = klass.new.extend(FriendsRepresenter)
    d.elements = @friends
    d.to_json

    The FriendsRepresenter would have a :collection of :class => Friend, :extend => FriendRepresenter.

  25. Although this is pretty late past the discussion, I have implemented DCI Role injection that does not use #extend and therefore does not affect the method cache. It also allows roles to be removed as well as added.

    http://rebo.ruhoh.com/dci-role-injection-in-ruby-using-method-aliasing/

Leave a Reply