Mining gems in your existing codebase
@sanderhahn amsterdam ruby meetup 17th Dec 2014
Agenda• Why extract gems?
• Prevent Rails apps to grow into monoliths
• How to design more modular apps?
• Railties, plugins, engines and mountables
• Ready for microservices?
• Just a perspective, open for discussion and your milage may vary
How Rails evolves
• Rails is a collection of separate gems living inside a single git repository (synchronised release planning and versions)
• Deprecation is done by including and extracting functionality into separate gems to allow managing technical debt (optional use of new and legacy gems)
Evolution by inclusion and extraction
• attr_accessible moved into protected_attributes gem, replaced by strong_parameters gem
• responds_with into responders gem
• cache_digests gem
• asset pipeline into sprockets-rails gem
• active_resource gem
Rails apps are structured by type
• app/assets
• app/controllers
• app/models
• app/views
• config/routes.rb
• db/migrate
Monolithic apps
• Rails development is very fast in greenfield projects but can become slower as your application and team grows
• Tests might require large fixtures of unrelated data and can take a long time to run
Engines enable apps to be structured by function
• Website Content
• Newsletter Subscription
• Lead Generation
• Webshop
• Public API
Advantages• Apps are build from logical separate modules
with explicit boundaries
• Distribute responsibility of code and work in a team
• Code reuse in client projects is possible
• Tests are more isolated and run faster
• Technical dept is easier to manage
Guiding principles
• Loosely coupled system is one in which each of its components has little or no knowledge of the definitions of other separate components
• High cohesion means to keep similar and related things together which share the same responsibility or goal
How to dissect apps into modular gems
• Railties (initialization)
• Plugins (extend Rails functionality)
• Engines (app composition)
• Mountable's (isolated namespace and route)
Foundation of Rails# rails/railtie.rb class Railtie
# rails/engine.rbclass Engine < Railtie
# rails/application.rb class Application < Engine
# config/application.rbclass Application < Rails::Application
Railtie provides several hooks to extend Rails and/or modify
the initialization process
Rails components are Railties
ActionMailer example
Plugin is an extension or a modification of the core
framework
Plugin structure$ rails plugin new gems/myplugin
Gem dependencies
Plugin config with Railtie
Engine is a miniature application that provides functionality to its
host application
Engine structure$ rails plugin new gems/myengine --full
Rails generate/destroy are available inside an engine
$ cd gems/myengine
$ rails generate scaffold user name:string
$ cd ../..
# add dependency in Gemfile
$ rake myengine:install:migrations
Mountable is a namespace-isolated engine
Mountable structure
$ rails plugin new gems/mymountable --mountable
Rails inside a mountable
$ cd gems/mymountable
$ rails generate scaffold post user:references title:string
$ cd ../..
# add dependency in Gemfile
$ rake mymountable:install:migrations
Engine vs mountables
Cross engine url_helpers
Testing an engine• At application runtime all engines are available
• Integration testing is done by creating a dummy app inside the test dir
• Other engines are only available if specified as a dependency
• Fake models and migrations can be created in the dummy app on engine boundaries
Dummy app for integration test in the devise gem
Example: Tolk gem is a Rails engine for translating your app to other languages
Tolk structure
Tolk generator
Thor and Generator actions
Breaking the Monolith @ Soundcloud
We decided to move towards what is now known as a microservices architecture. In this style, engineers separate domain logic into very small components. These components expose a well-defined API, and implement a Bounded Context — including its persistence layer and any other infrastructure needs. … In the end, the team decided to use Rails' features of engines to create an Internal API that is available only within our private network.
http://www.slideshare.net/pcalcado/from-a-monolithic-ruby-on-rails-app-to-the-jvm https://developers.soundcloud.com/blog/building-products-at-soundcloud-part-1-dealing-with-the-monolith https://developers.soundcloud.com/blog/building-products-at-soundcloud-part-2-breaking-the-monolith https://developers.soundcloud.com/blog/building-products-at-soundcloud-part-3-microservices-in-scala-and-finagle
Engines vs microservicesMicroservice architectural style: designing software applications as suites of independently deployable services
http://martinfowler.com/articles/microservices.html
Bounded Context helps guiding engine design
Domain-driven design (DDD) deals with large models by dividing them into different Bounded Contexts and being explicit about their interrelationships
http://martinfowler.com/bliki/BoundedContext.html
Thank you!
Top Related