Referenced article is among the best I’ve read in the past year. While I
don’t agree with everything stated there, the ideas described are
super awesome (this is when I found out there is a limit on a number of
claps you can give in medium). I’ve tried to apply them in my pet project
that is built with classic Rails structure - while extraction wasn’t easy
the result was definitely worth it. Here I’ve extracted a gem and an engine
from the project into a brand new Rails application and it was painless,
moreover this extraction improved the design of the engine. I will cover
important pieces of the setup below, but for TLDR people here is a
Github Repo, check the seed.rb file for credentials to use.
Gem Overview
The gem allows you to fetch event data from a Google Calendar
separation of dependencies
Code in the local gem has it’s own dependencies but does not
rely on a main app, these dependencies were moved from the parent app’s
Gemfile into a gem’s *.gemspec
and are loaded in initializer
separation of tests
All Unit tests for the gem were moved to a gem’s folder, they can be
executed independently and in isolation - navigate to a gems folder
and run bundle exec rspec spec/. In order to make tests run you will
need to do a manual setup in helper file
Engine Overview
Engine provides authentication using authlogic gem
separation of dependencies
Same pattern as in gems, all dependencies live in engines *.gemspec and
do not pollute parent app’s Gemfile.
and are loaded in the initializer
engine also depends on a local google_calendar gem, it is loaded
directly in the Gemfile
separation of tests
All Unit tests for the engine were moved to a engine’s folder, they can be
executed independently and in isolation - navigate to a engine’s dummy
application folder domains/customers/spec/dummy/ and run bundle exec rspec spec/
In order to make tests run you will need to do manual setup of the
environment in the helper file
separation of migrations
I believe migration files should not be copied to a parent application,
required configuration is specified in engines initializer
separation of translations
Translations for views from an engine also live in an engine, configuration is
specified in engines initializer
Enabling authentication in the main application
Authentication was extracted to be a controllers concern so you need to
add this concern to a controller
Testing Engine/Gem Integration into a main application
I believe that tests located in engines/gems should be unit tests - they
should run fast and stub any external dependencies. It doesn’t make sense
to me to test integration outside of the main applications - System Tests
are great tool to do this job. A basic example
Summary
Extracting a gem was quite easy, extracting an engine was a bit of work.
Advantages of the Modular Monolith over the classic app:
Separation of code - dramatically improved application design
Separation of dependencies - keeps main application cleaner
Separation of tests - each unit has it’s own suite that is fast and
can be run independently