Multiple Phoenix apps, one umbrella

Hey folks, asked this on Slack and may have gotten an answer there, but I’m a screen reader user and Slack is a bit of an accessibility nightmare. :slight_smile: So I may have missed it.

We’re currently refactoring a monolithic Phoenix app into an umbrella. The impetus is that we need two closely-coupled web apps. One runs online and is available over the open internet. The other runs in a VM appliance, on-prem. Ultimately they’ll share lots of common code, but the on-prem version won’t include a number of features that only make sense on the open internet, and will also use a different storage layer that can more effectively handle failed upgrades without needing a database rollback. Currently we have apps/web for the web interface, apps/vm for the VM appliance, and apps/shared for code that I manage to share between the two. Eventually I’d like to keep as much in apps/shared as possible, but there’s unfortunately a bit more duplication than I’d like right now.

One issue I’m hitting is with multiple web apps in a single project. I have apps/vm and apps/web. In local development, I can cd apps/vm && mix phx.server. But I’m having trouble running cd apps/web && mix phx.server, because apps/web has many more dependencies. In particular:

$mix phx.server
...
== Compilation error in file lib/web/controllers/auth_controller.ex ==
** (MatchError) no match of right hand side value: :error
    lib/ueberauth.ex:368: Ueberauth.get_providers/2
    lib/ueberauth.ex:239: Ueberauth.init/1
    (plug) lib/plug/builder.ex:304: Plug.Builder.init_module_plug/4
    (plug) lib/plug/builder.ex:288: anonymous fn/5 in Plug.Builder.compile/3
    (elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
    (plug) lib/plug/builder.ex:286: Plug.Builder.compile/3
    (phoenix) expanding macro: Phoenix.Controller.Pipeline.__before_compile__/1
    lib/web/controllers/auth_controller.ex:1: Web.AuthController (module)

That’s a brand new error. My non-umbrella app didn’t throw this, and running mix phx.server from the top-level directory doesn’t either, but then I don’t necessarily know which web app is running or how to reach one and not the other. It only seems to surface when I run the web app from its directory. Ueberauth providers are listed in config/, and that file may not be findable if I run the app from within its directory.

So in this instance where I’m trying to build two web apps from the same umbrella project, is it possible to run a single app at a time? Or do I have to assign them different ports? Hoping I can run only a single app, since our full web app relies on bunches of services I’m not running locally, and I’d have to bring up the full development environment just so it’d have something to connect to and not use.

Thanks.

4 Likes

You may want to take a look at the acme bank example here, they are running their frontend and internal admin phoenix apps from an umbrella with a config that handles proxying to the right app.

2 Likes

And this is the proxy that uses: https://github.com/jesseshieh/master_proxy

I’m using it at a deployment service to proxy to different endpoints based on hostname behind the same port.

1 Like

Thanks, folks. Unfortunately, both of these replies miss the mark a bit,
which makes me wonder if I’m abusing umbrellas in an unintended way.

We have a piece of business functionality that communicates with a GRPC
backend. We also have two web frontends. One, which I’ll just call “web”
runs on the open web. It has authentication, ability to link
social/Oauth accounts, etc. The other, called vm, is a currently
unauthenticated web frontend which runs in an on-prem VM. But there is
some shared code–the GRPC converter interface, some utility libraries,
etc. Packaging these as separate Elixir libraries is certainly an
option, but for better or worse, last week I went with an umbrella app.
We have apps/shared, apps/web, and apps/vm.

I figured out part of my issue was that I needed to set the build,
mix.lock, deps, etc. directories in each sub-app. Generating a new app
did this for me, but my rough conversion to an umbrella didn’t, so
copying that code helped.

My next task, once I reach my first milestone and communicate with our
GRPC backend from the newly-created VM sub-app, is to wrangle some of
this code duplication. At this point I’m basically copying and lightly
modifying controllers, because the first thing each one does is Use Web or Use VM, and I need to get them all using the same base module
while swapping out a few basic modules. So Web will use a Documents
module backed by an Ecto repo, VM will use Documents backed by DETS,
etc. But that may be more trouble than it’s worth. We have a very small
team and a fairly broad scope (story of my life) so I’m trying to strike
a good balance between minimizing code duplication while not obfuscating
intent.

@ndarilek did you stick to umbrella approach or moved to separate packages?

I’m currently trying to do basically the same - merge into umbrella couple separate projects, that have a lot of duplicate code (logging, telemetry, request/response schemas and etc.).

No, we actually went back to separate web apps and a shared library.

1 Like