I suspect that you made your life a bit more difficult because you insisted on placing the “top level application” in apps/primary
while you placed the included application in included_apps/secondary
. When I “ported”
https://github.com/francescoc/scalabilitywitherlangotp/tree/master/ch9/top_app
to Elixir/mix I simply created a regular umbrella project in which the top level application was in umbrella/apps/top_app
- side by side with the included app in umbrella/apps/bsc
- that way {:bsc, in_umbrella: true}
is sufficient inside the the top level applications dependencies:
# File: umbrella/apps/top_app/mix.exs
use Mix.Project
def project do
[
app: :top_app,
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],
included_applications: [:bsc], # added
start_phases: [{:start, [5]},{:admin, [4]}, {:stop, [3]}], # added
mod: {:application_starter, [TopApp.Application, []]} # modified
]
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"},
{:bsc, in_umbrella: true} # added
]
end
end
# File: umbrella/apps/top_app/lib/top_app/application.ex
# Based on
# https://github.com/francescoc/scalabilitywitherlangotp/blob/master/ch9/top_app/top_app.erl
#
# p.229 - "Designing for Scalability with Erlang/OTP Implement Robust, Fault-Tolerant Systems"
# by Francesco Cesarini and Steve Vinoski (O’Reilly).
# Copyright 2016 Francesco Cesarini and Stephen Vinoski, 978-1-449-32073-7.
# http://shop.oreilly.com/product/0636920024149.do
#
defmodule TopApp.Application do
use Application
def start(_type, _args),
do: {:ok, _pid} = BscSup.start_link()
def start_phase(phase, start_type, phase_args),
do: IO.puts "top_app:start_phase(#{inspect phase},#{inspect start_type},#{inspect phase_args})."
def stop(_state),
do: :ok # same as the default callback
end
# Additions modifications to mix.exs -> application
#
# included_applications: [:bsc],
# start_phases: [{:start, [5]},{:admin, [4]}, {:stop, [3]}],
# mod: {:application_starter, [TopApp.Application, []]}
#
# Additions modifications to mix.exs -> deps
#
# {:bsc, in_umbrella: true}
# File: umbrella/apps/bsc/mix.exs
defmodule Bsc.Mixfile do
use Mix.Project
def project do
[
app: :bsc,
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(),
description: "Base Station Controller" # added
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger],
mod: {Bsc.Application, []},
registered: [Bsc.Supervisor, FreqOverload, Frequency, SimplePhoneSup], # added
env: [my_key: :my_value], # added
start_phases: [{:init, [2]},{:admin, [1]}, {:oper, [0]}] # added
]
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
# File: umbrella/apps/bsc/lib/bsc/application.ex
# Based on
# https://github.com/francescoc/scalabilitywitherlangotp/blob/master/ch9/start_phases/bsc.erl
#
# p.227 - "Designing for Scalability with Erlang/OTP Implement Robust, Fault-Tolerant Systems"
# by Francesco Cesarini and Steve Vinoski (O’Reilly).
# Copyright 2016 Francesco Cesarini and Stephen Vinoski, 978-1-449-32073-7.
# http://shop.oreilly.com/product/0636920024149.do
#
defmodule Bsc.Application do
use Application
def start(_type, _args),
do: BscSup.start_link()
# p. 227 - mix.exs "application -> start_phases"
def start_phase(phase, start_type, phase_args),
do: IO.puts "bsc:start_phase(#{inspect phase},#{inspect start_type},#{inspect phase_args})."
def stop(_state),
do: :ok # same as the default callback
end
umbrella/apps/top_app$ iex -S mix
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
top_app:start_phase(:start,:normal,[5]).
top_app:start_phase(:admin,:normal,[4]).
bsc:start_phase(:admin,:normal,[1]).
top_app:start_phase(:stop,:normal,[3]).
Interactive Elixir (1.5.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
Now on a personal note I view the “primary app” as a conductor/orchestrator. For me “primary app + included apps” is the application in the conventional, non-OTP sense because “primary app + included apps” all exist within the same supervision tree - the primary app simply has the privilege/responsibility of controlling the life cycle of the supervision tree.