Slide 1

Slide 1 text

DDD-rail Your Monorail Breaking up the Rails monolith with domain driven design

Slide 2

Slide 2 text

Hi, I’m Andrew

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Meet Delorean “It’s like Uber, for time travel!”

Slide 5

Slide 5 text

The situation

Slide 6

Slide 6 text

%

Slide 7

Slide 7 text

What happened?

Slide 8

Slide 8 text

In the beginning, there was an app.

Slide 9

Slide 9 text

The business decides… • Drivers deliver passengers from year A to year B. ⌛

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

The business decides… • Drivers deliver passengers from year A to year B. ⌛ • Passengers choose the type of Delorean service they want.

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

The business decides… • Drivers deliver passengers from year A to year B. ⌛ • Passengers choose the type of Delorean service they want. • Time travel carpools! 01

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

The business decides… • Drivers deliver passengers from year A to year B. ⌛ • Passengers choose the type of Delorean service they want. • Time travel carpools! 01 • DeloreanEATS: Customers order food, drivers pick up from time period and deliver to customer time period!

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Hm.

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Regressions

Slide 28

Slide 28 text

The Payments team regresses Trip while refactoring ServiceTier

Slide 29

Slide 29 text

The Restaurants team deploys a new pricing algorithm that regresses rideshare pricing

Slide 30

Slide 30 text

Responsibilities?

Slide 31

Slide 31 text

The Mobile team requests a new mobile login API, but which team should implement it?

Slide 32

Slide 32 text

Business won’t stop

Slide 33

Slide 33 text

Outsourced dev shop is rebuilding the marketing home page and needs a pricing API.

Slide 34

Slide 34 text

CEO wants to launch "Airbnb for time travel” feature in, let’s say, 2 months!

Slide 35

Slide 35 text

Does this sound familiar?

Slide 36

Slide 36 text

There are deeper insights to be had

Slide 37

Slide 37 text

What is DDD?

Slide 38

Slide 38 text

A set of techniques to arrive at a flexible design that cleanly maps to the business model

Slide 39

Slide 39 text

Strong, expressive domain models

Slide 40

Slide 40 text

There is no such thing as a One True Perfect model

Slide 41

Slide 41 text

Embrace the chaos

Slide 42

Slide 42 text

Domain-Driven Design: Eric Evans Implementing Domain- Driven Design: Vaughn Vernon

Slide 43

Slide 43 text

Let’s get designing!

Slide 44

Slide 44 text

Step 1: Visualize your domain models

Slide 45

Slide 45 text

Rails ERD https://github.com/voormedia/rails-erd

Slide 46

Slide 46 text

Ruby gem to generate UML diagrams from your ActiveRecord models

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

This helps you get the entire system into your mind.

Slide 49

Slide 49 text

Print it out! ✏

Slide 50

Slide 50 text

Step 2: Find your core- and sub-domains

Slide 51

Slide 51 text

Concept: Core Domain

Slide 52

Slide 52 text

What a business does

Slide 53

Slide 53 text

Transportation Core Domain

Slide 54

Slide 54 text

Concept: Subdomains

Slide 55

Slide 55 text

A supporting unit within the business

Slide 56

Slide 56 text

Rideshare Subdomain Food Delivery Subdomain Financial Transaction Subdomain Identity and Access Subdomain

Slide 57

Slide 57 text

Ridesharing Food Delivery Financial Transactions Identity/ Access

Slide 58

Slide 58 text

Step 3: Get the team talking in the same language

Slide 59

Slide 59 text

Concept: Ubiquitous Language

Slide 60

Slide 60 text

A defined language that is used consistently across business and technical contexts

Slide 61

Slide 61 text

Bring in the domain experts

Slide 62

Slide 62 text

A Driver picks up a Passenger

Slide 63

Slide 63 text

A Passenger requests a Pickup

Slide 64

Slide 64 text

An Owner drives a Vehicle

Slide 65

Slide 65 text

An Operator? drives a Vehicle

Slide 66

Slide 66 text

A User logs in and changes her Password

Slide 67

Slide 67 text

An Invoice is delivered to the Passenger

Slide 68

Slide 68 text

A Customer orders an item from a Menu, which is picked up and delivered by the Delivery Agent

Slide 69

Slide 69 text

Step 4: Make a glossary

Slide 70

Slide 70 text

Ridesharing • Passenger: “…” • Driver: “…” • Trip: “…” • Pickup: “…” • Vehicle: “…” • Vehicle Owner: “…”

Slide 71

Slide 71 text

Financial Transaction • Invoice: “…” • Order: “…” • Payment: “…” • Royalty: “…” • Salary: “…”

Slide 72

Slide 72 text

Identity and Access • User: “…” • Secure password: “…” • Role: “…”

Slide 73

Slide 73 text

Print ‘em out! ✏

Slide 74

Slide 74 text

Step 5: Draw out your software systems (bounded contexts)

Slide 75

Slide 75 text

Concept: Bounded Contexts

Slide 76

Slide 76 text

A software system that defines the applicability of a ubiquitous language

Slide 77

Slide 77 text

FoodDeliveryService TripRoutingEngine “menu” “trip” “restaurant” “plan” “delivery” “pickup” “customer date” “destination”

Slide 78

Slide 78 text

Software systems are natural boundaries for these linguistic terms

Slide 79

Slide 79 text

If a term leaks into a different software system/bounded context, you have a smell

Slide 80

Slide 80 text

Ridesharing Financial Transactions Identity/ Access Food Delivery DeloreanMonolith StripeAPI

Slide 81

Slide 81 text

DeloreanMonolith Ridesharing Financial Transactions Identity/ Access Stripe API Food Delivery

Slide 82

Slide 82 text

DeloreanMonolith Identity/ Access Food Delivery Driver Routing Service Fraud Detection Service (etc)

Slide 83

Slide 83 text

DeloreanMonolith Ridesharing Financial Transactions Identity/ Access Stripe API Food Delivery

Slide 84

Slide 84 text

Step 6: Now add directional dependencies

Slide 85

Slide 85 text

DeloreanMonolith Ridesharing Financial Transactions Identity/ Access Stripe API Food Delivery U D

Slide 86

Slide 86 text

DeloreanMonolith Identity/ Access Food Delivery Driver Routing Service Fraud Detection Service (etc) U U D U D U D

Slide 87

Slide 87 text

This helps you see dependencies between teams, where communication will be most important.

Slide 88

Slide 88 text

Context Map: A tool to visualize how your software systems relate to each other and the domain(s)

Slide 89

Slide 89 text

Our goal is to map our bounded contexts directly to our subdomains.

Slide 90

Slide 90 text

Ridesharing Financial Transactions Identity/ Access Stripe API Food Delivery

Slide 91

Slide 91 text

Show me the code!

Slide 92

Slide 92 text

Tactic 1: Get the names right

Slide 93

Slide 93 text

Adjust your class and method names to reflect the ubiquitous language

Slide 94

Slide 94 text

# old User.without_drivers # new Passenger.hailing_drivers # old order.calculate_cost # new order.calculate_billing_total

Slide 95

Slide 95 text

Tactic 2: Namespace and modulize your domains

Slide 96

Slide 96 text

Break Rails’ folder structure conventions

Slide 97

Slide 97 text

app/domains/financial app/domains/financial/inflation_adjustment.rb app/domains/financial/invoice.rb app/domains/food_delivery app/domains/food_delivery/menu.rb app/domains/food_delivery/menu_item.rb app/domains/food_delivery/menu_items_controller.rb app/domains/food_delivery/menus_controller.rb app/domains/identity app/domains/identity/user.rb app/domains/identity/users_controller.rb app/domains/rideshare app/domains/rideshare/driver.rb app/domains/rideshare/passenger.rb app/domains/rideshare/service_tier.rb

Slide 98

Slide 98 text

class Menu < ActiveRecord::Base belongs_to :restaurant end

Slide 99

Slide 99 text

module FoodDelivery class Menu < ActiveRecord::Base belongs_to :restaurant end end

Slide 100

Slide 100 text

class Trip < ActiveRecord::Base belongs_to :service_tier belongs_to :vehicle end

Slide 101

Slide 101 text

module Rideshare class Trip < ActiveRecord::Base belongs_to :service_tier belongs_to :vehicle end end

Slide 102

Slide 102 text

Domain code stays together

Slide 103

Slide 103 text

Tactic 3: Design with Aggregate Roots

Slide 104

Slide 104 text

Aggregate: A collection of domain objects that can be treated as a single unit

Slide 105

Slide 105 text

Use aggregates to model real-world entities that belong together

Slide 106

Slide 106 text

Aggregate Root: An entity at the “top” of the collection that can represent the whole

Slide 107

Slide 107 text

FoodDelivery aggregate root: Order

Slide 108

Slide 108 text

Food Delivery

Slide 109

Slide 109 text

A user adds a menu item to their cart

Slide 110

Slide 110 text

A User adds a MenuItem into their ShoppingCart

Slide 111

Slide 111 text

module Financial class ShoppingCart def order @order ||= FoodDelivery::Order.new end def amount @order.menu_items.sum(&:cost) + @order.menu_items.sum(&:tax_amount) end def add(item) order.menu_items << item end def remove(item) order.menu_items.delete(item); order.save end end end

Slide 112

Slide 112 text

module FoodDelivery class Order < ActiveRecord::Base belongs_to :user has_many :order_menu_items has_many :menu_items, through: :order_menu_items end end

Slide 113

Slide 113 text

module FoodDelivery class Order < ActiveRecord::Base belongs_to :user has_many :order_menu_items has_many :menu_items, through: :order_menu_items def total_cost item_cost + tax_cost end def item_cost menu_items.sum(&:cost) end def tax_cost menu_items.sum(&:tax_amount) end def add_item!(menu_item) menu_items << menu_item end def remove_item!(menu_item) menu_items.delete(menu_item); save end end end

Slide 114

Slide 114 text

module Financial class ShoppingCart def order @order ||= FoodDelivery::Order.new end def amount @order.total_cost end def add(item) order.add_item!(item) end def remove(item) order.remove_item!(item) end end end Calculation logic kept in FoodDelivery domain Implementation- agnostic

Slide 115

Slide 115 text

The root items of these aggregates are the only entities that external callers may fetch

Slide 116

Slide 116 text

Ridesharing Financial Transactions Identity/Access Food Delivery

Slide 117

Slide 117 text

Building good interfaces for a service oriented future!

Slide 118

Slide 118 text

Tactic 4: Break database joins between domains

Slide 119

Slide 119 text

`has_many`-itis!

Slide 120

Slide 120 text

module Rideshare class Trip < ActiveRecord::Base belongs_to :service_tier has_many :trip_pool_trips has_one :trip_pool, through: :trip_pool_trips belongs_to :driver, foreign_key: :driver_id, class_name: I belongs_to :passenger, foreign_key: :passenger_id, class_n belongs_to :vehicle belongs_to :order, class_name: FoodDelivery::Order has_one :payment end

Slide 121

Slide 121 text

These tend to happen in your God Objects

Slide 122

Slide 122 text

No content

Slide 123

Slide 123 text

module Rideshare class Trip < ActiveRecord::Base belongs_to :service_tier has_many :trip_pool_trips has_one :trip_pool, through: :trip_pool_trips belongs_to :driver, foreign_key: :driver_id, class_name: I belongs_to :passenger, foreign_key: :passenger_id, class_n belongs_to :vehicle belongs_to :order, class_name: FoodDelivery::Order has_one :payment end

Slide 124

Slide 124 text

module Rideshare class Trip < ActiveRecord::Base # belongs_to :order, class_name: FoodDelivery::Order def order FoodDeliveryAdapter.new.order_from_trip(self) end end end

Slide 125

Slide 125 text

module Rideshare class FoodDeliveryAdapter def order_from_trip(trip) FoodDelivery::Order.find_by(trip_id: trip.id) end end end

Slide 126

Slide 126 text

Decoupling domains now will ease your architectural transitions later

Slide 127

Slide 127 text

You can do this all in small steps!

Slide 128

Slide 128 text

Toward a distributed architecture

Slide 129

Slide 129 text

Namespaced, Isolated Modules to Rails Engines

Slide 130

Slide 130 text

Component-Based Rails Engines (Stephan Hagemann)

Slide 131

Slide 131 text

Rails engines to a remote (micro-)service

Slide 132

Slide 132 text

Good architecture goes a very long way.

Slide 133

Slide 133 text

To recap

Slide 134

Slide 134 text

Drew things on a wall

Slide 135

Slide 135 text

Came up with a language

Slide 136

Slide 136 text

Moved code around

Slide 137

Slide 137 text

Team is on the same page

Slide 138

Slide 138 text

Big idea: Your software speaks the same language as your domain experts

Slide 139

Slide 139 text

Big idea: Bounded contexts are separators for linguistic drivers. Keep your language consistent in one and only one system

Slide 140

Slide 140 text

Big idea: Domain code should live together

Slide 141

Slide 141 text

Big idea: Adapters between domains enforce domain purity

Slide 142

Slide 142 text

Big idea: Incremental changes

Slide 143

Slide 143 text

Thanks! Sample code: https://github.com/andrewhao/delorean Slides: https://github.com/andrewhao/dddrail-talk Twitter @andrewhao Github @andrewhao 8