Communication between Rails engines

Philip Sampaio
Oct 19, 2015 · 3 min read

So you had the idea to separate different areas, A.K.A. domains, of your application using Rails Engines? You thought it was a great idea until have to communicate through two or more engines?

An example of what you want to do

This article explains how to do that communication, and how you can keep the productivity using Rails and a little bit of DDD.

If you are not familiar with Rails engines, they are great tools to separate concerns, reuse code, or simply namespace things. The most famous example of Rails Engine is Devise. You can learn much more reading the Rails guides about it.

A bank system

To illustrate the problem, I will follow the common case of a bank system. The engine one is called "Accounting", the engine two is called "Credit Report". They are two different domains of our Bank system. You can call them "subsystems".

The domain of this bank system

The Accounting engine is responsible for the transfer transaction that needs to trigger a new credit report for the accounts related to the transfer.

So we need to send a message to the "Credit Report" subsystem somewhere around the return of the "transfer" method.

Something like:

Don't do this

In this example you can see the message being sent to other domain ("CreditReport"), directly inside the "transfer money" use case.

This article could finish here, but this would lead to some problems: I'm invading a domain with another that does not have a strong relationship. It means that two subdomains are strongly connected. This is … not good.

An alternative to that is to inject the "CreditReport" subsystem into the "Accounting".

This can be done using a configuration, by setting the constant representing the Credit Report subsystem as string.

Line 8 does the magic of transforming strings into constants.

And this configuration would be done inside some initializer in the main app (Bank). The reason we are configuring using a string (and getting the constant dynamically) is that Rails can use auto load without problems.

We can change the "transfer money" use case to use that injected/configured dependency.

Using injected version

Good! Now the system is more flexible. There is still existing a contract between the two subsystems, but now "Accounting" does not need to know that a real "CreditReport" exist.

The problem with this approach is that if we want to test this engine in isolation, we will eventually need to depend on "Credit Report" engine or create a mock to represent that subsystem.

To avoid a strong relationship between the Accounting and Credit Report engines, we can create a Pub/Sub communication between them.

Watch out:

Low coupled version

You can see that the Credit Report dependency is gone. A new line get in. That line publishes the event with the result value.

This approach enables us to configure a subscriber class that receives the event and sends the message to the target, which is the Credit Report subsystem.

And the actual message to the "Credit report" goes inside the "AccountingTransferDoneEvent" class.

This class could go into "app/events/" in the main app.

A possible problem with this is that the events are almost async. You can't check if they work as expected in the publisher class. This is not a problem if you are dealing with something that can be treated as async job, and therefore can handle errors in isolation.


Communication between two domains is very common, and using Rails engines is easy to get separation of concerns while we keep the productivity that Rails brings to us.

Using dependency injection through configuration facilitates the communication, and enable us to easily change the implementation of the dependency. But it's much more flexible to have the event system when you can deal with async messages.

A POC using the event system can be found at

This article is highly inspired in the Clean Architecture ideas applied to Rails. Thanks to Fabiano B. for the insights and great discussions we always have. And thanks to the BankFacil folks, that started a great discussion about architecture using Ruby.

magnetis backstage

giving back to the community with things we learned at…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store