Hanami: Simplified Models through Repositories and ROM
Last year, my team at AdHawk chose to use Hanami as our Ruby framework for building a greenfield project we are now deep into coding, the product Flooring Stores. Every time I mention Hanami outside of my workplace, the confused look and the barrage of follow-ups I get from other Software Engineers has made me gradually whittle down what Hanami is and why we use it.
I’d also like to describe a basic feature set of Hanami, its model layer, in the tech stack we’ve chosen with ROM (Ruby Object Mapper, our mapping toolkit) and Sequel (our Object Relational Mapper) to give a feel for it’s philosophy. The default Hanami::Model uses rom-sql as well, but we dropped that wrapper since it used an outdated version and went with the latest available.
Hanami: The What and Why
Hanami is a framework for coding in the Ruby language, focused on simplicity rather than feature-heavy convention over configuration, like Rails. Hanami has positioned itself as a lighter alternative but still with all the important needs baked in, including routing, security, models, views, controllers, migrations and more. One key selling point is speed — Hanami claims to offer up to 60% less memory usage than other full-featured Ruby frameworks.
In our experience the past six months, Hanami has provided a great foundation for a tech stack that developers can easily pick up the syntax with as well as a powerful set of features that are intuitive out of the box. Our team has launched a new product and been able to iterate fast, from database changes, to back-end data modelling, to API customization for front-end needs and more.
Many of the conventions in Hanami are either similar to Rails or easy to reason about. A great example of this would be routing. In Hanami, per app sub-directory (say your admin panel versus your consumer website), there is an individual routes.rb file similar to Rails. In Hanami, the route for our internal style guide would look like this:
And an example of resources like the products show page route, would look like this:
Look familiar? This would be exactly how these would appear in most Rails routes.rb files. Since both frameworks are built on top of Rack, there are no major differences in routing approaches. Many aspects of Hanami are similar to Rails like this.
Spotlight on ROM — Entities & Repositories
One of the core concepts of Hanami is splitting the logic in the model layer into entities (for expressed behavior) and repositories (for persistence). In contrast to Rails with Active Record, Hanami has a repository pattern for its model approach. While it’s ORM-agnostic and can utilize Active Record, we’ve chosen to use ROM with Sequel and swap out the Hanami defaults for ROM repositories. ROM has much of the same philosophy behind it as Hanami’s model layer, looking to separate database querying from model logic.
Entities in Hanami are simple, oftentimes empty files, that expose the basic attributes of a data model and at most wrap a few methods providing additional information. For example, in our codebase, our Visitor entity has a simple method to expose if it is a verified visitor or not, which is determined by a simple column on that table:
In contrast, repositories offer much of the functionality you might expect from a Rails model file. From data manipulation to storage-agnostic queries of objects, repositories offer full-fledged model functionality for any Hanami app. You can find a great write-up of the repository philosophy in Martin Fowler’s Domain-Driven Design (https://martinfowler.com/eaaCatalog/repository.html). Here’s an example of our Retailer Account repository:
Solid repository pattern code will not worry about how the repository fetches entities and will have clear intent in the method names. As mentioned in the ROM docs, this type of caller works on a single level of abstraction and can easily be tested, versus the typical chain-of-methods Active Record or other manual database ORM approach. You can find a great write-up by the ROM folks on its differences with Active Record here.
I rarely have to enter our entity files to edit after creating one, and with our repositories, we write clear methods that give developers access to the most important and commonly-used datasets or subsets. Often they’re find_by methods like in the example code above. Other times, we might expose all the stores via a method on the Store repository, ordering all stores in our database by distance from a point on a map, for a ‘Nearby Stores’ feature. In this way, repositories hold much of the traditional Rails model layer code.
What’s the main advantage? In a nutshell, simpler abstractions with less pitfalls. I think any of us who’ve worked with Active Record and traditional Rails models have experienced at times dense and difficult syntax mixing abstractions for data validation, storage, querying and more. Hanami with ROM eliminates those hours of Active Record fine-tuning.
ROM also removes the possibility of unintentional N+1 queries, where you make a database query per record by accident, with major performance bottlenecks. By accessing combined relations through performant defaults (.combine syntax), you never forget to preload or join and thus slow down your app. On top of that, while there’s a learning curve even with similar code syntax/style, ROM has cleanly separated data access and manipulation logic from database-level operations.
Interestingly, ROM offers no callbacks of its own, in contrast to Active Record. So if you’re used to before_validation, after_create or other Rails model code, you’ll have to utilize alternate approaches. Avoiding callbacks in general is a best practice, producing less “magical” code with fewer steps to reason about. There’s less magic and much more specific, intentional code in Hanami with ROM, and I find that to be a selling point.
So there you have it: Hanami, our lighter weight yet still fully-featured Ruby framework, one you may not have considered for your side project or your startup’s dev team. Along with ROM and Sequel, Hanami offers a simpler, more modular approach to the model layer than Rails developers have experienced over at times a decade plus of experience coding in that framework.
My experience with Hanami has been all-around positive, with enough similarity to Rails to get moving fast as a dev, and thoughtful abstraction choices throughout the framework that make a difference in developer productivity.
One aspect of coding I value most in my career is the way it makes me constantly question ways of thinking I take for granted. This change in tech stack has made me reconsider in depth the many aspects of Ruby on Rails that I never questioned. While there’s always a learning curve, I find myself greatly enjoying building new features with this new set of tools. I highly recommend giving Hanami and ROM a try when you next have the opportunity.
Side Note: Hanami, Japanese Edition
Hanami (花見, “flower viewing”), in Japanese, comes around annually in the spring with picnic parties and food and drink under cherry blossom and plum blossom trees. In Japan, for thousands of years people have enjoyed taking in the transient beauty of flowers, and to this day it’s a national event when cherry blossoms in particular bloom for a few weeks in the spring and arrive in each town and city. It’s broadcast on TV as if it were a sporting event!
Hanami was one of my favorite events living in Japan, and an aspect of Japanese culture that distilled the essence of a non-Western viewpoint on beauty, time, impermanence and more. I hope Hanami the framework can live up to the millennia of history behind its namesake tradition!