Slide 1

Slide 1 text

Winter is coming Luis Zamith

Slide 2

Slide 2 text

Umbrella Apps

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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 …

Slide 5

Slide 5 text

$ mix new shelf --umbrella

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

MAINTAINABLE BUILDING A APPLICATION

Slide 9

Slide 9 text

Large files

Slide 10

Slide 10 text

Slower Pace

Slide 11

Slide 11 text

High Coupling

Slide 12

Slide 12 text

Low cohesion

Slide 13

Slide 13 text

Easy to UNDERSTAND CHANGE Easy to

Slide 14

Slide 14 text

The system is the asset. Code is a liability. “ - Chad Fowler https://www.youtube.com/watch?v=-UKEPd2ipEk

Slide 15

Slide 15 text

DOMAIN DRIVEN DESIGN

Slide 16

Slide 16 text

Subdomains Sales (Supporting Subdomain) Support (Generic Subdomain)

Slide 17

Slide 17 text

Sales (Supporting Subdomain) Support (Generic Subdomain) Sales Context Support Context Bounded Contexts

Slide 18

Slide 18 text

Bounded Contexts

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Sales (Supporting Subdomain) Shipping (Supporting Subdomain) Sales Context Shipping Context Manufacturing (Core Subdomain) Manufacturing Context CRM (external)

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Customer Address Product Territory Sales Person Sales Bounded Context

Slide 24

Slide 24 text

Customer Address Domain Invariant There cannot be more than one customer per address Customer Aggregate Consistency and transactional boundary Aggregate Root

Slide 25

Slide 25 text

Big Design Up Front

Slide 26

Slide 26 text

CQRS

Slide 27

Slide 27 text

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 +

Slide 28

Slide 28 text

Client Data Command Model Query Model App create persist find read

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Event Sourcing

Slide 31

Slide 31 text

Projection (read model) Command AG (Aggregate Root) Persists
 Event Publishes
 Event Message Bus Reads Event Persists Data Queries Event Store Database Bounded Context A

Slide 32

Slide 32 text

Umbrella Apps

Slide 33

Slide 33 text

https://github.com/commanded/commanded

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

@zamith news.zamith.pt

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

Winter is coming Luis Zamith