Hexagonal Rails: Objects, Values and Hexagons

This is the second post in a series about my experience of applying a GOOS-style hexagonal architecture to a Ruby on Rails application. In the first post, I talked about why the idiomatic, connected, style of Rails architecture might have advantages in the short term, but that I want a more modular architecture to make it habitable for the long term.

In this post, I’ll explain a couple of the fundamental concepts that will be used in the rest of the series, which should give you some new ideas to think about as you’re working on your own Rails applications. We’ll talk about the tell, don’t ask style of object communication, and about the difference between objects and values. Let’s start by explaining what that funny term hexagonal architecture actually means.

So what is a hexagonal architecture?

The fundamental idea behind this style of architecture is to isolate the core domain of your program—the bit that defines its unique and interesting behaviour—from the technical infrastructure that enables it to talk to the outside world. Technical infrastructure means things like databases, file systems, user interfaces, web services and message queues.

We can visualise it as two hexagons, one inside the other, as in the diagram below. The inner hexagon contains our core business logic. It doesn’t know or care anything about the details of how it will communicate with the outside world, but it exposes ports where we can plug in adapters that fulfil that role. The adapters sit in the outer hexagon, and translate between our core domain and the technology-specific domain of a particular piece of infrastructure.

Hexagonal Architecture Sketch

That’s it, nothing fancy. The word hexagon isn’t actually really very important but I like to think it as a metaphor for the modular, pluggable nature of this architecture. It’s catchier than ports and adapters, too.

Keeping this separation between the inner and outer hexagons, between the core behaviour and infrastructure details of our program, gives us a few advantages:

  1. Our code is easier to understand. When we’re working in the inner hexagon, all the language and concepts we see are related to our core domain, without the distraction of the technical realities of connecting it to the real world. When we’re working in the outer hexagon, we can concentrate on the job of mapping core domain events and data onto the domain of the technical infrastructure that we’re writing an adapter for, without the distraction of business logic.

  2. We can easily replace pieces of infrastructure, such as swapping out a relational database for a key-value store, or using an in-memory database for fast acceptance testing, without modifying the core domain at all.

  3. Our tests can run fast. The code in the core domain does not depend on Rails or any other large external libraries, so its tests are lightning quick. Once we start to recognise core domain logic and push it inside the inner hexagon, more and more of our tests are running against plain old Ruby objects that are quick to instantiate. We have separate tests for our adapters that ensure they integrate with their respective technology, but we don’t need to run them as often as we used to. We only need to run those slow tests when the adapter needs to change, and because our business logic has moved into the core domain, that’s not very often.

There are apparent costs too. As I’ve moved the Relish codebase towards a more hexagonal style, I’ve noticed that there’s more code appearing. However, each piece of code is simpler to understand, and the pieces are much less coupled together, giving me flexibility for the future.

For me, probably the biggest factor in achieving this flexibility has been finally understanding an object-oriented design axiom I’ve been hearing for a long time: that objects should communicate by telling each other things, rather than asking each other questions.

Tell, don’t ask

Your Rails application most likely has a problem with procedural code. When I talk about a procedural style of programming, I am talking about this kind of thing:

class PublishersController < ApplicationController
  def create
    @publisher = Publisher.new(params[:publisher])
    @publisher.creator_user = current_user

    if @publisher.save
      current_user.add_membership(@publisher)
      redirect_to publisher_collaborators_path(@publisher),
        :notice => 'Publisher created successfully. You can now add collaborators.'
    else
      render :index
    end
  end
end

This is a fairly typical Rails controller action method. We can read this code and quite easily understand what is going to happen when it runs. This is the advantage of a procedural style – it reads like a story. The problem with this style really hits us when we want to change the behaviour.

Imagine we want to add an activity feed to our application, so that as a side-effect of successfully creating a new publisher, we want to post a message to the user’s activity feed. The way things stand, we’d probably be inclined to squeeze another line into the top branch of that if statement:

...
if @publisher.save
  current_user.add_membership(@publisher)
  current_user.feed_entries.create! content: "Created a new publisher #{publisher.name}."
  redirect_to publisher_collaborators_path(@publisher),
    :notice => 'Publisher created successfully. You can now add collaborators.'
else
...

Over time, as more and more behaviour is added to the application, this is only heading in the direction of chaos and ugliness. A more object-oriented design would allow us to plug in the extra activity feed behaviour without modifying this code at all, and it’s the tell-don’t-ask principle that will guide us into producing that kind of code.

We don’t have space for an example here, but we’ll see plenty of them as the series unfolds.

Your domain is in the protocols

Let’s examine that controller action again, looking in detail at the messages passing between the different objects in the scenario where the publisher is saved successfully. All in all, there are six different actors in this little scene: the Controller, the Publisher class, the new Publisher instance, the current User instance, the user’s Feed Entries collection, and Rails itself. Let’s imagine this method as a conversation between them:

Controller: Dear Publisher class, please may I have a new instance using these parameters? [Query]

Publisher Class: Certainly, here you are. [Query Response]

Controller: Publisher instance, this User instance is your creator_user! [Command]

Controller: Publisher instance, attempt to persist yourself! [Command]

Controller: Hey Publisher instance, did the attempt to persist yourself succeed? [Query]

Publisher instance: Thanks for asking! Yes, it went perfectly. [Query response]

Controller: User instance, grant yourself membership of this publisher instance! [Command]

Controller: User instance, can I see your feed entries collection please? [Query]

User instance: Oh alright then, here it is. [Query response]

Controller: Feed entries collection, create and persist a new entry with these parameters! [Command]

Controller: Rails, redirect to the following path, and show this message to the user! [Command]

In total, there are eleven different messages passing between the six different objects in play as this method runs. Of these, five messages are commands, and the other six are queries or their responses. We’re not even counting the potential for the call to feed_entries.create! to raise exceptions – we’ll talk about that some other time.

A revelation for me as I re-read GOOS recently was this: Your domain model is not in the classes you create in your source code, but in the messages that the objects pass to one another when they communicate at runtime.

In other words, the structure of these conversations—the protocols—is what’s most interesting about your application. The simpler you can keep these protocols, the easier your code will be to change in the future. Code that involves queries is inevitably more complex, because the code asking the question needs to then act upon the answer. Using a tell, don’t ask style encourages us to push that action off into other objects. Smaller, more re-usable objects.

So is there ever a valid case for using queries? There is, and that’s when the object is a value.

Objects and values

The authors of the GOOS book distinguish between just two different categories of class in an object-oriented program: objects, and values. As Rails programmers, we can find these two categories confusing because many of the objects we’re used to dealing with, particularly ActiveRecord::Base subclasses, fit into both categories. Nevertheless, I’ve found these two labels extremely useful in helping me think about how I structure my own core domain code.

Objects are the things that get work done. They may carry state, but they don’t expose it. They receive messages from collaborating objects, and make decisions about what messages to send to their collaborators.

Values carry state. They’re immutable: once created, they can’t be changed. Values have query methods that either return component parts of their state, or the results of calculations on that state.

So I’ve explained that a hexagonal architecture involves separating the code that allows your application to talk to the outside world, from the code that is the application itself. I’ve talked about how, when designing that code, it’s important to think about the protocols between the objects, and I’ve talked about the difference between objects and values.

I realise that these first two posts have been fairly abstract and theoretical, but I really felt I needed to put down some terminology before we could continue with the story. In the next post we’ll get much more practical and look at a pattern for applying tell-don’t-ask in my Rails controllers which I’m calling passive controller.

Published by Matt

I write software, and love learning how to do it even better.

Join the Conversation

6 Comments

  1. Hi!
    I’ve been playing with similar idea lately.

    Haven’t completed the cycle, but I do have an example that goes along these lines: https://github.com/elvanja/roar_example
    It is still unfinished and perhaps a bit off topic since it was created with the idea to be a showcase for roar gem, but is basically “gemified” into several packages.

    Will this series of articles result in something similar? I would like to compare ideas 🙂

    And a link that goes through this hexagonal idea too:
    http://alistair.cockburn.us/Hexagonal+architecture

    Looking forward to reading the rest of the series 🙂
    Vanja

  2. Thank you for these articles. I hope it’s just the beginning.

    Hexagonal Rails is a very interesting topic and seems to be a pretty new idea in the community. Therefore, it is hard to find a lot of practical examples of how the existing code should be decoupled from the Rails application.

    One of my biggest concerns is models refactoring. As I understand, in the hexagonal architecture ORM (ActiveRecord / DataMapper) acts as an adapter, and all the code, that calls ORM dependent methods, should be kept inside the model class: attribute definitions, validations, associations, and finders.

    My two questions are:

    1. As the Rails model cannot be tested without invoking Rails with all of its dependencies, does it mean, that my model validations do not belong to business logic, and should not be tested by unit tests? It doesn’t seem to make much sense to test validations at integration level either, since some of the models might be called from UI, others as background tasks, and etc. Then how do I test it?

    2. If ORM acts as an adapter, it means, that there cannot be any Model.find(id) (ActiveRecord) or Model.get(id) (DataMapper) calls outside of the Model class (app/models/model.rb), since that would create a dependency on a specific ORM for the entire application. Nevertheless, if I keep all the model related database calls in the same Model class, doesn’t that break the principles of Single Responsibility (it will contain queries for different behaviors of, for example, User model) and Open/Closed (which will cause endless modifications instead of extensions of the same model)?

    Again, I am very interested in this topic, but such dilemmas as these make it hard move to Hexagonal Rails camp and I would be grateful for any possible solutions or examples.

  3. I really like this topic. Uncle Bob has been given talks lately about this approach:

    http://www.confreaks.com/videos/759-rubymidwest2011-keynote-architecture-the-lost-years

    http://vimeo.com/43612849

    I’ve been using this approach in 2 new projects I’m working on. One is a groovy project and the other one is a python project.
    At first I threw away lots of code trying to figure out how things should go, where to put them, etc. It was getting a little frustrated, but then the light bulb lit. And things are going much smoother now. Tests are blazingly fast, and the code is clean (IMO). So, don’t be afraid to experiment and throw away code. I think sometimes that keeps us back. We invest an entire week on something, and we don’t throw it away even if we realize it is ugly and wrong. This happens alot in the initial phase (at least for me).

    As for Steve’s question regarding SRP in hexagonal models: it abides more to SRP than using ActiveRecord, GORM, or Django’s ORM the way they are prescribed to be used in their frameworks. This was the hardest thing for me to grasp, but it eventually kicked in. The persistence is not attached to the domain model, but to the ‘interactors’ as Uncle Bob calls them, or to your use cases. So if I have a use case called: fill application form, then I create an interactor that will do that. That interactor will then call the persistence layer to do the persistence it needs to do. The beauty about it, is that I can inject the persistence service (or repository in DDD terms), and just mock it out for my unit tests. Then I test my repository separate. Like it or not, testing your DB is an integration test by definition. You are integrating your application with a data storage. Something that is external. I can have a ApplicationFormRepository#persist() that will match the use case I’m working on. I can test that in isolation also. And I can use datamapper, ActiveRecord, SQLAlchemy, GORM, MyBatis … whatever ORM to make my life easier. The ORM becomes a tool, a mere detail that can be swapped.

  4. Oh, and in regards to validation, that belongs in your UI AND your interactors. When you think about it, validations are defined in your use cases. I think the way ORMs are encouraged to be used in these web frameworks have propagated wrong information about what a domain model is. A domain model is how you model a specific domain business rule. What these frameworks refer to as domain models should really be entities. SO if I have a FillApplicationForm interactor, when I call FillApplicationForm.initialize(), I would validate through a private method. If validation fails, I send a response model that can contain the validation errors.

    The tricky part to me is to decide if I should send a validation error, or throw an exception. Why is this? Well, I’ve also been toying with command-query pattern and this basically says that if you tell your object to do something, it shouldn’t return a value, and if you tell your object to give you something, it shouldn’t do any complicated transactions. So if I say: FillApplicationForm.getall(), then I don’t expect that call to modify any record in the database. If I say: FillAppplicationForm#updateaddress(params), then that call should not return anything back. This comes in handy when you want to separate your reads and writes into separate dbs also. Anyway, that is another subject. So, the dilemma comes in when something fails. How do I notify the caller that something failed? That is where exceptions come in. I can throw an exception that contains a response model with the errors, and those errors can then be displayed to the user. This is just experimentation. I don’t know anything about CQRS, I’m just trying to learn and this has been my experience with it. I can be totally wrong, but this is what makes sense to me at the moment.

  5. Great topic and very timely! The procedural nature of examples in rails/django/fill-in-the-blank-mvc has been really bugging me recently. I’m very glad the term Hexagonal Architecture in the InfoQ interview intrigued me enough to click. I’m looking forward to the rest of the posts. It looks like Roberto has posted an example of his hexagonal grails project: https://github.com/uris77/tasks

Leave a comment

Your email address will not be published. Required fields are marked *