Skip to content

Umbrella Application Structure

Gargi Balasubramaniam edited this page May 23, 2018 · 3 revisions

An elixir project may contain several sub-applications units interacting with each other.These which are big enough to be treated as small independent working applications in themselves. In such a case, it makes semantic sense to have an 'umbrella' structure which consists of several 'sub apps' which are in essence independent elixir applications in themselves ,with their own configurations and tests. These interact with each other and come together to form the main application.

Elixir provides an option of creating umbrella application structure. Such applications don't have source or test files in the main directory. We use the running example of input_parser and multiplier and demonstrate the application structure here-

  1. Creating the project

mix new sample --umbrella

* creating .gitignore
* creating README.md
* creating mix.exs
* creating apps
* creating config
* creating config/config.exs

Your umbrella project was created successfully.
Inside your project, you will find an apps/ directory
where you can create and host many apps:

    cd sample
    cd apps
    mix new my_app

Commands like "mix compile" and "mix test" when executed
in the umbrella project root will automatically run
for each application in the apps/ directory.

Carefully observe the structure and the folders created.

  1. Now, we move on to adding our 'sub application' units. The first one is input_parser.We proceed as-

cd apps

mix new input_parser

* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/input_parser.ex
* creating test
* creating test/test_helper.exs
* creating test/input_parser_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd input_parser
    mix test

Run "mix help" for more commands.

Observe that this works like the creation of any normal mix project.

Now, open input_parser/mix.exs. It looks like-

defmodule InputParser.Mixfile do
  use Mix.Project

  def project do
    [
      app: :input_parser,
      version: "0.1.0",
      build_path: "../../_build",
      config_path: "../../config/config.exs",
      deps_path: "../../deps",
      lockfile: "../../mix.lock",
      elixir: "~> 1.5",
      start_permanent: Mix.env == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
      # {:sibling_app_in_umbrella, in_umbrella: true},
    ]
  end
end

It is worth noting:

build_path: "../../_build",
      config_path: "../../config/config.exs",
      deps_path: "../../deps",
      lockfile: "../../mix.lock",

in the file, which signifies that all dependencies will be checked out to sample/deps, and they will share the same build, config and lock files. This ensures dependencies will be fetched and compiled once for the whole umbrella structure, instead of once per umbrella application.

  1. Add the source code as done previously.(Build With Mix ) Our directory structure for input_parser would now look like-
.
├── config
│   ├── config.exs
│   ├── debug.exs
│   ├── dev.exs
│   └── test.exs
├── lib
│   ├── input_parser
│   │   └── application.ex
│   ├── input_parser.ex
│   └── mix
│       └── tasks
│           ├── debug.ex
│           └── parse.ex
├── log
│   └── debug.log
├── mix.exs
├── queries.txt
├── README.md
├── script
│   ├── debug.sh
│   └── normal_parse.sh
└── test
    ├── input_parser_test.exs
    └── test_helper.exs

8 directories, 16 files
  1. In a similar fashion, create the multiplier application and add the source code. The resulting directory structure would look like-
.
├── config
│   ├── config.exs
│   ├── debug.exs
│   ├── dev.exs
│   └── test.exs
├── lib
│   ├── mix
│   │   └── tasks
│   │       └── multiply.ex
│   ├── multiplier
│   │   └── application.ex
│   └── multiplier.ex
├── mix.exs
├── README.md
├── script
│   └── multiplying.sh
└── test
    ├── multiplier_test.exs
    └── test_helper.exs

7 directories, 12 files
  1. Now that our sub applications are ready, let us look at the final directory structure of our umbrella application i.e. sample
.
├── apps
│   ├── input_parser
│   │   ├── config
│   │   │   ├── config.exs
│   │   │   ├── debug.exs
│   │   │   ├── dev.exs
│   │   │   └── test.exs
│   │   ├── lib
│   │   │   ├── input_parser
│   │   │   │   └── application.ex
│   │   │   ├── input_parser.ex
│   │   │   └── mix
│   │   │       └── tasks
│   │   │           ├── debug.ex
│   │   │           └── parse.ex
│   │   ├── log
│   │   │   └── debug.log
│   │   ├── mix.exs
│   │   ├── queries.txt
│   │   ├── README.md
│   │   ├── script
│   │   │   ├── debug.sh
│   │   │   └── normal_parse.sh
│   │   └── test
│   │       ├── input_parser_test.exs
│   │       └── test_helper.exs
│   └── multiplier
│       ├── config
│       │   ├── config.exs
│       │   ├── debug.exs
│       │   ├── dev.exs
│       │   └── test.exs
│       ├── lib
│       │   ├── mix
│       │   │   └── tasks
│       │   │       └── multiply.ex
│       │   ├── multiplier
│       │   │   └── application.ex
│       │   └── multiplier.ex
│       ├── mix.exs
│       ├── README.md
│       ├── script
│       │   └── multiplying.sh
│       └── test
│           ├── multiplier_test.exs
│           └── test_helper.exs
├── _build
│   ├── debug
│   │   ├── consolidated
│   │   │   ├── Elixir.Collectable.beam
│   │   │   ├── Elixir.Enumerable.beam
│   │   │   ├── Elixir.IEx.Info.beam
│   │   │   ├── Elixir.Inspect.beam
│   │   │   ├── Elixir.List.Chars.beam
│   │   │   └── Elixir.String.Chars.beam
│   │   └── lib
│   │       ├── input_parser
│   │       │   ├── consolidated
│   │       │   │   ├── Elixir.Collectable.beam
│   │       │   │   ├── Elixir.Enumerable.beam
│   │       │   │   ├── Elixir.IEx.Info.beam
│   │       │   │   ├── Elixir.Inspect.beam
│   │       │   │   ├── Elixir.List.Chars.beam
│   │       │   │   └── Elixir.String.Chars.beam
│   │       │   └── ebin
│   │       │       ├── Elixir.InputParser.Application.beam
│   │       │       ├── Elixir.InputParser.beam
│   │       │       ├── Elixir.Mix.Tasks.Debug.beam
│   │       │       ├── Elixir.Mix.Tasks.Parse.beam
│   │       │       └── input_parser.app
│   │       ├── logger_file_backend
│   │       │   └── ebin
│   │       │       ├── Elixir.LoggerFileBackend.beam
│   │       │       └── logger_file_backend.app
│   │       └── multiplier
│   │           └── ebin
│   │               ├── Elixir.Mix.Tasks.Multiply.beam
│   │               ├── Elixir.Multiplier.Application.beam
│   │               ├── Elixir.Multiplier.beam
│   │               └── multiplier.app
│   ├── dev
│   │   ├── consolidated
│   │   │   ├── Elixir.Collectable.beam
│   │   │   ├── Elixir.Enumerable.beam
│   │   │   ├── Elixir.IEx.Info.beam
│   │   │   ├── Elixir.Inspect.beam
│   │   │   ├── Elixir.List.Chars.beam
│   │   │   └── Elixir.String.Chars.beam
│   │   └── lib
│   │       ├── input_parser
│   │       │   ├── consolidated
│   │       │   │   ├── Elixir.Collectable.beam
│   │       │   │   ├── Elixir.Enumerable.beam
│   │       │   │   ├── Elixir.IEx.Info.beam
│   │       │   │   ├── Elixir.Inspect.beam
│   │       │   │   ├── Elixir.List.Chars.beam
│   │       │   │   └── Elixir.String.Chars.beam
│   │       │   └── ebin
│   │       │       ├── Elixir.InputParser.Application.beam
│   │       │       ├── Elixir.InputParser.beam
│   │       │       ├── Elixir.Mix.Tasks.Debug.beam
│   │       │       ├── Elixir.Mix.Tasks.Parse.beam
│   │       │       └── input_parser.app
│   │       ├── logger_file_backend
│   │       │   └── ebin
│   │       │       ├── Elixir.LoggerFileBackend.beam
│   │       │       └── logger_file_backend.app
│   │       └── multiplier
│   │           └── ebin
│   │               ├── Elixir.Mix.Tasks.Multiply.beam
│   │               ├── Elixir.Multiplier.Application.beam
│   │               ├── Elixir.Multiplier.beam
│   │               └── multiplier.app
│   └── test
│       ├── consolidated
│       │   ├── Elixir.Collectable.beam
│       │   ├── Elixir.Enumerable.beam
│       │   ├── Elixir.IEx.Info.beam
│       │   ├── Elixir.Inspect.beam
│       │   ├── Elixir.List.Chars.beam
│       │   └── Elixir.String.Chars.beam
│       └── lib
│           ├── input_parser
│           │   └── ebin
│           │       ├── Elixir.InputParser.Application.beam
│           │       ├── Elixir.InputParser.beam
│           │       ├── Elixir.Mix.Tasks.Debug.beam
│           │       ├── Elixir.Mix.Tasks.Parse.beam
│           │       └── input_parser.app
│           ├── logger_file_backend
│           │   └── ebin
│           │       ├── Elixir.LoggerFileBackend.beam
│           │       └── logger_file_backend.app
│           └── multiplier
│               └── ebin
│                   ├── Elixir.Mix.Tasks.Coveralls.beam
│                   ├── Elixir.Mix.Tasks.Multiply.beam
│                   ├── Elixir.Multiplier.Application.beam
│                   ├── Elixir.Multiplier.beam
│                   └── multiplier.app
├── config
│   ├── config.exs
│   └── test.exs
├── deps
│   └── logger_file_backend
│       ├── hex_metadata.config
│       ├── lib
│       │   └── logger_file_backend.ex
│       ├── LICENSE
│       ├── mix.exs
│       └── README.md
├── mix.exs
├── mix.lock
├── queries.txt
├── README.md
├── results.txt
└── script
    └── mul.sh

53 directories, 105 files
  1. Closely observer the sample/config.exs file-
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

# By default, the umbrella project as well as each child
# application will require this configuration file, ensuring
# they all use the same configuration. While one could
# configure all applications here, we prefer to delegate
# back to each application for organization purposes.
import_config "../apps/*/config/config.exs"

# Sample configuration (overrides the imported configuration above):
#
#     config :logger, :console,
#       level: :info,
#       format: "$date $time [$level] $metadata$message\n",
#       metadata: [:user_id]

This shows that by default, individual sub application configurations are imported. We can also chose to have a centralised config for all the sub applications by commenting out import_config "../apps/*/config/config.exs".

8.Dependencies in an umbrella project while testing individual applications must be mentioned explicitly. We know that multiplier depends on input_parser. Hence, we add the following line to the deps/0 of apps/multiplier/mix.exs-

{:input_parser, in_umbrella: true}

9.For running the application, we go back to the main folder i.e. sample and execute the task mix multiply which we earlier defined for multiplier.Set the MIX_DEBUG=1 for greater detail-

$ MIX_DEBUG=1 mix multiply
** Running mix loadconfig (inside Sample.Mixfile)
Loading from config/config.exs in input parser
In dev.exs of input parser
Loading from config.exs in multiplier
In dev.exs of multiplier
** Running mix multiply (inside Sample.Mixfile)
** Running mix deps.loadpaths (inside Sample.Mixfile)
** Running mix app.start (inside Sample.Mixfile)
** Running mix loadpaths (inside Sample.Mixfile)
** Running mix archive.check (inside Sample.Mixfile)
** Running mix compile (inside Sample.Mixfile)
** Running mix compile.all (inside Sample.Mixfile)
==> input_parser
** Running mix compile.all (inside InputParser.Mixfile)
** Running mix compile.yecc (inside InputParser.Mixfile)
** Running mix compile.leex (inside InputParser.Mixfile)
** Running mix compile.erlang (inside InputParser.Mixfile)
** Running mix compile.elixir (inside InputParser.Mixfile)
** Running mix compile.xref (inside InputParser.Mixfile)
** Running mix compile.app (inside InputParser.Mixfile)
==> multiplier
** Running mix compile.all (inside Multiplier.Mixfile)
** Running mix compile.yecc (inside Multiplier.Mixfile)
** Running mix compile.leex (inside Multiplier.Mixfile)
** Running mix compile.erlang (inside Multiplier.Mixfile)
** Running mix compile.elixir (inside Multiplier.Mixfile)
** Running mix compile.xref (inside Multiplier.Mixfile)
** Running mix compile.app (inside Multiplier.Mixfile)
Starting application in InputParser.Application(lib/input_parser/application.ex)
Input Parser Initialised and Queries are Obtained
Starting application in Multiplier.Application(lib/multiplier/application.ex)
Multiplier initialised
Multiplying....
Done calculating,please open results.txt
Clone this wiki locally