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

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.

Luís Ferreira

November 17, 2017
Tweet

More Decks by Luís Ferreira

Other Decks in Technology

Transcript

  1. 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 …
  2. * creating .gitignore * creating README.md * creating mix.exs *

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

    creating apps * creating config * creating config/config.exs
  4. The system is the asset. Code is a liability. “

    - Chad Fowler https://www.youtube.com/watch?v=-UKEPd2ipEk
  5. 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
  6. Sales (Supporting Subdomain) Shipping (Supporting Subdomain) Sales Context Shipping Context

    Manufacturing (Core Subdomain) Manufacturing Context CRM (external)
  7. Customer Address Domain Invariant There cannot be more than one

    customer per address Customer Aggregate Consistency and transactional boundary Aggregate Root
  8. 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 +
  9. 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
  10. Projection (read model) Command AG (Aggregate Root) Persists
 Event Publishes


    Event Message Bus Reads Event Persists Data Queries Event Store Database Bounded Context A
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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