I have an Accounts application that manages core business logic. There is an Organization model in the Accounts application that represents a single instance of a real world client.
I’m now working on a Billing application, but I want it to be on a separate node. The Billing application will need to know the Organization’s name and its ID, but nothing else.
What is the “right way” to get the Organization information from the Accounts application to use it in the Billing application?
Should the billing application subscribe to Organization events on a bus and create its own instance of an Organization?
Or should I use
:rpc to query the Organization data on the node that’s running the Accounts application?
Or something else?
Running something on a separate node can never be a goal on its own: Why would you want to split your application up into these two things and run that one on a different node?
If you do go ahead and run its on a separate node, the following are possible:
- Connect the nodes directly using Distributed Erlang, and then indeed use
:rpc or similar functions (If you restrict what runs on what node, using
:rpc is not even necessary, since you can just use
GenServer.call directly). Distributed Erlang is however not that well-suited for having the nodes in different data centers across the globe.
- Connect them externally, either by directly communicating (only works when both nodes are available), or using a solution like RabbitMQ (keeps messages when receiver is down; can easily be extended once you add more instances of one of the nodes).
- Connect both nodes to the same datastore, and have this be the ‘single source of truth’. Useful for some applications, but makes your datastore a single point of failure. (And if it is not a distributed datastore, it also might not be that scalable).
And there are other possibilities as well, probably.
Thanks for asking this question, because I hadn’t actually asked myself.
I realized that it was because I wanted microservices. In other words, the Accounts application would be a microservice and the Billing application would too.
The original motivation arose from the different roles for application use – an administrative role, an employee role, and a client role. All would need to use the Accounts role in some capacity.
I think for now I’m going to consider a node per role and replicate domain events across all nodes.
Have a care about what events you distribute. It is easy to distribute events that are “too low level” and get unusual cross-service coupling that way. For example, if your ordering system broadcasts “order began”, “item added”, “item removed”, and “order completed” messages, and your inventory system had to respond to all those messages, then any change in the set of events, or their organization, could require changes to both components.
Instead of blindly broadcasting all events, components should have a curated set of semantic events that describe “milestones” for their domain. It’s no different than being careful to define the private or public interface of any other software component - just keep it in mind as you are looking at coupling service through domain events as well.