ddd-rail your monorail

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

Upload: andrew-hao

Post on 14-Apr-2017

425 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: DDD-rail Your Monorail

DDD-rail Your MonorailBreaking up the Rails monolith

with domain driven design

🚝💥🚀

Page 2: DDD-rail Your Monorail

Hi, I’m Andrew

Page 3: DDD-rail Your Monorail
Page 4: DDD-rail Your Monorail

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

🚗🚀

Page 5: DDD-rail Your Monorail

The situation

Page 6: DDD-rail Your Monorail

%💨😢😱😡

Page 7: DDD-rail Your Monorail

What happened?

Page 8: DDD-rail Your Monorail

In the beginning, there was an app.

Page 9: DDD-rail Your Monorail

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

year B. 🚕⌛

Page 10: DDD-rail Your Monorail
Page 11: DDD-rail Your Monorail
Page 12: DDD-rail Your Monorail

💰😂

Page 13: DDD-rail Your Monorail

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

year B. 🚕⌛ • Passengers choose the type of Delorean

service they want. 🚕 🚗 🚐

Page 14: DDD-rail Your Monorail
Page 15: DDD-rail Your Monorail
Page 16: DDD-rail Your Monorail

💰💰💰😂📰

Page 17: DDD-rail Your Monorail

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

Page 18: DDD-rail Your Monorail
Page 19: DDD-rail Your Monorail
Page 20: DDD-rail Your Monorail

💰💰💰💰💰😂📰

Page 21: DDD-rail Your Monorail

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! 🚕🍔

Page 22: DDD-rail Your Monorail
Page 23: DDD-rail Your Monorail
Page 24: DDD-rail Your Monorail

💩😐📰

Page 25: DDD-rail Your Monorail

Hm.

Page 26: DDD-rail Your Monorail
Page 27: DDD-rail Your Monorail

💣 Regressions

Page 28: DDD-rail Your Monorail

The Payments team regresses Trip while refactoring ServiceTier

Page 29: DDD-rail Your Monorail

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

Page 30: DDD-rail Your Monorail

💣 Responsibilities?

Page 31: DDD-rail Your Monorail

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

Page 32: DDD-rail Your Monorail

💣 Business won’t stop

Page 33: DDD-rail Your Monorail

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

Page 34: DDD-rail Your Monorail

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

Page 35: DDD-rail Your Monorail

Does this sound familiar?

Page 36: DDD-rail Your Monorail

There are deeper insights to be had

Page 37: DDD-rail Your Monorail

What is DDD?

Page 38: DDD-rail Your Monorail

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

to the business model

Page 39: DDD-rail Your Monorail

Strong, expressive domain models

Page 40: DDD-rail Your Monorail

There is no such thing as a One True Perfect model

Page 41: DDD-rail Your Monorail

Embrace the chaos

Page 43: DDD-rail Your Monorail

💥 Let’s get designing!

Page 44: DDD-rail Your Monorail

Step 1: Visualize your domain models

Page 45: DDD-rail Your Monorail

Rails ERDhttps://github.com/voormedia/rails-erd

Page 46: DDD-rail Your Monorail

Ruby gem to generate UML diagrams from your ActiveRecord models

Page 47: DDD-rail Your Monorail
Page 48: DDD-rail Your Monorail

This helps you get the entire system into your mind.

Page 49: DDD-rail Your Monorail

Print it out!✏📎

Page 50: DDD-rail Your Monorail

Step 2: Find your core- and sub-domains

Page 51: DDD-rail Your Monorail

Concept: Core Domain

Page 52: DDD-rail Your Monorail

What a business does

Page 53: DDD-rail Your Monorail

Transportation Core Domain

Page 54: DDD-rail Your Monorail

Concept: Subdomains

Page 55: DDD-rail Your Monorail

A supporting unit within the business

Page 56: DDD-rail Your Monorail

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

Page 57: DDD-rail Your Monorail

Ridesharing

Food Delivery

Financial Transactions

Identity/Access

Page 58: DDD-rail Your Monorail

Step 3: Get the team talking in the same language

Page 59: DDD-rail Your Monorail

Concept: Ubiquitous Language

Page 60: DDD-rail Your Monorail

A defined language that is used consistently across business and

technical contexts

Page 61: DDD-rail Your Monorail

Bring in the domain experts

Page 62: DDD-rail Your Monorail

A Driver picks up a Passenger

Page 63: DDD-rail Your Monorail

A Passenger requests a Pickup

Page 64: DDD-rail Your Monorail

An Owner drives a Vehicle

Page 65: DDD-rail Your Monorail

An Operator? drives a Vehicle

Page 66: DDD-rail Your Monorail

A User logs in and changes her Password

Page 67: DDD-rail Your Monorail

An Invoice is delivered to the Passenger

Page 68: DDD-rail Your Monorail

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

Delivery Agent

Page 69: DDD-rail Your Monorail

Step 4: Make a glossary

Page 70: DDD-rail Your Monorail

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

Page 71: DDD-rail Your Monorail

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

Page 72: DDD-rail Your Monorail

Identity and Access

• User: “…” • Secure password: “…” • Role: “…”

Page 73: DDD-rail Your Monorail

Print ‘em out!✏📎

Page 74: DDD-rail Your Monorail

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

Page 75: DDD-rail Your Monorail

Concept: Bounded Contexts

Page 76: DDD-rail Your Monorail

A software system that defines the applicability of a ubiquitous

language

Page 77: DDD-rail Your Monorail

FoodDeliveryService TripRoutingEngine

“menu” “trip”

“restaurant” “plan”

“delivery” “pickup”

“customer date” “destination”

Page 78: DDD-rail Your Monorail

Software systems are natural boundaries for these linguistic terms

Page 79: DDD-rail Your Monorail

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

Page 80: DDD-rail Your Monorail

RidesharingFinancial

TransactionsIdentity/Access

Food Delivery

DeloreanMonolith

StripeAPI

Page 81: DDD-rail Your Monorail

DeloreanMonolith

RidesharingFinancial

TransactionsIdentity/Access

Stripe API

Food Delivery

Page 82: DDD-rail Your Monorail

DeloreanMonolith

TransactionsIdentity/Access

Food Delivery

Driver Routing Service

Fraud Detection

Service (etc)

Page 83: DDD-rail Your Monorail

DeloreanMonolith

RidesharingFinancial

TransactionsIdentity/Access

Stripe API

Food Delivery

Page 84: DDD-rail Your Monorail

Step 6: Now add directional dependencies

Page 85: DDD-rail Your Monorail

DeloreanMonolith

RidesharingFinancial

TransactionsIdentity/Access

Stripe API

Food Delivery

U

D

Page 86: DDD-rail Your Monorail

DeloreanMonolith

TransactionsIdentity/Access

Food Delivery

Driver Routing Service

Fraud Detection

Service (etc)

U

UD

U

D U

D

Page 87: DDD-rail Your Monorail

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

important.

Page 88: DDD-rail Your Monorail

Context Map: A tool to visualize how your software systems relate to each other and

the domain(s)

Page 89: DDD-rail Your Monorail

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

Page 90: DDD-rail Your Monorail

RidesharingFinancial

TransactionsIdentity/Access

Stripe API

Food Delivery

Page 91: DDD-rail Your Monorail

Show me the code!

Page 92: DDD-rail Your Monorail

Tactic 1: Get the names right

Page 93: DDD-rail Your Monorail

Adjust your class and method names to reflect the ubiquitous language

Page 94: DDD-rail Your Monorail

# old User.without_drivers # new Passenger.hailing_drivers

# old order.calculate_cost # new order.calculate_billing_total

Page 95: DDD-rail Your Monorail

Tactic 2: Namespace and modulize your domains

Page 96: DDD-rail Your Monorail

Break Rails’ folder structure conventions

Page 97: DDD-rail Your Monorail

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

Page 98: DDD-rail Your Monorail

class Menu < ActiveRecord::Base belongs_to :restaurant end

Page 99: DDD-rail Your Monorail

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

Page 100: DDD-rail Your Monorail

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

Page 101: DDD-rail Your Monorail

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

Page 102: DDD-rail Your Monorail

Domain code stays together

Page 103: DDD-rail Your Monorail

Tactic 3: Design with Aggregate Roots

Page 104: DDD-rail Your Monorail

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

Page 105: DDD-rail Your Monorail

Use aggregates to model real-world entities that belong together

Page 106: DDD-rail Your Monorail

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

Page 107: DDD-rail Your Monorail

FoodDelivery aggregate root: Order

Page 108: DDD-rail Your Monorail

Food Delivery

Page 109: DDD-rail Your Monorail

A user adds a menu item to their cart

Page 110: DDD-rail Your Monorail

A User adds a MenuItem into their ShoppingCart

Page 111: DDD-rail Your Monorail

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

Page 112: DDD-rail Your Monorail

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

Page 113: DDD-rail Your Monorail

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

Page 114: DDD-rail Your Monorail

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

Page 115: DDD-rail Your Monorail

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

Page 116: DDD-rail Your Monorail

Ridesharing

Financial Transactions

Identity/Access

Food Delivery

Page 117: DDD-rail Your Monorail

Building good interfaces for a service oriented future!

Page 118: DDD-rail Your Monorail

Tactic 4: Break database joins between domains

Page 119: DDD-rail Your Monorail

`has_many`-itis!

Page 120: DDD-rail Your Monorail

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: Identity::User belongs_to :passenger, foreign_key: :passenger_id, class_name: Identity::User belongs_to :vehicle belongs_to :order, class_name: FoodDelivery::Order has_one :payment end

Page 121: DDD-rail Your Monorail

These tend to happen in your God Objects

Page 122: DDD-rail Your Monorail
Page 123: DDD-rail Your Monorail

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: Identity::User belongs_to :passenger, foreign_key: :passenger_id, class_name: Identity::User belongs_to :vehicle belongs_to :order, class_name: FoodDelivery::Order has_one :payment end

Page 124: DDD-rail Your Monorail

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

Page 125: DDD-rail Your Monorail

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

Page 126: DDD-rail Your Monorail

Decoupling domains now will ease your architectural transitions later

Page 127: DDD-rail Your Monorail

You can do this all in small steps!

Page 128: DDD-rail Your Monorail

Toward a distributed architecture

Page 129: DDD-rail Your Monorail

Namespaced, Isolated Modules to Rails Engines

Page 130: DDD-rail Your Monorail

Component-Based Rails Engines (Stephan Hagemann)

Page 131: DDD-rail Your Monorail

Rails engines to a remote (micro-)service

Page 132: DDD-rail Your Monorail

Good architecture goes a very long way.

Page 133: DDD-rail Your Monorail

To recap🚕 💨

Page 134: DDD-rail Your Monorail

Drew things on a wall

Page 135: DDD-rail Your Monorail

Came up with a language

Page 136: DDD-rail Your Monorail

Moved code around

Page 137: DDD-rail Your Monorail

Team is on the same page

Page 138: DDD-rail Your Monorail

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

experts

Page 139: DDD-rail Your Monorail

Big idea: Bounded contexts are separators for linguistic drivers. Keep your language

consistent in one and only one system

Page 140: DDD-rail Your Monorail

Big idea: Domain code should live together

Page 141: DDD-rail Your Monorail

Big idea: Adapters between domains enforce domain purity

Page 142: DDD-rail Your Monorail

Big idea: Incremental changes

Page 143: DDD-rail Your Monorail

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

Twitter @andrewhaoGithub @andrewhao

8