Advantages of umbrella applications?

I would like to better understand what the advantages/disadvantages of umbrella applications are compared to structuring your app as as single OTP application.

This isn’t specific to Phoenix but let’s use Phoenix as an example. Why would I create a separate Phoenix app in an umbrella over the regular structure where the domain logic is just organized separately from the web logic (the Phoenix part). Elixir/OTP has supervision trees which even keep these isolated from the rest of the application in terms of what gets run.

1 Like

I’m not necessarily any kind of authority here, but one point of reference here is Saša Jurić’s “boundary” library…

Umbrella apps can be a slightly more built in way of enforcing boundaries, and/or for composing your applications from components that you might wish to extract/reuse as libraries or open source projects in the future. That said, you do need to be careful about the boundaries you create in them, and when developing locally it often won’t stop you from violating those boundaries until you run on CI or build a release, at which point boundary violations will surface, somewhat the opposite of the boundary library which will always tell you at compile time (at the cost of some additional explicit declarations). I think when in doubt, having a single application is probably a good bias, as if you aren’t certain about some boundaries, getting them wrong can be a little messy to untangle/reorganize later, but if you already have an umbrella app structure, adding (or moving) one application around within it is pretty easy. There are some downsides for sure though, like how testing works in umbrella apps, and possibly managing dependencies since there’s still a single global mix.lock but possibly duplicated dependencies in different umbrella apps.

2 Likes

To be honest, I read your description and I don’t see why an umbrella app would be a good thing. Except adding a lot of additional song and dance with boundaries, and how testing and dependencies work

In most cases - that what you have written is 100% true.

I mean, I guess that’s why I said all else being equal, defaulting to a single application is probably for the best? But I guess one man’s song and dance is another man’s architecture and safeguards/seperation of concerns? :man_shrugging:

4 Likes

This is really close to the question which recently was discussed here:

5 Likes

There’s always a lot of discussion around umbrellas and when to use them. To me umbrellas are an alternative to developing multiple applications – for whichever reason they are or need to be separate – rather than an alternative to a single application. I’m not sure the discussions pro or contra splitting an application will differ in arguments, but imo the context and judgement of tradeoffs is different.

2 Likes

An umbrella project (not “umbrella application”) can offer multiple releases. If you don’t need that, then they don’t offer much that can’t be achieved in other ways.

7 Likes

To elaborate, each umbrella application gets built into an individual release, which is deployed separately. This is useful, for example, if you have a web app umbrella app and a background job umbrella app. They can use the same configuration, dependencies, common code, and build process in the same repo, but then their releases can be deployed and scaled separately according to their unique resource consumption needs.

4 Likes

Actually, revisiting my past umbrella apps, I remembered another usecase I reach for often:

There are kind of 3 levels of abstraction of mix/OTP “builds”

  • a compiled codebase
  • an OTP application
  • an OTP release

This kind of correlates to the 3 reasons I use umbrellas, with the second being the one I just remembered:

  • a compiled codebase
    If you are using umbrella apps because you need more than one of the below, you can create no-runtime dependencies as umbrella apps to get code-sharing.

  • an OTP application
    OTP applications are kind of a way of building singleton runtime boot-time dependencies. For example, many libraries provide an OTP application you ensure must get started before your own by specifying it in your mix.exs. You then set up your own project as an application that will boot after dependencies start successfully. This is useful for libraries that need to manage their own supervised stuff like connection pools, or timers that must be available for use by the time your project starts.

    I’ve found a lot of value in adding these to my own projects. For example, I often have a configuration umbrella app that brings together all my runtime env var, file, and external systems settings into an ETS table. Other umbrella applications go through this centralized place to get runtime configuration. The key advantage of this is that if a required setting is missing, or malformed and unparsable into the expected data structure, the application fails to boot—and any of my umbrella apps trying to use it never get around to booting themselves. This is nice because runtime configuration failures no longer happen at the site they are used/first referenced, but instead in a really clear place in the backtrace. This fails blue-green deployments way earlier in their boot cycle. And none of my applications get around to consuming any resources or establishing things like db connections because they require the config app to start successfully first.

  • an OTP release
    You’d use this for the reasons I describe above to deploy different applications with different deployable executables.

TL;DR: umbrella apps are also useful for building reusable OTP applications as well as reusable code and deployable apps.

2 Likes