Building an Elixir Umbrella App
In this series, we'll builld out an Elixir umbrella app with two child applications. One of our child applications will be a Phoenix API with a React + Redux front-end. Another child app will build out a YouTube API client written in Elixir.
In this post, we'll learn about umbrella apps, why they're useful, and how to get started building one.
What's an umbrella app?
An Elixir Umbrella application defines one parent application that houses and manages a set of child applications. Children applications can be straight up Elixir apps, and/or Phoenix applications.
Why we're building an umbrella app
At TuneCore, we distribute our customers' releases to a number of music stores and streaming outlets, including YouTube. Our deliveries to YouTube, however, can be tricky to manage. Since YouTube can take a while to process the delivery of a given song, we don't always receive an accurate confirmation of a successful delivery from YouTube. In order to get this information, we have to make an API call to YouTube, separately from the work of making the initial song delivery.
One option for making this API call and communicating the state of a YouTube song delivery would be to shoe-horn that code into our existing delivery code.
Our existing delivery code is already complex, however. Wouldn't it be great if we could manage the state of a YouTube distribution in one centralized location, instead of tacking on yet another micro-service?
Enter, the Elixir Umbrella app!
An Elixir Umbrella app allows us to:
- Separate out the discreet responsibilities of our YouTube distribution domain
- Manage each of these responsibilities under one parent
- Make each individual child app aware of/able to access the code of its siblings.
In this post, we'll set up our umbrella app and set the stage for Part II. In Part II, we'll build out our first child application––an Elixir app that contains a YouTube API client.
Let's get started!
Designing the Umbrella App
Before we write any code, we'll plan out the architecture of our umbrella app. In order to do this, we need to determine the discreet responsibilities of our "YouTube distribution" domain.
Currently, we're scoping our project to one area: checking the status of a delivery with YouTube.
Checking the status of a song's (or batch of songs') delivery to YouTube requires a few different steps:
- Consuming a list of songs (identified by their ISRC) from the user
- Sending an API request to YouTube with those ISRCs and collecting the response.
- Displaying the response, i.e the confirmation from YouTube regarding the presence of a given ISRC in their system.
We can group these responsibilities into two buckets: communication with the YouTube API and communication with our front-end user via a web interface.
We'll take each of our buckets and make them into their very own child app of the larger umbrella application.
The child app responsible for communicating about deliveries to the YouTube API will be a plain Elixir app, which we'll call Deliveries
. While this app may grow to include actually distributing content to YouTube and even other stores, we'll just focus on the job of validating, or checking the status of, song ISRCs with YouTube.
The child app responsible for getting ISRCs from the user and displaying the results of the communication with YouTube will be a shiny new Phoenix 1.3 app with a React + Redux front-end.
Now that we've decided on our application architecture, let's start building.
Scaffolding The Umbrella App
Generating our new umbrella app is easy:
$ mix new tc_distributor --umbrella
This generates an app with the following structure:
├── README.md
├── apps
├── config
│ └── config.exs
├── mix.exs
├── mix.lock
Recall that an umbrella application doesn't have any of its own source files, hence the relatively slim project structure.
Our umbrella app expects all of the child applications to be housed in the apps/
directory. The umbrella app will be made aware of its child apps via the following configuration in the mix.exs
file:
defmodule TcDistribution.Mixfile do
use Mix.Project
def project do
[apps_path: "apps",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
end
defp deps do
[]
end
end
Now we're ready to build our first child app, the Deliveries
Elixir application. Stay tuned for Part II in which we build a YouTube API Client with Elixir, and house that client in our Deliveries
child app! Remember you can subscribe below to get notified when the next post in this series goes live :)