Winter is coming

Winter is coming

Umbrella apps are a type of Elixir (and Erlang) application that allow you to have nested OTP applications as part of the same bigger ecosystem. You can think of them as a sort of microservice architecture in which the bulk of the infrastructure work is handled for you by the VM. It makes super nice and easy to handle inter dependencies, integration testing, different scaling needs, distribution, etc… A part of extracting multiple apps is thinking where to define the boundaries, that’s where DDD, bounded contexts and aggregates come into play, and even though they are not something that came out of the Elixir or Erlang communities is something that is being discussed a lot as a way to build more maintainable, scalable and adaptable application.

480bed58fce74c9a9dc3fe882a292aab?s=128

Luís Ferreira

November 17, 2017
Tweet

Transcript

  1. Winter is coming Luis Zamith

  2. Umbrella Apps

  3. Application Master Elixir.IEx.Supervisor Elixir.IEx.Config OTP Applications (the IEx example)

  4. Application Master Elixir.IEx.Supervisor Elixir.IEx.Config Umbrella APPS Application Master Elixir.Auth.Supervisor Elixir.Auth.Repo

    … Application Master Elixir.Phoenix.Supervisor Elixir.Phoenix.Code Reloader.Server …
  5. $ mix new shelf --umbrella

  6. * creating .gitignore * creating README.md * creating mix.exs *

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

    creating apps * creating config * creating config/config.exs
  8. MAINTAINABLE BUILDING A APPLICATION

  9. Large files

  10. Slower Pace

  11. High Coupling

  12. Low cohesion

  13. Easy to UNDERSTAND CHANGE Easy to

  14. The system is the asset. Code is a liability. “

    - Chad Fowler https://www.youtube.com/watch?v=-UKEPd2ipEk
  15. DOMAIN DRIVEN DESIGN

  16. Subdomains Sales (Supporting Subdomain) Support (Generic Subdomain)

  17. Sales (Supporting Subdomain) Support (Generic Subdomain) Sales Context Support Context

    Bounded Contexts
  18. Bounded Contexts

  19. None
  20. Total unification of the domain model for a large system

    will not be feasible or cost-effective “ - Eric Evans Domain-Driven Design: Tackling Complexity in the Heart of Software
  21. Sales (Supporting Subdomain) Shipping (Supporting Subdomain) Sales Context Shipping Context

    Manufacturing (Core Subdomain) Manufacturing Context CRM (external)
  22. Sales (Supporting Subdomain) Shipping (Supporting Subdomain) Manufacturing (Core Subdomain) App

    Context CRM (external)
  23. Customer Address Product Territory Sales Person Sales Bounded Context

  24. Customer Address Domain Invariant There cannot be more than one

    customer per address Customer Aggregate Consistency and transactional boundary Aggregate Root
  25. Big Design Up Front

  26. CQRS

  27. CQS If you have a return value you cannot mutate

    state. If you mutate state your return type must be void CQRS CQS different model by responsibility +
  28. Client Data Command Model Query Model App create persist find

    read
  29. It is not possible to create an optimal solution for

    searching, reporting, and processing transactions utilizing a single model. “ - Greg Young https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf
  30. Event Sourcing

  31. Projection (read model) Command AG (Aggregate Root) Persists
 Event Publishes


    Event Message Bus Reads Event Persists Data Queries Event Store Database Bounded Context A
  32. Umbrella Apps

  33. https://github.com/commanded/commanded

  34. Shipping.ship_order(%{…}) Sales.place_order(%{…}) %PlaceOrder{…} Router Order (Aggregate Root) %PlaceOrder{…} EventHandler %OrderPlaced{…}

    Sales context EventHandler %ShipOrder{…} Router Shipment (Aggregate Root) %ShipOrder{…} EventHandler %ShippedOrder{…} Shipping context EventHandler
  35. defmodule Sales do alias Acme.Sales.Router alias Acme.Sales.Commands.{PlaceOrder} alias Acme.Sales.Projections.{PlacedOrders} def

    place_order(%{total_price_cents: total_price_cents, buyer_uuid: buyer_uuid}) do uuid = UUID.uuid4() %PlaceOrder{uuid: uuid, total_price_cents: total_price_cents, buyer_uuid: buyer_uuid} |> Router.dispatch() end def list_orders do PlacedOrders.list end end Public API
  36. defmodule Acme.Sales.Router do alias Acme.Sales.Orders.Order alias Acme.Sales.Commands.{ PlaceOrder, } use

    Commanded.Commands.Router dispatch [ PlaceOrder, ], to: Order, identity: :uuid end Router
  37. defmodule Acme.Sales.Router do alias Acme.Sales.Orders.Order alias Acme.Sales.Commands.{ PlaceOrder, } use

    Commanded.Commands.Router dispatch [ PlaceOrder, ], to: Order, identity: :uuid end Router
  38. defmodule Acme.Sales.Orders.Order do alias Acme.Sales.Orders.Order alias Acme.Sales.Commands.{PlaceOrder} alias Acme.Events.{OrderPlaced} defstruct

    [:uuid, :skus, :total_price_cents, :buyer_uuid] def execute(%Order{uuid: nil}, %PlaceOrder{…}) do %OrderPlaced{…} end def apply(%Order{} = state, %OrderPlaced{…}) do %Order{state | uuid: uuid, …} end end Aggregate Root
  39. defmodule Acme.Sales.Projections.PlacedOrders do defmodule Handler do … use Commanded.Event.Handler, name:

    "sales_placed_orders", start_from: :origin def handle(%OrderPlaced{uuid: uuid} = event, _meta) do persist_order(event) :ok end end end Event Handler / Projection
  40. Conclusion Building maintainable apps that scale is not easy Communication

    with domain experts keeps the abstractions in check (aka I’m finally done) Straight-forward MVC is flawed
  41. Homework Think about the subdomains and bounded contexts for the

    app you’re currently working on and draw a diagram. Do you think it could be improved with these techniques? Bonus: Implement it with Umbrella Apps and Commanded
  42. @zamith news.zamith.pt

  43. None
  44. Winter is coming Luis Zamith