Best practice for calling a function in another application without defining a dependency

I think so. I mean if A depends on B, but B can call A’s code, then B also depends on A and you’ve got a circular dependency whether you’ve written it down in mix.exs or not.

Phoenix.PubSub is both a bit heavy weight, and also I think the wrong paradigm. What you need are event handlers, which can happen with basically just pure data, the process based subscription model of Phoenix.PubSub doesn’t make sense here. Simple version basically just does the following.

Have A configure b with an event handler module found in A:

# in app A's config

config :b, event_handlers: [SomeModuleInA]

Then in B, instead of calling A’s code, grab event handlers and call a function:

for handler <- Application.get_env(:b, :event_handlers) do
  handler.dispatch(:foo_created, some_data)
end

We’ve done this in our umbrella apps and it’s worked pretty nicely. It avoids a circular dependency, while still avoiding a “broadcast to the winds and who knows who responds” kind of situation, since it’s pretty easy to keep track of the handlers list.

9 Likes