Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Using Your Umbrella – ElixirDaze

Using Your Umbrella – ElixirDaze

A look at what Applications and Umbrella Applications are in Elixir, as well as a deep dive into an approach for structuring your application as bounded contexts using Umbrella Apps.

9ffad8bbc282b748763697965f27b3c8?s=128

Chris Bell

March 03, 2017
Tweet

Transcript

  1. ELIXIRDAZE 2017 Using Your Umbrella Chris Bell • @cjbell_

  2. • twitter.com/cjbell_ • speakerdeck.com/cjbell88 • github.com/cjbell

  3. May 20th |> New York City empex.co • @empexco

  4. Using Your Umbrella The what, why, and how to use

    Umbrella apps
  5. Applications & Umbrella Applications in Elixir PART I (a brief

    introduction to)
  6. “In Elixir an application is a component implementing some specific

    functionality, that can be started and stopped as a unit, and which can be re-used in other systems.” Elixir Application Documentation
  7. defp deps do [{:ecto, “~> 2.0”}, {:phoenix, “~> 1.2”}, {:spotify_ex,

    “~> 2.0”}] end
  8. Applications give us a defined way to configure and run

    our Elixir code.
  9. defmodule MyApp do
 use Application def start(_, _) do import

    Supervisor.Spec, warn: false
 children = [worker(MyApp.Worker, [])] opts = [strategy: :one_for_one]
 Supervisor.start_link(children, opts) end end
  10. An umbrella application is a single project that is made

    up of one or more local applications.
  11. $ mix new my_app —umbrella

  12. my_app/
 ├── config/
 | ├── config.exs
 ├── apps/
 | mix.exs

  13. $ cd my_app/apps
 $ mix new sub_app —sup
 $ mix

    phoenix.new web_app
  14. my_app/
 ├── config/
 | ├── config.exs
 ├── apps/ | ├──

    web_app/ | ├── sub_app/
 | mix.exs
  15. $ mix test 
 # we can run this from

    the top level
 # (to run all tests in all apps)
 # or inside one of the apps
  16. $ iex -S mix
 # we can also run this

    from the top
 # level to start all the apps inside
  17. defmodule WebApp.MixFile do # rest of file omitted 
 defp

    deps do # declaring a dependency on an app under # your umbrella is easy [{:sub_app, in_umbrella: true}] end end
  18. We can make complex applications that are composed of small,

    isolated applications THE BIG IDEA:
  19. Designing Umbrella Applications PART II

  20. Lets steal some ideas from Domain Driven Design

  21. Separate your applications into specific bounded contexts

  22. “[Bounded Contexts are] a conceptual boundary where a domain model

    is applicable. It provides Ubiquitous Language that is spoken by the team and expressed in its carefully designed software model” Eric Evans: Domain Driven Design
  23. “[Bounded Contexts are] a conceptual boundary where a domain model

    is applicable. It provides Ubiquitous Language that is spoken by the team and expressed in its carefully designed software model” Eric Evans: Domain Driven Design Huh?
  24. Think about breaking your app into smaller pieces that have

    clear responsibilities and defined boundaries
  25. We want each application in our umbrella to focus on

    a single context
  26. Lets see an example

  27. Ecommerce App • Processes Sales • Ships Orders • Manages

    Customer support requests • Manages product catalog
  28. API Backoffice Sales Shipping Customer 
 Support Product 
 Catalog

    Ecommerce Project
  29. • Be tested in isolation • Be developed in isolation

    • Be deployed independently (if you wish) • Hides its complexity from other applications through a defined public interface Each application can:
  30. Yes, you can think of them as microservices… Read more:

    http://blog.plataformatec.com.br/ 2015/06/elixir-in-times-of-microservices/
  31. What about integrating with Ecto in our apps?

  32. API Backoffice Sales Shipping Customer 
 Support Product 
 Catalog

    Ecommerce Project
  33. We can introduce a single DB app that manages our

    migrations and has access to our database through an Ecto.Repo
  34. API Backoffice Sales Shipping Customer 
 Support Product 
 Catalog

    Ecommerce Domain DB
  35. defmodule DB.Repo do
 use Ecto.Repo, otp_app: :db
 end defmodule DB.Mixfile

    do
 defp deps do [{:ecto, “~> 2.1”}, {:postgrex, “~> 0.13”}] end
 end
  36. Sales Ecommerce Domain Customer Order LineItem DB Shipping Customer Order

    LineItem DB Repo
  37. Each application defines its own schemas which are essentially ‘value

    objects’ we can pass around
  38. defmodule Sales.Customer do
 use Ecto.Schema schema “customers” do
 # Relevant

    sales customer fields
 end
 end defmodule Shipping.Customer do
 use Ecto.Schema schema “customers” do
 # Relevant shipping customer fields
 end
 end
  39. defmodule Sales.Customer do
 use Ecto.Schema schema “customers” do
 # Relevant

    sales customer fields
 end
 end defmodule Shipping.Customer do
 use Ecto.Schema schema “customers” do
 # Relevant shipping customer fields
 end
 end But we’re duplicating our schemas!
  40. Remember: DRY is about avoiding duplication of logic not duplication

    of code.
  41. We call out to the DB app through entities /

    aggregates / services
  42. defmodule Sales.OrderManager do
 def complete_order(user, order, params) build_completion_changeset() |> DB.Repo.update()

    |> handle_result() end end
  43. And depend on it in our domain context applications

  44. defmodule Sales.Mixfile do def application do [mod: {Sales, []}, applications:

    [:db]] end 
 defp deps do [{:db, in_umbrella: true}] end
 end
  45. Could also have a Repo per application instead of DB

    as separate app. OTHER OPTIONS ARE AVAILABLE:
  46. What about Phoenix?

  47. Treat Phoenix as the web layer into your domain context(s)

    PHOENIX IS NOT YOUR APPLICATION
  48. defmodule API.V1.CheckoutController do plug :ensure_current_user
 plug :ensure_current_order 
 def create(conn,

    params) do user = get_current_user(conn) order = get_current_order(conn)
 Sales.OrderManager.complete_order(user, order, params)
 |> case do {:ok, order} -> render(conn, data: order) {:error, error} -> render_error(conn, 422, data: error) end end
 end
  49. Our web layer concerns itself with all things web (parsing

    params, reading headers for auth’d users etc)
  50. Phoenix 1.3 introduces generators to do this inside of your

    Phoenix app
  51. What does this give us?

  52. We have a dependency flow that only points inwards, meaning

    nothing on the inside knows about something on the outside
  53. API Sales DB DB Web Req

  54. “Code that is loosely coupled isn’t necessarily easy-to-delete, but it

    is much easier to replace, and much easier to change too.” @tef – ‘Write Code That Is Easy To Delete, Not Easy To Extend’
  55. • Clear separation of concerns • Your Phoenix app becomes

    very thin • Your application more clearly signals its features and its intent • Simple, unit testable business logic separate from your UI layer What you end up with:
  56. Of course, this is just one way of doing things

    and it’s not necessarily the right way™
  57. Umbrella applications in the wild PART III

  58. github.com/bitwalker/distillery Take a look at Distillery for deploying your Umbrella

    application. TIP
  59. release :api do set applications: [:api] end
 
 release :backoffice

    do set applications: [:backoffice, :another_app] end
 # distillery makes it easy!
 # read more at
 # https://hexdocs.pm/distillery/umbrella-projects.html
  60. Generate internal documentation using ex_doc for your apps TIP

  61. You don’t have to start out with an Umbrella app

    on day 1. Refactoring into separate applications is easy. TIP
  62. Circular app dependencies are not allowed. GOTCHA

  63. You can no longer run 
 mix ecto.gen.migration from the

    top level. GOTCHA
  64. Dependency conflicts can be tricky to resolve. You may have

    to use multiple overrides. GOTCHA
  65. Thank you for your time.
 Questions? cjbell1988@gmail.com