Hanami vs Ruby on Rails
Oct 30, 2020
Ruby on Rails (aka RoR) is arguably the most famous web framework in the world. This statement is true in the Ruby ecosystem, of course, but RoR is famous too in other programming languages, having inspired their corresponding web frameworks.
However, RoR is not the only web framework in the Ruby world. Another famous one is Sinatra and then, perhaps less well known, we have Hanami.
Hanami was inspired by the ideas presented in the Domain Driven Design (DDD) work by Eric Evans. At Lavanda, we use it and we like it very much.
At Lavanda, we use both Ruby on Rails and Hanami. Our experience with both inspired the content of this blog post, in which we'll be comparing the two frameworks putting extra emphasis on the way defaults of each framework guide the engineer to develop a web application. We hope that this blog post will make you want to download, install and play with Hanami - and perhaps even become productive with it.
Side Note: What does Hanami mean?
āHanamiā is the Japanese word for flower viewing - the traditional enjoyment of the transient beauty of flowers. You can find more information about it in Wikipedia.
Default Project Folder Structure

Let's start with a comparison of the default folder structure of a project that is bootstrapped with either of the frameworks.
The main differences are the following:
app
vs apps
Ruby on Rails creates a project with a folder tree app
that hosts the application specific files. On the other hand, Hanami creates a project with a folder tree apps
. Inside apps
, you can host one or more applications. Hence, you can have a sub-folder named, for example, web
, that holds your web application and another one called api
, that holds your exposed JSON API.
In other words, in Hanami, you can host multiple applications within the same project. They will all share the same domain model.
lib
Tree
Both frameworks create a lib
folder, but their purpose and importance is completely different.
In RoR, we use the lib
folder to write some utility, accompanying classes that have horizontal functionality. Some times, the code we write in lib
could be potentially shared with other Ruby or RoR projects, if it were extracted into a gem. It is code that is application independent, but it does not actually model the domain of the project. We have seen RoR projects that they have very few lines of code inside the lib
tree, or even none. Hence, this folder does not usually play very important role in the RoR projects.
On the other hand, in Hanami, the lib
folder is the heart of the project because it is used to model the whole business domain. We put a lot of code inside this folder. Everything is application independent and ready to be shared by all applications that are hosted inside the apps
folder. Some of the things that we put inside the lib
folder are:
entities
repositories
interactors
validators
Keep on reading to learn more about these.
Hence, in Hanami the lib
folder has the most important part of the code of the project. The apps
holds code that relies on the lib
folder code. lib
folder code stands on its own and completely models the application-independent aspect of the world.
config
Folders
Both projects have a project-level config
folder. This includes files and folders necessary to configure the whole project. In addition to this Hanami has also one config
folder per application. In other words, one can configure global project settings as well as specific configuration per hosted application.
db
Folders
This folder serves the same purpose in both frameworks. This is where we keep database specific configuration, including the schema migrations.
Test Tree
In RoR the test
tree is holding the whole automated test suite. In Hanami, it is the spec
folder that does that. This is because RoR integrates, by default, with the mini-test, whereas Hanami integrates with RSpec.
MVC
Both frameworks are MVC (Model View Controller) web frameworks. However, they apply this software design pattern in a slightly different way.
Let's see how.
Controllers
In RoR we have one controller class implementing multiple actions.
On the other hand, in Hanami, we have one class per action. That's why we also call them controller action classes.
Generally, something that we really like with Hanami, is that it favours small classes, that have a very simple public interface and they do a single thing. And one occurrence of this idea is how Hanami asks you to put your controller actions into separate classes. Smaller, simpler classes have many benefits, amongst of which are the ability to easily test it, maintain it, read it and understand what it does, reuse it and refactor it without generating ripple effects. This all falls into the general software engineering principle of Single Responsibility.
Otherwise, controllers between the two frameworks have a lot of similarities. For example, you can install before action hooks, i.e. code that is executed before the main code of the action.
Views vs Templates
In RoR we have the views being *.html.erb
files. They contain the HTML content to be returned. They can include Ruby snippets that build the dynamic part of the page/resource requested. We have one such file per action. They can be combined with special *.html.erb
files that are called layouts.
On the Hanami side, we have exactly the same thing as we do in RoR, but these files are called templates and not views. And, on top of that, there is one more layer which is called views. The views in Hanami are Ruby classes that prepare the data for the templates. In other words, in Hanami we are advised to use views to prepare the data for the templates, and not the controllers. Whereas, in RoR, we prepare the data for the views inside the controller actions.
Again:
RoR: only views and data for the views prepared in controllers.
Hanami: templates and views, with data for the templates prepared in views and not in controllers.
In the following picture you can also see the difference in the folder structure.
In RoR, the
views
folder is inside theapp
folder. There, you can find a sub-folder forlayouts
too.In Hanami, we have
templates
andviews
inside the application specific folder tree. Note that Hanami can have layouts too, but they are just othertemplates
files.
Models vs Entities
In RoR all models derive from ActiveRecord::Base
. Moreover, they are feature and functional-rich class. Some they say that they are quite heavy because they inherit so much code from the base class.
Hanami does not have models. It has, what they call, Entities. Each entity derives from the class Hanami::Entity
. Entities are light-weight classes with very minimum functionality. They have an id
and you can read the values of its attributes, without being able to change/update them.
Both types of instances, either models in RoR and entities in Hanami, represent one row (at least usually) from a table. However, in RoR, you use the same instance to both read and update the corresponding row. In Hanami, the instance db-related state is read-only. If you want to change the persistent state of an entity, you have to use a Repository specially designed for this. Keep on reading in order to understand what a Repository is.
Models and Entities differ also on the folder they live in.
In RoR the folder is called models
and it is inside the app
tree.
In Hanami the folder is called entities
and it is inside the lib
tree. It is application independent and that is very useful, because the entities can be shared amongst multiple applications in your Hanami project.
ActiveRecord vs Repository Pattern
Having given some details about the differences between models and entities, we would like to emphasize here the fundamental difference between the two frameworks with regards to the way they map objects to relations.
Models in RoR are an instantiation of the ActiveRecord pattern, whereas Entities, with the help of Repositories they instantiate the Repository Pattern.
Here are the definitions of the two patterns as given by Martin Fowler.
ActiveRecord Pattern
An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.
Repository Pattern
A Repository mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
This makes the two frameworks, RoR and Hanami, differ quite a lot when it comes to interacting with the database. For example, in Hanami, there is no code like this:
book = Book.find(1)
book.title = 'Of Mice and Men'
book.save
In order to update the title of a book, you should write something like this:
book_repository = BookRepository.new
book = book_repository.find(1)
data = { title: 'Of Mice and Men' }
book_repository.update(book.id, data)
The philosophy of Hanami is to try to prevent developers from spreading query-related chains of statements all over the code base.
This is something that Hanami doesn't like:
Book.where(author_id: 23).order(:published_at).limit(8)
As you can imply from the above Ruby statement, the technique to access the persistent state is not exposed outside of the Repository. All the application code is using, it is the business-friendly methods that Repository is exposing. And that's good! Because whenever you want to change the data access layer implementation details, then you don't affect the rest of the application code.
Repositories Folder
All the repositories in Hanami are under the lib tree inside the repositories folder. Here is an example picture of such a folder:

Repository Methods
As we said above, in the Hanami repository, we expose business-friendly methods and we hide the technique we use to retrieve data from or update data to the database. Here is the public API of an example repository:
Validations
Let's talk now about another difference between the two frameworks. That of the validations.
RoR Validations
In RoR, validations are written at the model level. Like this:
class Article < ActiveRecord::Base
validates :title,
presence: true,
uniqueness: { case_sensitive: false },
length: { maximum: 25 }
end
We do like Rails validations because they are easy to write and easy to read. What we don't really like very much is that they are written at the model level. We believe that the validations should be part of a form model, rather than of an ActiveRecord model, because validations, usually, change, depending on the use case.
Hanami Validations
In Hanami, validations are not written at the entity level. They are separate classes, validator classes, and they can be used anywhere in the code they are needed.
class CreateArticle
include Hanami::Validations
predicate :unique_title?, message: 'has already been taken' do |title|
ArticleRepository.new.find_by_title(title).nil?
end
validations do
required(:title) { str? & size?(1..255) & unique_title? }
end
end
Hanami Validator Classes
They include the mixin
Hanami::Validations
They have a block
validations do ... end
which includes all the validations.The validations are written using a special language. This language has constructs that are called predicates. One can invoke a predicate or more, combining them with the
&
operator.The validator class can define its own custom predicates, using the block
predicate <name of predicate>... do ... end
. The custom predicates can be used alongside the default Hanami predicates when defining the validation rules inside thevalidations do ... end
block.
Hanami Custom Global Predicates
Besides the custom predicates that one can define inside a class validator, with the help of the method predicate
(and which are called custom inline predicates), one can define reusable predicates at a global level. I.e. custom global predicates that can be invoked in any class validator validations
block.
These custom global validation predicates need to be defined in a module
. Here is an example:
module AwesomeHolidays
module Predicates
include Hanami::Validations::Predicates
REGEXES = {
phone_number: /\A\+?\d+[\-.\s\d]+\d\z/,
email: /.*@.*\..*/,
url: /\Ahttps?:\/\/.+\z/
}.freeze
self.messages_path = 'config/predicate_error_messages.yml'
predicate :phone_number? do |string|
string =~ REGEXES[:phone_number]
end
predicate :email? do |string|
string.nil? || string.empty? || string =~ REGEXES[:email]
end
predicate :url? do |string|
string =~ REGEXES[:url]
end
end
end
In the example above:
Custom predicates are defined inside the module
AwesomeHolidays::Predicates
.This module should include the
Hanami::Validations::Predicates
mixin. This gives thepredicate
method to the module.The
predicate
method is used to define the custom predicates.Note that the
predicate
s do not declare the error messages inline. they use the"config/predicate_error_messages.yml"
file.
Then, all you have to do is to incorporate this module into your class validator. If you do that, then you have its predicates available to use in the validations do ... end
block. Something like this:
class SessionValidator
include Hanami::Validations
predicates AwesomeHolidays::Predicates
validations do
# ...
required(:email) { email? }
# ...
end
end
Note that when incorporating the global custom predicates module into your class validator, you use the method predicates
and not the method predicate
Use a Hanami Class Validator
Defining a class validator is useless unless you invoke its services in other places in your code, where you need a validation to take place.
Here is an example of using a validator inside a controller action:
module Web
module Controllers
module Articles
class Create
include Web::Action
# We incorporate the validator class and we attach it to the +params+.
params CreateArticle
before :validate_params
def call(params)
# work here with valid params
end
private
def validate_params
return if params.valid? # +params+ now responds to +valid?+ using our validator class +CreateArticle+
halt_with_error(422, ...)
end
end
end
end
end
The validator class is the CreateArticle
and we attach it to the params
. Then params
responds to #valid?
using the validation rules defined in the CreateArticle
class.
Associations
Associations and the way they are defined, is another big difference between the two frameworks.
Rails Associations
Rails allows you to define associations at the model level. It is part of the ActiveRecord::Base
API. Here is an example:
class Article < ActiveRecord::Base
belongs_to :author
# ...
end
class Author < ActiveRecod::Base
has_many :articles
# ...
end
Hanami Associations
On the other hand, Hanami defines associations at the repository level. They need to be defined only if you really need them. For example, when you want to load associated models.
Here is an example:
class AuthorRepository < Hanami::Repository
associations do
has_many :books
end
def create_with_books(data)
assoc(:books).create(data)
end
def find_with_books(id)
aggregate(:books).where(id: id).as(Author).one
end
end
In the above example code, we declare the association of an Author to their books inside the AuthorRepository
. We do it using the associations do ... end
block. Then, we can load, for example, the author alongside their books. This is what we do inside the find_with_books
method.
Undoubtedly, it requires more code to navigate associations in Hanami, if compared to Rails. But, yet, this is due to the Hanami philosophy and the repository pattern it is promoting.
Hanami Interactors
You may have already heard about the Service Objects, plain old ruby objects that have a single responsibility and that they are easily testable.
Hanami likes this idea very much and for that reason, it offers the Interactor tool. A Hanami Interactor is an object that works like a Service Object.
It can be initialized and then
It has only one public method named
call
One more thing: they integrate well with Hanami validations. We will explain this with the following example:
module Interactors
module Sites
class Update
include Hanami::Interactor
def call
# do the work inside +call+ assuming
# interactor has been initialized with valid params
# ...
end
private
def valid?
# this method is called automatically when you call +call+
# If it returns +false+, then +call+ will not fire
end
end
end
end
In the example above, you can see the public call
implementation. Also, you can see a private method named valid?
. The valid?
is automatically called when you call the call
method on an interactor. And if it returns false
, then call
is not invoked.
Usually, the valid?
invokes the services of a Hanami validator object.
Hanami @ Lavanda
As we said at the beginning, @ Lavanda, we have already started using Hanami in production. Here is our preferred layered architecture:
At the top we have the controllers. These are responsible for handling the request and returning a response back. They have WEB/HTTP logic only.
Controllers rely on Interactors to carry out the real work that the business requirement dictates.
Interactors rely on validations, repositories and possibly on other interactors.
Closing Note
In this post, we did a quick comparison between Hanami and Rails. Certainly, non-exhaustive.
@ Lavanda, while working with Hanami, we found a lot of practices making great sense. Such as:
Model of the domain in an application-independent folder, reusable by multiple applications.
Repository pattern that protects flooding of the application code with query data-access API commands.
Small classes that do a single thing.
Service objects with a very clear simple interface.
Light-weight model classes with associations and validations defined externally, in other classes, Repositories and Validators.
These practices are framework-independent and can be applied even to a Rails project. And that's the good thing about them. If you really like Rails for many other reasons, you may well apply these practices to your next Rails project and avoid using the corresponding Rails defaults.