Rails Hexagonal Architecture – Repository Pattern

Let me preface this article with a couple of things. Firstly, I strongly disagree that Hexagonal Architecture is particularly useful, relevant or close to achieving the goals ascribed to it when combined with Rails. Secondly, I have an innate dislike for direct translation of Java paradigms into dynamic languages. I coded almost exclusively for almost a decade, and still enjoy the elegance of well designed Java applications, but Ruby is not Java and it has a set of paradigms of it’s own which are equally elegant, if different.

Ok, that’s out of the way, what is this article useful for? Well, one of my strongest objections to Hexagonal has been that every Rails implementation I have seen has been a direct Java port. There’s been none of the convenience and standards I associate with Rails and to be frank, reading translated code from a strongly typed language such as Java is nauseating without the Interface and Abstract Base Class types it relies on.

This is an attempt to implement Hexagonal in Ruby, within the context of Rails, to examine whether a different implementation is any more appealing.

What Is The Repository Pattern

As ActiveModel is in league with the devil, it cannot be directly referenced from an innocent controller. Lets take a look at what Repository Pattern is fighting:

Those highlighted lines are instances where the controller is directly manipulating the model and it is inferred that we have tightly coupled the controller to the model. The Repository Pattern attempts to isolate the controller by managing Repository Object instances and handing them off to the Controller as required. Where before the Controller directly called Model class and instance methods, it will now ask the Repository Manager for a Repository object and call that object’s methods.

Thus, changes in storage implementation are remote from the Controller and therefore no code will ever be changed if the storage mechanism changes. Creating more loosely coupled, highly coherent applications results in easier growth as functional requirements increase, cheaper maintenance, world peace and fusion power for all.

Here is an excellent implementation of The Repository Pattern by Mike Ebert, I’m going to use it as the foundation for a more Rails-like implementation.

The specific advantages of Repository Pattern are purported to be

  • Controllers may be unit tested in isolation.
  • Controllers are divorced from storage mechanism.
  • Applications are composed of a series of APIs with Rails as a dispatcher rather than being the mediator, compositor and dispatcher it is today.

Where this pattern becomes a little fuzzy is that its usually presented together with a ServiceManager pattern. Unfortunately, these are wholly separate architectural components and this leads to the most challenging problem in rationally discussing Hexagonal; where the changes of can be examined in isolation. For example, most of the benefits associated with the Repository pattern are truly the effect of the ServiceManager pattern.

With that in mind I set out to make the Repository pattern behave closer to the way Rails already works. Complete source code is available here on github.

A Rails Repository Pattern

So what is Rails-like? Well that means something different to everyone. To me it includes the following

  • Convention over configuration
  • Injection by mixin or safe monkey patch
  • Separation of concerns
  • Narrative methods

If you look at the typical implementation of Repository, the controller asks the Manager for a Repository Object, or it is dependency injected into the controller.

Mike’s sample is the only rational example I have come across. Other methods are directly specifying the Repository Object from the controller and managing through initializer configuration. The odd one injecting via public method and filter is incredibly destructive. While Mike’s works well, I don’t necessarily want to have to keep specifying the correct Repository in each controller I create.

I want a Repository which is capable of determining which repository to return based on convention, overriden by configuration. The first change is to alter RepositoryManager and transform it from being a manager (or mediator according to GOF) into both a manager and factory.

With one method we now have a factory which will attempt to create a Repository based on a key. For example, if the key is :products, the manager will attempt to find ProductsRepository and register it. We guard against overwriting explicit configuration by failing if the same key already exists. So, this won’t prevent you from registering your ProductsTestRepository or InventoryRepository against a :products key, but if you simply want to name classes in the same convention used for the rest of rails, it will automatically work.

You’ll notice the factory mechanism is simply Kernel.const_get. This will throw an exception if the constant does not exist, I make no effort to wrap it and add a custom Exception, I am going to do that elsewhere, rather than create custom Errors or Exceptions.

There are a couple of configuration options available which allow you to alter convention; object suffix and module prefix. If you want your Repositories to be end in ‘Store’ or put them in a module Sbb::Entities::Database, then an OStruct configuration object can be injected into the RepositoryManager. Happily, this is the same mechanism Rails uses for its configuration objects, though the method of configuration is irrelevant to our RepositoryManager. In fact, our manager has no references to Rails at all with no knowledge outside of what is contained in its own hash.

As an example, let’s configure repository with a couple of custom repository keys and a custom suffix

In this sample, I am using the standard Rails configuration object with custom keys. I then pass the repository key into my factory in an initializer, and add a specific repository for the key :products.

By configuring this way, I can specify a wholly different set of repositories for testing without individually registering each one. I can unit test the repository without Rails, and test my controllers without a database:

Testing with Rails, I can move Repository configuration into a specific environment file, use a module prefix and not have to change another line of code to remove requests for those database backed repositories, they can be replaced by mocks or stubs. When you have a significant sized application, one big enough to warrant using this pattern, this convenience and automation makes the whole thing feel like Rails again.

Exciting stuff if you’re that way inclined. There is one, teeny-weeny, ever so ugly issue with Rails configuration; currently Rails only allows a single set of initializers across all environments. So if you want to directly register your repositories and have a lot of exceptions to convention, you would have to do something like this in your initializer

Thats not particularly pleasant, but if you won’t maintain a convention for your naming which allows your repositories to be switched by module, you’re probably not interested in this implementation!

Rails Controller Repository Adapter

That’s a very fancy title for ‘how do the controllers use this?’ Well, lets take the same controller from the original problem above and look at the changes this architecture introduces.

The highlighted lines show changes, we have broken the direct link to ActiveModel and replaced it with a call to the RepositoryManager.

So what is repo and how does it work? How do I specify which repository to use? Well, I wanted to use the controller name itself unless I choose to override. So, lets look at two controllers:

Its not uncommon to split administrative functions off into their own controllers, here I have two different controllers which both need to access the same Repository. As you can see, I use convention for the ProductsController, and specific configuration for the InventoryController. You may have also spotted the include which is responsible for the ‘magic.’

When looking at options for how to do this I rejected monkey-patching ActionController, writing an include which would only be used for ApplicationController, or any other silent or derived mixing. I strongly believe that there needs to be some clue for the next wave of developers who will be picking over your undocumented code trying to figure out what’s going on.

So with that include, I mix in the RepositoryAdapter and everything just works, how? Let’s take a look

That’s a bunch of code to either allow an explicit key name or figure out from the class what the key should be. I’m sure there is greater elegance which can be achieved in derived_name but for now it will do. Essentially, ProductsController, Product as a model, or Product as a ruby class will all set a the class variable @repository_name to  :products at the time of inclusion. If the macro register_repository is called from the class, then @repository_name is overwritten. I think that’s as simple and self documenting as I can make it.

Great, so we have a key which either defaults to a class derived name or is specified, now what? Well, if you look again at the controller code, I am able to use repo as an instance method directly linked to the correct repository. The final piece of the puzzle is how the adapter interacts with the Manager/Factory

I have a class method repository which calls the Repository Manager and asks for a Repository object registered with this class’s key. If none exists, it asks Repository to create a new one based on the key. The instance method repo is added for syntactic sugar.

I am not settled on error handling at this point, for now I am catching the potential failure here in the mixin and pushing the trace so that the stack trace begins in my controller. There is more to Hexagonal Architecture than Repository, so I will amend error handling over time as I attempt to Rails-ify each pattern.

So, with two mind-numbingly simple files, the Repository pattern is changed to use convention by default and allow enough configuration to provide flexibility. If you look at the source code you can see it being used in a sample controller, and some sample unit tests for the controller.

Until this point, I have deliberately avoided any detail on what a repository such as ProductsRepository does for the simple reason, the Repository Pattern does not care. It can simply point to an ActiveRecord object, or a highly defined CRUD model which then delegates to ActiveRecord, direct to the database or to a remote service. This may be controversial, but the detailed implementation of repositories does not belong in this pattern, even though it is often confused and lumped in. Why? Well it’s more appealing to throw additional functionality and pretend it’s all part of the pattern, but if we look at it logically, a Repository object can be a Facade, Composite, even a Command or Strategy, wholly separate patterns. All the Repository Pattern is responsible for is to serve the correct object to a caller to do unto it what it will.

The Repository Objects

So what goes into a Repository Object? It’s outside the scope of this article, but I am going to quickly walk through two samples. One quick word of warning, if you are going to convert an existing project to use a Repository then you are quickly going to find that all your standard links in controllers and views will be broken. Rails relies on naming convention and the implementation of modules to automatically create links from objects, so you will need to determine how you intend to change your application. One method is to ensure that whatever object is returned from your repository implements several of the ActiveModel modules. There is more complete information on how to do this here. I hope the irony is not lost that in dismantling the Rails ActiveRecord mechanism you may find yourself implementing ActiveModel.

My quick and dirty product repositories are an ActiveRecord object and an Hash-based Active Record pattern. Let’s look at the first one, well, first.

I can simply use our old friend ActiveRecord with a couple of modifications to ensure Rails’s naming conventions are followed. I specify the table and the model_name, when returned from the RepositoryManager, this simply plugs into the rest of my controller and view code seamlessly.

The second is a little more work. I used it in the source code’s dummy rails application controller tests, and there is a line in config/development.rb you may uncomment to see it working in the development environment.

Here is the object’s source.

Memory-based storage smacks of theoretical nonsense, never to be found in the real world, and so it is. However, to meet the goal of divorcing controller tests from the database and not using a mock, I have created a camouflaged mock; a real class which will never be used outside of testing.

Does Repository Pattern Work?

So, has the Repository pattern achieved its goals? Lets review.

Controllers may be unit tested in isolation

Failed. Hilariously, in researching this piece I read another article specifying how injection allowed one to unit test without mocks or stubs, go on to create a mock to demonstrate. Controllers by their very definition cannot be unit tested without additional classes. No amount of indirection or abstraction can hide that one must have a fake or real repository, just as much as one must have a fake or real active record. Perhaps there is confusion in the goal, a controller may certainly be unit tested without a database, but that’s a wholly different goal. Unit testing a controller in isolation is simply not possible.

Controllers are divorced from the storage mechanism

Succeeded, but at a cost. Controllers are nicely isolated from storage, but to do so we have added another layer which is intimately tied to that storage. Even though it’s easily configurable don’t mistake it for not being coupled. So while controllers are made more remote, we have swapped one class’s dependency for another.

Applications are composed of a series of APIs with Rails as a dispatcher rather than being the mediator, compositor and dispatcher it is today

Failed by confusion. It’s not the repository which enables this, it’s the series of patterns you implement within your Repository Objects which create this rather neat architecture. The Repository Mediator/Factory is a piece of the puzzle, but it contributes with some cost. I’ve taken a thoroughly understood paradigm of Controllers using ActiveRecord objects and replaced it by adding two additional classes which must be comprehended in addition to the controller to understand how the controller is getting it’s data. For some frameworks that’s an improvement, but for Rails there just doesn’t seem to be much benefit to doing it this way.

Wrapping Up

Perhaps RepositoryManager only makes sense within the larger context of Hexagonal, and once those other pieces are used together then all will become clear? I intend to go through the other pieces as this series progresses so we will see, but let me state now that suggesting the whole is greater than the sum of its parts is kind of the reverse of good architecture. Think what you might say if a developer came to you and told you “I know my design looks bad at the moment, but wait until the whole application is finished before you judge.”

The Repository Pattern is a neat academic exercise, it does give the appearance of being cleaner, but don’t be mistaken that it is any cleaner.

Finally, I like to judge investment of time against the importance of what I am doing. Is it likely that having chosen Rails I will sometime in the future decide that I want to rip out ActiveRecord and replace it with direct sql? It is almost inconceivable that anyone would make such a decision. So is it worth investing even a moment coding for it?

Source code for this article is available at Github. It consists of a Rails gem, unit/integration tests for the gem, a dummy rails application and tests for the for the dummy application.

Next I’ll look at the multitude of patterns which together replace ActiveRecord calls and abstract your data layer to an APIs and see what that buys us. Unlike network TV, there’s no cliffhanger here. Facade, Composite et al are incredibly useful when constructed for purpose. Most of us have been using these patterns for years before Hexagonal became fashionable.

Leave a Reply

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