In the previous part, we talked a bit about Rails engines. In this article, we’re going to talk about how these engines can be organized and work together.
This is the fourth article in the Modular Rails series. Here are the other posts:
- Modular Rails: Introduction
- Modular Rails: Use Cases
- Modular Rails: The Engines
- Modular Rails: Architectures
- Modular Rails: Create a modular app
- Modular Rails: F.A.Q
Monolithic vs Modular
Creating a modular application is quite different from building a traditional Ruby on Rails application.
Regular Ruby on Rails applications are monolithic apps. They are self-contained applications that will do what you programmed them for. Note that this is not a bad thing. Ruby on Rails is known for its fast prototyping that will let you create a MVP (Minimum Viable Product) fast and easily.
Now, let’s talk about modularity. There are many ways to make a modular application. After all, the main principle of modularity is to build something with a set of modules.
These modules can be micro-services for example. But that’s not the approach I want to talk about in this article and in my book. What I want to teach you is how to build a modular Ruby on Rails application composed of a set of Rails engines as your modules.
Now that we know the difference between monolithic and modular applications, let’s see how to organize our app!
By using engines as modules, you could say that we are still deploying a monolithic application. That’s true, but you can still extract, add or export modules easily. If you need to, you could even extract one engine and run it in a new application as a micro-service. That’s the power of what I want to show you!
There is not just one way to organize your code. Here, I’ll go over three architectures that I’ve personally played with. I’m sure there are more and if you have any suggestions, I would be happy to talk about them with you!
As we said earlier, we are going to separate a monolithic application in small modules. But what should we put in these modules? Since we will be using Rails engines, we can put models, controllers, views, migrations, tests and so on. Basically everything that you will find inside a Ruby on Rails application.
But how do we organize all that? Should one module have all the models? Or should one module have a model-view-controller silo?
Approach 1 - Three Tier Modules
You should be familiar with the three tier architecture. If not, here’s a definition from Wikipedia:
‘Three-tier architecture is a client–server architecture in which the user interface (presentation), functional process logic (“business rules”), computer data storage and data access are developed and maintained as independent modules, most often on separate platforms.’ - Wikipedia
The idea is to separate the application into the following modules/engines:
- Engine 1 - Data Engine
This engine contains the data layer: models, migrations and models specs.
- Engine 2 - API
This engine contains an API allowing external applications to connect to it through RESTful URLs. This engine depends on the Engine 1.
Here’s a schema of this architecture:
This architecture can be optimized based on your needs. For example, you could put the second engine code (API) directly inside the parent application. You could also separate the Engine 1 into smaller engines. Keep reading to learn more about other architectures to find the perfect one for your needs!
Approach 2 - Hybrid Component Based
This approach is the first one I tried when I started to work on modular applications. With time, I realized that it’s not the best solution. The idea is to have a couple of engines representing the minimal logic of your application. You can then plug new engines on them and add more features.
With this architecture, the ‘core’ of your application will be composed of at least two engines. Let’s call them Data and Presentation. The Data engine contains the Models and the Migrations. The Presentation engine includes the controllers and the views.
Now, you can start adding more modules which will depend on Data and Presentation.
- Feature 1 engine (models + controllers + views)
- Feature 2 engine (models + controllers + views)
- Feature 3 engine (models + controllers + views)
That might look abstract, so to give you something more concrete, here’s how your Gemfile might look like with this architecture:
gem 'core_data', '1.0' gem 'core_view', '1.0' gem 'feature_1', '1.0' gem 'feature_2', '1.0' gem 'feature_3', '1.0'
And here’s a diagram showing you the complete architecture:
Approach 3 - Full Component Based
The Full Component Based architecture is a refined version of the previous example. In this one, we remove the Data/Presentation engines and the separation of the models, views and controllers into different engines. Instead, each engine encapsulates all the components required to make itself work. That means each engine is a micro Rails application containing models, controllers, views and specs.
- Core (models + controllers + views)
- Feature 1 (models + controllers + views)
- Feature 2 (models + controllers + views)
In this case, the Core could be run alone inside the containing application and would offer only limited features. For example, we could simply include the minimum code for users to be able to login and access an empty dashboard. We can then add completely encapsulated features to the core to improve our application.
This is a very good solution to build modular applications with Ruby on Rails. We will build an application of this type in Modular Rails.
Here’s a diagram of this architecture:
Want to learn more?
You can continue reading about modularity with this article where I show you how to create your first modular application.
You can also checkout the book I wrote about modular applications : Modular Rails.
Don’t hesitate to subscribe to my newsletter in the form below or on the book website to receive a free sample of the book.