TL;DR
If OTP application A makes calls to a globally registered gen_server in application B, and I don't want to install all of app B on nodes that don't run it, how do I handle the gen_servers client code?
Background (slightly simplified)
I have a system using distributed erlang, 2 nodes with distinct purposes, running mostly different code. So far, I have been using hand made Makefiles and installed all software on both nodes. Some of the code is run as OTP applications with supervisors, but it is not done systematically so not all modules listed in any app-files of part of proper supervision trees.
The dependencies of the code running at each node is different enough that I want to divide it into OTP applications (one per node), to build releases and install them separately. I hope this would let me ditch my handmade Makefiles and switch to rebar3.
One node runs a central server in all erlang, it has dependencies (cowboy) which are not relevant for the other node. The other node runs a client program that use the server, but also use different port programs and gui libs which are not needed in the server node.
Problem
The way the client interact with the server is by making regular function calls to the client API of a globally registered gen_server. I.e. the gen_server which runs on the server node, has its client functions in the same module. This means that this gen_servers beam file needs to be present in both nodes, but it should only be part of a supervision tree in one of the applications.
The server side code in this gen_server uses other modules that are only needed in the server node, thus there are test code for the gen_server that also depend on those other modules. (I realise this could be solved by proper mocking in the tests.)
What solutions have I considered?
Put it in a library application
I could put the gen_servers code in a library app which both the others depend on. It would be strange for few reasons.
- The gen_server module would no be part of the same app as the other modules it depends on (and the app-level dependency would be reversed compared to the actual dependency in the code).
- Test code would either need to stay in the server app (not the same app as the code it tests) or be re-worked to not depend on surrounding modules (which would be good but time consuming).
Include the server app in both releases
I could include the server app in both nodes, and have the supervisor code check if it should actually start anything based on init-arguments or node name. But it would kind of defeat the purpose of what I'm trying to do.
Include the gen_server module in both apps
I could use a symlink or something to include the gen_server module in the client app as well. I guess it would work but it feels dirty.
Split the gen_server module into a client- and a server-module
Then the client module could be put in the client app (or in a lib if some part of that server also use it). It would divert a lot from the way gen_severs are are usually written.