Separate umbrella apps can access each other despite have no defined dependencies?

I created an umbrella project, and created two apps for testing:

defmodule Appa do
  def hello do
    "this is a string in appa!"
  end
end

and in a separate umbrella app:

defmodule Appb do
  def hello do
    IO.puts "I will print from appb"
    IO.puts Appa.hello()
  end
end

When I call hello from Appb (in a shell started with iex -S mix) it prints:

I will print from appb
this is a string in appa!

I haven’t defined any dependencies between the two apps. I was hoping I would have to explicitly define a dependency between the two apps for this code to work.

Perhaps I’m having this issue because my “apps” are not really “applications” in the Erlang sense of the word? I think I’m not using umbrella apps correctly?

If I were using umbrella apps correctly will explicitly defined dependencies be required then? This is something I would appreciate if it’s possible? I’ve seen too many projects where people will call any code from anywhere, and the project ends up being a ball-of-mud, where all parts of the code depend on all other parts, and changing one part usually requires changing all other parts. I believe having a sane structure, and sane dependencies between “modules” to be one of the most important design principles, and I was wondering to what extend umbrella projects can help with this?

1 Like

Unlike Java for example, Elixir doesn’t require you to import / require other modules in order to use them. As long as the module is loaded into the BEAM, it will “just work”. That’s how you use the Elixir standard library, as well as OTP.

Defining dependencies in mix.exs is just a way to get the build tool to download and compile external dependencies, putting the results into the VM’s load path.

When you create an umbrella project, you’re specifically saying you want all of the included apps compiled and loaded together, usually because they’re so tightly coupled at the time (with regards to new feature development etc at least).

So no, you don’t need to declare any dependencies between them… If you want that, maintain them as entirely separate projects, and accept the pain of handling version control etc separately whenever you need to develop a new feature that requires changes in all two, three or five of them :stuck_out_tongue_winking_eye:

3 Likes

You are right, you should not be able to access one child application from another one without explicitly declaring it a dependency.
However, from your umbrella project, you will be able to use all child apps/modules automatically. Did you maybe start your iex session from the main folder instead of the folder of Appb?

2 Likes

So I think you have similar (wrong) idea about Erlang/Elixir apps that I did have initially. I thought that having multiple apps loaded on a VM involves some sort of separation, i.e. that one app cannot call public functions on modules from other apps, and the only way to communicate is to pass messages between processes/apps.

This is not the case. When an umbrella project is started (from it’s project root), it will load all the applications on the same VM. The modules of the apps are not separated in any way, and from Appa you can call public functions of modules in Appb.

It’s up to programmer to enforce (or not) the separation. What I like to do for my clients’ projects, is that umbrella apps that will be interacted from the outside, specify some sort of public API. There is no good way that I know of, however, that would limit a programmer from calling other public functions, if they are known. It’s up to you :slight_smile:

2 Likes

Yes, I did. I think the “apps” in an umbrella project should probably be bigger and more self contained than what I was originally picturing.

Thanks for the replies. They answered my question and gave me some good insight into how Elixir and umbrella projects work.

2 Likes

It’s pretty well explained the why already. But sometimes you want it to hardly fail if you use code that you shouldn’t.

For example: if the application A depends on application B, it’s a very well known good practice not to have B depending on A.

If these two applications were not on the same umbrella the code would not compile, and one of the advantages of making umbrella projects is the ability to extract any app under it to reuse it out of your umbrella project. That would not be possible if you have circular dependencies there.

So, I went through this problem and what I have done to make sure it never happen again is very simple:

for app in apps/**; do cd $app && mix test; cd ../..; done

This will try to compile and execute the tests for each application individually. This will make sure you don’t use any code that is not included as a dependency on your application.

5 Likes