1st of October, 2018
When we restarted developing an application from scratch, I stumbled upon a great way to split the files in your Rails app into different parts.
For over 4 years, we have been building a Rails app for helping people finding work. Recently, we decided to restructure our application and stumbled upon a great way to split the files in your Rails app into different parts.
Just a few months ago, we started making new concepts for the core component of our application. We sat back and thought if this could finally be the opportunity we have been waiting for – to start a new architecture. We had learned so much about our business and what our customers needed. Our team had matured and developers were more experienced. And we were eager to employ the concepts we made up to reduce technical debt further.
Break it up
We wanted to start the redesign on the right foot. We decided to focus on simplicity, encapsulation and re-usability. So I started a quest to find a nice way to break our app into nice little components. The obvious candidates were:
Microservices Amazon does it, so we should do too, right? Well, except the app we are building is not Amazon. In the past, whenever I tried to make small individual services, the deployment and testing quickly became an issue. Also, getting interfaces right and evolving them is hard. You may end up in dependency hell.
For us, microservices were not an option. So, we decided to stick with a monolith. But – still encapsulated and nicely split up, so multiple teams can work on it.
Engines In our existing app we had a few engines to separate components. But we were frustrated with them: They come with some boilerplate code. It is not obvious how to make tests and migrations work as if they were part of the same app. And yes, you can make it all work, but we did not find a satisfying way for us.
There are a lots more options to segregate your logic like putting stuff in gems or creating
app/services to provide re-usable components.
I continued my search…
The Parts Concept
To this day, I still don’t know how I found it, but there it was:
# documentation from: https://api.rubyonrails.org/classes/Rails/Engine.html class MyEngine < Rails::Engine # ... paths["app/controllers"] # => ["app/controllers"] paths["app/models"] # => ["app/models"] paths["app/views"] # => ["app/views"] # ... end
A way to split your app in components. I was excited.
I created the following folder structure1, moved a bunch of files around2, added two test controllers and removed the
app folder (!):
├── bin ├── config ├── ... └── parts ├── banana │ ├── controllers │ ├── models │ └── views └── cheese ├── controllers ├── models └── views
Now the only thing left to do, was to configure Rails so it knows where to find everything.
To configure the new layout, I changed the
module TestProject class Application < Rails::Application # ... paths['app/controllers'] = ['parts/banana/controllers', 'parts/cheese/controllers'] paths['app/models'] = ['parts/banana/models' , 'parts/cheese/models'] paths['app/views'] = ['parts/banana/views' , 'parts/cheese/views'] # ... end end
After a quick server restart, it had worked! The controllers in
banana responded as well as the one in
cheese. All models worked and the views were found.
There was no funny business such as custom require magic or monkey patching. No. A fully supported, standard way to change and add new sub-folders in Rails.
I asked myself: “Why had I never seen this before?” Turns out my colleagues would asking the same question soon.
Encouraged by my initial success, I wanted to know if this would also work for migrations and tests. Yes it does. You can put locales, specs (see spec_helper.rb & .rspec), migrations and assets into individual parts – even routes and seeds (using a simple SeedManager). After a few hours the project’s file structure looked like this1:
└── parts ├── core │ ├── assets │ ├── controllers │ ├── helpers │ ├── jobs │ ├── locales │ ├── mailers │ ├── migrate │ ├── models │ ├── policies │ ├── services │ ├── spec │ ├── views │ ├── routes.rb │ └── seeds.rb └── banana ├── controllers ├── migrate ├── models ├── policies ├── services ├── spec ├── views └── routes.rb
core would keep all the base classes like
Later, we decided that the
core part contains common classes needed by other parts (e.g. Users).
We made up the rule that no part may use another part, with the exception that all parts may use the
This way, we should be able to change one part without breaking another3.
After we started developing we had to do our homework: How do we slice our application into different parts? It quickly became clear, that we needed to draw the lines around business areas & responsibilities. We were tempted to do parts like backend or services. While backend would have ended up being a huge pile of everything, services is a natural folder in each part.
We are working with parts for a few months now. It is a simple way to divide the contents of your app up. It is:
- simple to understand, because we keep the Rails folder structure in tact
- better overview, due to the separation into several parts
- fewer dependencies, since we only allow depending on
- fully supported by Rails, because we only use standard configuration options
- fewer conflicts, since every team stays in their part
There are a some things to mind though:
- Parts look like namespacing, but classes are not isolated (you can still use regular Rails namespacing though).
- If you have two files with the same name, Rails gets confused (we added a simple linter to identify such common problems).
- The hardest part is to find a sustainable split of your app.
All in all, the parts concept works great. I am sure we will learn much more about the pros and cons in upcoming sprints. Please feel free to check this repository out and drop us a email if you have questions.
Have fun Tom
P.S.: If you want to work with parts first hand, we are looking for new members on our dev team. Send me an email.
I had to move the
assets, and the main layout. ↩