I took a look at the Hexagonal Repository Pattern for Rails last time, added some code to make it more Rails-like, and built some tests to determine if it’s worth the effort.

Generally, when people talk about “The Hexagonal Repository,” they are including the additional patterns which go with Repository itself to replace ActiveRecord. One or more of Facade, Composite or Proxy patterns are added to the pile and then presented as a single pattern or methodology. This article will create a small set of requirements and implement them in both a traditional Rails way, and using the Repository with Composite and Facade patterns, I’ll then review the differences.

Source code is here for the hexagonal gem and the sample application 

If you’ve used code quality tools such as Rubocop or Reek, you should be familiar with warnings in your controllers when you try and do something like this:

Warnings about not using more than 1 model per controller, and calling no more than 1 model method per action, or warnings about method complexity when you start conditional branching. There have been volumes written about eliminating logic and dependencies in controllers, the paradigm is generally described as creating skinny controllers and fat models. However, in an attempt to improve on this, Hexagonal Architecture dictates going further and eliminating the link between controllers and models entirely.

Skinny Controllers Make Fat Models

In simple terms, the solution to controller weight has always been to try and strip down as much of the logic as possible and stick it in the model. While there are other components to create a clean, maintainable application, the Fat Model should take care of controller logic and eliminating all those model calls. If your controller is doing anything other than logically branching on true/false conditions from the model then you should take a hard look at whats going on. There is the very real danger of super fat models entangled with many other models which one finds in deployed applications, but if you try and stick a model’s methods to interaction with it’s own, or it’s associated tables, then, as a rule you should be able to produce clean, coherent and loosely coupled code. As always, an example to work through should be illustrative, let’s take a look at the requirements.

A Stock Manager

We’re going to create a product management page and allow a user to manipulate products and their stock. Here are some simple rules:

  1. If a product is subject of  a reorder, display details about the reorder.
    1. Sort reorders by status then date updated.
  2. When a product has fewer than 100 items remaining in stock, if there is no active reorder, display a reorder button.
  3. When the reorder button is pressed, providing there are fewer than 100 items in stock, create a reorder record for later processing.
    1. Assume this table is used by another process which sends to vendors and periodically updates the table. We will not be coding any of these processes.
  4. Never allow a product to have more than one active reorder.
  5. When reordering, set the quantity to be 1000 less the current product stock.
  6. Create an estimated reorder date which should be 7 days from when the reorder was created.
  7. The reorder table should have a date field which will be updated with the vendors delivery date.
  8. If a product has an active reorder, show the estimated delivery date unless a real delivery date exists.

A fairly simple set of requirements, but there is some cross model logic there which is excellent for the purposes of this demonstration. I am going to take the code example from part 1 and add to it, fleshing out the repository model with a business facade. However, I am first going to solve this with a fat model. In order to not pollute each solution, the fat model tables will be inventory and inventory_reorders, the repository solution will use  product and product_reorders. Associated controllers and views will follow the naming convention. You should be able to fork and run the sample, but let me know if you are having any issue setting up.

I assume no one needs to see the obese controller and logic-riddled view such requirements can create for the unwary, those have been refactored out to either the model or repository, as a rule I try to  call no more than one method per model per controller action. In a view, I never branch on model attributes, only on methods returning true|false.

Last time I used the dummy application within my gem to demonstrate, but as this example grows I have set up another repository for the application. The gem is available here on github, the sample application is available here.

If you download the app, it references github in the gemfile. Make sure to rake, including rake db:seed, as there is some sample data to show off application features.

Implementing a slimmer model

Ok the Rails-way naturally drives all of the business logic to Inventory and InventoryReorder. The controller has nothing other than a bare scaffold, plus one custom action – a Patch to the custom action name reorder which will create a new reorder process if valid. I’m not a huge fan of ERB, but for the purposes of this article I’m using it, but using it logiclessly-ish. There are if/then/else conditions for display, but they are all branching off model methods, there is no evaluation going on in the view other than true/false/nil.

After writing the models, implementing controller and view code was completed very quickly; there’s almost nothing to do other than call the correct model method and to structure the page’s data.

Writing this way makes it straightforward and clean, but remember we only have a few business methods. After reviewing, I noticed a fault;  InventoryReorder doesn’t have any reason to exist without it’s parent model; Inventory. First time through I coded business logic in the class to enforce requirements 4, 5 and 6. This is a really common issue when blindly coding models. As first coded, my Controller would have to call methods in Inventory and InventoryReorder. Although life is rarely simple, thinking of your models strictly in terms of public servants with consumable APIs will go a long way to eliminate the kind of mistake, so I moved the public method over to Inventory so that InventoryReorder becomes a supporting class for Inventory and not a public component with functionality of it’s own. Put another way, if your controller, web service, app, or console command requires interaction with more than 1 model then do something to change it.

I refactored the code out so that our business logic is now self contained leaving InventoryReorder with responsibility for field validation and two scopes. Here are the classes and method stubs.

I could argue to move those scopes out to real methods in Inventory but I tend to like scopes, and if using them, they must be in the model they are scoping. Although Scopes get a lot of criticism, if you look at InventoryReorder you’ll find it’s a class which is composable and those scopes can be used elsewhere. For example, if I later need an Accrual class which has_many InventoryReorders, I will be using the same scopes to handle queries for accounting logic.

In Inventory, you may note a number of methods which are added to eliminate any inspection of the model in either the controller or views. There is also a delegate to InventoryReorder’s scope to produce the record order in the view, I tend to try and create an external API to a unit of work, including other models linked by association. I have seen decorators solve the problem, but I already have Inventory and InventoryReorder linked in the has_many call, once two objects are linked and dependent I don’t see the point in pretending they are not and then going on to introduce dependencies elsewhere.

I hope this is as natural and standard for all of you. This is the ‘Rails way’ and has been for years. Technically, this is a partial implementation of a Composite – we are treating 2 tables and 2 objects through 1 unified interface, and a facade – shielding the larger ActiveRecord interface with a more focussed and compact one. ActiveRecord models are quite capable of implementing any number of design patterns and will do provided you take care to regulate your dependencies and always consider what other options are available.

So, let’s return to our Repository pattern from before and implement the same requirements. Before I get into the depths, I am going to make a couple of changes from last week and make sure the tests still work. If you recall, in a fit of laziness, one of my repositories was also an ActiveRecord object, I’m going to ditch than now and use a separate the Repository class from the data access object, so I am going to create Product and ProductReorder, along with their tables and call ActiveRecord methods from my repository instead of the shortcut I used.

 Quick History – The Java Way

Well speaking of linking a Data Access Object to a Service Facade takes me back. In fact, 2003 called and told me I’ve lost way too much hair. Hexagonal Architecture did not materialize out of thin air, its been around years with other names. Let’s take a quick look at a concrete example of it, otherwise known as the Data Access Object pattern, something every Java programmer from that era can still write in their sleep. You can read the detailed pattern implementation guide here below I am going to list the classes and interfaces necessary to access data from an Inventory and InventoryReorder table.

That’s 8 classes centered on data access to do what 2 ActiveRecord classes manage to accomplish today. But it doesn’t end there, no right minded Java developer is allowed to use the DAO objects directly. No, you will need to go through a Facade, so bump the class total up to 12 to include the Service Facades and their interfaces. But don’t worry, as you add a new table you only need to add 10 new classes per table, and only a few of them are concrete, the others are just interface definitions! This was my life for years, it’s why I can still remember the pattern by heart and involuntarily twitch as I type the letters DAO at the end of a class name.

You may have heard of Spring and Hibernate, they came along to save Java and reduced the footprint of this pattern down to 5 classes per table. Amazingly, writing 5 classes per table was wonderfully liberating and felt like the most efficient thing in the world, after all, it was a great architecture and was wonderful for unit testing.

This is why I am guarded when former Java developers start creating Rails architectural patterns, and when the benefits being touted now are identical to those architectures from the early 2000’s. But let’s take a quick look at whether those classes really are the low coupled, high coherence ones every architect aspires to. Superficially, it all looks magnificent, but take a closer look and count how many classes I have to change when I add a column to Inventory or need to alter the default sort order of an InventoryReorder. The reality of the Data Access Object Pattern is that classes are highly coupled, and require a basketball-team of classes working together, so in reality we have close to zero coherence. The only changes which are well insulated are wholesale replacement in database or database storage type. Now let me ask you, are you more likely to you to add a column to a table or decide to change your enterprise database of choice, across hundreds of services and applications. If you’re doing the latter I guarantee you will have greater concerns than unit testing without a database.

Why are we looking at Java code from 2003? Well it turns out to be identical to the Hexagonal Repository pattern. Don’t believe me, let’s implement.

Hexagonal Repository Service Facade

We focussed on rails-ifying the Repository Manager last time, now lets create the facade and composite using a Repository object instead of ActiveRecord. Doing this isn’t difficult, especially after already testing and implementing the logic earlier, this is how ProductRepository now looks.

Two things  should jump out on you: all those CRUD methods, which came for free when this was ActiveRecord, are now hand coded and will have to have their own unit tests. Secondly; all those instance methods from Inventory have become class methods in ProductRepository. Our Product instance, which is still inherits from ActiveRecord, has been relegated to a Data Transfer Object, the only allowable methods to call on it are field getters and setters. In implementing this pattern and attempting to separate application logic from Rails we have an implementation which looks almost identical to Java with Spring and Hibernate circa 2006 – Data Transfer Object, Service Facade, Data Access.

Let’s not be fooled that we have decoupled anything over the Fat Model. Where Controller was dependent upon Product, it is now dependent upon ProductsRepository, and we’ve added a close coupling between ProductsRepository and Product on top of that. So what have we made easier to change? Yes, wholesale data storage strategy.

And this is where Hexagonal Architecture fails so spectacularly for me. In 15 years I have seen many things, but I have never seen an existing application upgrade project where the pain point is application code remediation when switching data storage strategy. Remember, this is not a solution for switching data source, moving for example from Postgres to Oracle is easy with Rails. What this pattern is good for is example moving from a relational database to flat document storage. I may of course be working in the wrong places, but is there a huge number of partial re-writes going somewhere looking to achieve only this?

What About The View?

As much as Controller to Repository adds unnecessary overhead, this pales with what we need in our view. When using straight Rails, our data object implements the ActiveRecord pattern, so our view has a self contained object with data and functionality. While this can be abused, if done correctly it is a clean, efficient solution.

There are ample ways to protect your views from over-coupling with the model, they all come at a cost. Even a lowly Decorator or Presenter which cleans up the view has the capacity to increase coupling and dependencies.

So now is a time to face some facts: the View and Model will always be coupled if you are using model field names to construct your view. Change your model, change your view. Let’s take an example, we want to display a reorder link or reorder date based on methods in Inventory which return true|false.

Some may find that distasteful, I don’t mind. But now we have a repository, we no longer have a model with methods outside of data. So, I have two choices, either decorate the Data Transfer Object in the controller and add another class to be coupled, or pass the Repository to the view so that it can call the methods. If I was doing this outside of an article, I would go with the Presenter pattern, but for now I’ll go with passing the Repository into the view as dependencies always look worse when written in ERB. Lets see what it takes.

First of all I need to add the ProductsRepository to all views as an instance variable. After that, it’s using the repository class methods with the Data Transfer Object as a parameter. Ok, it’s not particularly heavy lifting, but there is something syntactically hideous about calling the repository directly from the view. It’s another echo back to Java, where jstl is full of these calls, and ironically, it’s one of the pain points in Java that the view is polluted with multiple Service factories being used. Somehow, this is now considered a positive and is being touted as an enhancement to Rails.

Making It Better?

In the interest of trying to improve this and see if applying a more Rails-like methodology makes it palatable. The most obvious target is getting rid of formally ActiveRecord CRUD methods to a module and including them in the Repository.

If I had a little more verve for the task I would pick an alternate method of injection, and standardize across different types of DAO, but I don’t see a huge return for the work. Instead, setting a DAO to back the Repository can be accomplished with an extend and macro method. So ProductsReposutory’s first 40 lines are replaced by

The irony of adding yet another piece of the Java DAO pattern is not lost on me, but it does cut down on needless repetition when you are using ActiveModel backed repositories.

Back To Java Again

A few paragraphs ago I claimed that the Hexagonal Repository in Rails was identical to Java/Spring/Hibernate. If you look at our classes we have created a Repository, a Data Transfer Object, and a Data Access Object. Differences between Ruby and Java allow for Interfaces and Abstract classes to be eliminated as we can rely on duck-typing, but this is exactly the same pattern with exactly the same responsibilities being placed on each class.

Wrapping Up – What Do You See In An Empty Room?

I must admit to being quite excited the first time I saw a demo of Hexagonal Architecture on Rails. It looked as though an additional level of abstraction got to a less coupled application without losing any of the benefits that Rails brings. But, after taking a deeper look and coding with it, I have come to believe it’s a needless abstraction which replaces one set of dependencies for another and switches off many of the most time-saving, boilerplate-eliminating benefits of Rails. I found myself wondering what I was doing and what problem I was trying to solve. Aside from being able to unit test without a database, the answer is nothing. There is nothing that this type of abstraction solves which cannot be tackled more efficiently, more reusably and more coherently with other techniques.

Do you see an empty room and think, how much stuff can I shove in there? Sometimes, but more often we aim to put just enough into a room for its function. Architectural design should be the same, but tends to equate to shoving stuff into that empty room until there isn’t a square inch of space left. More design does not mean better design! Just as very few of us are compelled to use Bootstrap and Foundation, we should be selective about the architectural paradigms we adopt. If Hexagonal Architecture is a desirable goal for your project, then find a language and framework which complement it, and don’t try and shoehorn it in to Rails.

Rails Hexagonal Architecture – Part 2 Repository With Facade and Composite Patterns

Leave a Reply

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