You may have heard of monolithic applications.
For the last few years they’ve been regarded as a Bad Thing.
That huge single application, full of dependencies. Impossible to break into pieces. Impossible to understand its individual pieces in isolation.
The answer, according to many, is to use microservices.
Split your application into separate pieces. Each one has its own API, its own database. Each bit is small and easily understood. Each bit can be scaled independently. Or even rewritten in another language if that makes sense.
But now you’ve got to keep track of those dependencies. Make sure your API is properly versioned. Deal with failure. Deploy in stages. Ensure each API call is fast enough as you cross process, server and network boundaries.
For Rails applications there’s a middle ground. Rails Engines let you split your Rails application into separate pieces. Each one small, simple and easy to understand.
But you then mount them inside a container Rails application.
To the outside world, your application looks like a monolith.
But internally, it’s made out of separate pieces that can all talk to each other.
The best of both worlds.
The first step is to decide upon your boundaries. Which pieces belong where? Once you’ve decided that, it’s time to get going…
How to build an engine
It’s pretty simple to build an engine.
rails plugin new myapp --mountable
This creates a Rails plugin, which looks pretty much like a traditional Rails application. An app folder for your models, controllers and views. A config/routes.rb so you can define how the outside world talks to you. And a lib folder, which is where your engine diverges from a standard application (an application’s config/application.rb becomes lib/myapp/engine.rb). And instead of a standard Gemfile, you have a gemspec, because your engine is actually a gem; just with a special structure.
For the most part you write your engine as you would a traditional Rails application. The tests are slightly different (there’s a dummy Rails application in the test folder which simulates your container) but it should be pretty simple to get going.
bin/rails generate model Whatever
bin/rails generate controller DocumentsController
All very familiar. Apart from everything you create is namespaced – MyApp::Whatever, MyApp::DocumentsController, to keep it separate when your engines are reassembled.
How to assemble your engines
Once you’re done, you need to create a “container” application.
Add your engine to the container’s Gemfile and bundle it.
Now your engine’s models and code become available to the container (within their namespace).
Use the container’s routes.rb file to mount your engine –
mount MyApp::Engine, at: '/myapp'
Now your engine’s controllers and views become available to the container.
Add in your other engines into the container application, again, loading them as gems and mounting them into your routes.
Repeat till your container becomes fully functional.
One application, neatly divided into separate pieces.
And the real advantage – each individual engine is now fully reusable.
Use it in your next app. And the next one. And the next one. And the next.