How do YOU use umbrella apps, and why?

I’ve been using umbrellas for a while, and generally started off (on greenfield projects at least) by isolating subapps based on clearly delineated lines. For instance, imagine building an orchestration system; one subapp responsible for managing the internal state (owns the DBs), another for public side (API, etc). But I’ve found myself opting instead to not use umbrellas in newer things, with similar complexities, opting instead to explicitly define larger supervision trees and keep related code in the lib/ hierarchy; while at points where it makes sense, creating separate libraries for common code that can be shared, even if it had originated inside the app.

I guess I’m trying to get a sense of whether people are heavily investing in umbrellas, or what your use cases for umbrellas are and why?

5 Likes

Although your question is not Nerves specific, have a read thru this post. I’ve not seen anyone actually use an umbrella app in production. (i’ve not looked for one either) when i need these application boundaries you are describing i use the “poncho” project structure. Basically just hard code path: dependencies in a greater folder/repo.

3 Likes

Slightly off-topic, there is also a good discussion about Umbrellas vs apps as a dependency here:

If you haven’t seen Dave’s course I highly recommend it:

:023:

I see umbrellas mostly as a tool for code organization. You have a number of coupled OTP applications and you want to make version control and version management easier and hence use an umbrella mono-repo.

I feel the question you are asking is rather how to split work up between different OTP applications. If they are part of an umbrella or stand-alone dependencies make very little difference.

The questions you need to ask:

  • Do you want a separate OTP application with its own supervision tree? Then you must make it an OTP application. This is about how you want your application to fail. For example, are you OK with an application dying but want the rest of the system to keep going? Then use a stand-alone application. Do you want your application to fail if it fails? Then either depend on the application or move its supervision tree into your supervision tree (either though included applications or just by starting its supervisor in your tree)
  • Is the OTP application coupled with the rest of the application? I.e it is only going to be used for this particular application? Then I believe a mono-repo makes life easier but more from an SCM point of view rather than being an elixir problem.
  • Can the OTP application be re-used in a different application? Then it should be a stand-alone application and not part of an umbrella.
6 Likes

I’ve used umbrella apps to decompose a large web API. One main app defined the Endpoint and common Plug pipeline, then forwarded to child apps.

Each child app exposed a Router and defined a test-only Endpoint. We’d typically CD into an app directory while working on it, then run the whole project test suite before pushing to git.

All apps shared a single data warehouse, so the Repo and shared Schemas were defined in a shared app.

The shared DB architecture made it a good fit for the umbrella project. We could apply migrations and test that all apps depending on those tables were still working.

For a new project I’d like to try apps for web, cron, jobs and core logic.

5 Likes

So I’m not a new user, me and my team have been making use of umbrellas in production for over a year now, and have learned a few things:

  1. Being too quick to make an umbrella can be annoyingly painful to correct

We have a situation where occasionally we’ve had bugs in one app that cause crashes which end up propagating to other parts of the umbrella due to inter-app dependencies, resulting in other workers failing which should be isolated. Granted this was entirely our fault, but it was a difficult one to work around, one that if we’d spent a bit more time thinking about supervision more generally as one thing instead of split across multiple points, it would have resulted in a tree that was more resilient to this kind of failure. Lesson learned.

  1. By the time you have reason to split out common functionality into a separate library application in your umbrella, you already have met the need to have it as a standalone dependency.

If we gauge that criteria as having been using the same code in different places across application boundaries, it’s been my experience that an easy case can be made to extract it out of the umbrella entirely and have it stand on its own.

These and other minor issues over the past year, have led me to question the benefit of umbrellas, but I have only about 3 data points to work with – 3 umbrellas we have in production.

5 Likes

I see what you mean, and I agree. Perhaps using umbrella’s muddies the water and make it too easy to couple applications. We also found it hard in the beginning to find the right boundaries between applications and I agree that if you get too deep down that hole it is painful to get it back in shape.

Over the years we’ve gone from umbrella, to stand-alone and then half-way back(mostly in erlang though) and to some degree share your experience, especially with our first large project which too ended up with shoddy dependencies between applications and it took a while to refactor. Then we moved away from umbrellas for a couple of projects and now we are back to using a “reduced” version of them where they are designed around the failure semantics of the larger application, and trying to keep them reasonably loosely coupled.

The way we use umbrellas does not make much difference as we could as easily move parts to a stand-alone dependency and include them in a release but we choose to keep them in an umbrella to more easily keep shared protocols, schema and data structure in sync between applications.

5 Likes

My team is using it as monorepo. Some apps are pretty independent ao they might be in different repo - but for easier workflow we merge then into one umbrella project.

Supervision tree and isolation are kind of related but that’s not huge factor for umbrella or not.

2 Likes

Same here. Whether you use an umbrella or separate apps + path dependencies makes very little difference. A mono-repo however saves you a lot of code management work like git submodules or having to run same script commands 5 times in a row (I’ve seen people argue that you can do your own scripts that do that for you but this makes onboarding a bit harder, and makes the junior devs believe in magic which is highly undesirable).

As for apps vs. libraries – if any piece of code has any “active” parts (like firing background jobs or updating internal cache from a SaaS source or global database), then it must be an app; if it can be entirely passive and is only called when needed, it should be a library. If you can open-source it, even better.

6 Likes
2 Likes

This had me cracking up… gonna remember this saying…

4 Likes

Do you mind explaining the meaning of this? :sweat_smile:

We still use umbrella apps for organization and boundaries like (Common Services, API, WEB x, …) and deploy them in containers with distillery (we didn’t upgrade for native package yet).
Since we have multiple endpoints we also centralized localization with gettext in the services app.

Well basically, have an app in ~/code/app_a and ~/code/app_b and in your mix.exs of app_b you can do this (in your deps function):

  #...
  {:app_a, path: "../app_a"}
  #...

(Please note: for the ".." path to work, both apps have to be in the same parent directory, in this case ~/code.)

You can specify an absolute path as well but I rather not.

1 Like