Slide 1

Slide 1 text

Highly Cohesive, Loosely Coupled (& Very Awesome) A domain-driven approach to beautiful systems

Slide 2

Slide 2 text

Let's imagine tonight We work at a hot new startup: Delorean. The Uber for time-travel!

Slide 3

Slide 3 text

OMG it's making so much money! We're releasing features right and left!

Slide 4

Slide 4 text

But the code is a mess! As systems grow, they naturally want to fall into disarray...

Slide 5

Slide 5 text

The evolution of a feature Feature: As a passenger, I want to hail a Delorean So I can travel in time!

Slide 6

Slide 6 text

class TripsController def create passenger = Passenger.find(params[:passenger_id]) driver = Driver.where( available: true, longitude: params[:longitude], latitude: params[:latitude] ).first driver.send_to!(passenger) end end

Slide 7

Slide 7 text

The evolution of a feature Feature: As a passenger, I want to hail a Delorean So I can travel in time! ...and my credit card will be charged

Slide 8

Slide 8 text

class TripsController def create passenger = Passenger.find(params[:passenger_id]) driver = Driver.where( available: true, longitude: params[:longitude], latitude: params[:latitude] ).first BraintreeService.charge(passenger) driver.send_to!(passenger) end end

Slide 9

Slide 9 text

The evolution of a feature Feature: As a passenger, I want to hail a Delorean So I can travel in time! ...and my credit card will be charged ...and the system should log the event to Google Analytics

Slide 10

Slide 10 text

class TripsController def create passenger = Passenger.find(params[:passenger_id]) driver = Driver.where( available: true, longitude: params[:longitude], latitude: params[:latitude] ).first BraintreeService.charge(passenger) AnalyticsService.log_ride_created! driver.send_to!(passenger) end end

Slide 11

Slide 11 text

The evolution of a feature Feature: As a passenger, I want to hail a Delorean So I can travel in time! ...and my credit card will be charged ...and the system should log the event to Google Analytics ...and we should totally also do food delivery

Slide 12

Slide 12 text

class TripsController def create passenger = Passenger.find(params[:passenger_id]) is_food = params[:ride_type] == 'food' driver = Driver.where( can_food_delivery: is_food, # ... ).first restaurant = Restaurant.find_by(meal_type: params[:m BraintreeService.charge(passenger) AnalyticsService.log_ride_created! driver.itinerary.add(restaurant) driver.itinerary.add(passenger) end end

Slide 13

Slide 13 text

Code clutter in Rails As the monolith grows, feature code is scattered across the app. app/ controllers/trips_controller.rb models/trip.rb helpers/trip_helper.rb services/calculate_trip_cost.rb

Slide 14

Slide 14 text

Code clutter in Rails As the monolith grows, feature code is scattered across the app. app/ controllers/restaurants_controller.rb models/restaurant.rb models/meal.rb helpers/restaurant_helper.rb services/calculate_meal_cost.rb

Slide 15

Slide 15 text

Code clutter in Rails As the monolith grows, feature code is scattered across the app. app/ controllers/puppy_deliveries_controller.rb models/puppy_delivery.rb models/animal_shelter.rb

Slide 16

Slide 16 text

So here we are... The code is tangled & dif cult to change Regressions are common Features take forever to build & release

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Hi, I'm Andrew Friendly neighborhood programmer at Carbon Five

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

Beautiful systems

Slide 21

Slide 21 text

Beautiful systems are highly cohesive

Slide 22

Slide 22 text

Beautiful systems are loosely coupled

Slide 23

Slide 23 text

Highly cohesive Elements of a module are strongly related to each other Near each other, are easily accessible.

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Loosely coupled Modules minimize their dependencies so that they are easily modi able

Slide 26

Slide 26 text

Loosely coupled Modules minimize their dependencies so that they are easily modi able Can evolve independently of other modules in the system

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

Introducing Domain-Driven Design DDD is both a set of high-level design activities and speci c software patterns

Slide 29

Slide 29 text

Our goals tonight Visualize our system from a domain perspective.

Slide 30

Slide 30 text

Our goals tonight Visualize our system from a domain perspective. ✏ Learn insights to draw boundaries in our code!

Slide 31

Slide 31 text

Our goals tonight Visualize our system from a domain perspective. ✏ Learn insights to draw boundaries in our code! Do a little bit of refactoring.

Slide 32

Slide 32 text

Strategic Design Through an exercise called Context Mapping

Slide 33

Slide 33 text

Apply It! ⚡ Step 1: Visualize Your System Let's generate an ERD diagram! I like to generate mine with a gem like railroady or rails-erd If you have multiple systems, do this for each system.

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

Yikes.

Slide 36

Slide 36 text

Core domain The Core Domain is the thing that your business does that makes it unique.

Slide 37

Slide 37 text

Core domain The Core Domain is the thing that your business does that makes it unique. Delorean Core Domain: Transportation

Slide 38

Slide 38 text

Supporting domains A Supporting Domain (or Subdomain) are the areas of the business that play roles in making the Core Domain happen.

Slide 39

Slide 39 text

Supporting domains A Supporting Domain (or Subdomain) are the areas of the business that play roles in making the Core Domain happen. Delorean Supporting Domains: Driver Routing (route me from X to Y)

Slide 40

Slide 40 text

Supporting domains A Supporting Domain (or Subdomain) are the areas of the business that play roles in making the Core Domain happen. Delorean Supporting Domains: Driver Routing (route me from X to Y) Noti cations (push noti cations)

Slide 41

Slide 41 text

Supporting domains A Supporting Domain (or Subdomain) are the areas of the business that play roles in making the Core Domain happen. Delorean Supporting Domains: Driver Routing (route me from X to Y) Noti cations (push noti cations) Financial Transactions (charge the card)

Slide 42

Slide 42 text

Supporting domains A Supporting Domain (or Subdomain) are the areas of the business that play roles in making the Core Domain happen. Delorean Supporting Domains: Driver Routing (route me from X to Y) Noti cations (push noti cations) Financial Transactions (charge the card) Product Analytics (track business metrics)

Slide 43

Slide 43 text

Supporting domains A Supporting Domain (or Subdomain) are the areas of the business that play roles in making the Core Domain happen. Delorean Supporting Domains: Driver Routing (route me from X to Y) Noti cations (push noti cations) Financial Transactions (charge the card) Product Analytics (track business metrics) Customer Support (keep people happy)

Slide 44

Slide 44 text

Apply It! ⚡ Step 2: Draw domains on your diagram Overlay your domains on top of the ERD diagram You might discover some domains you never even thought you had!

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

Bounded Contexts A Bounded Context is: Concretely: a software system (like a codebase) Linguistically: a delineation in your domain where concepts are "bounded", or contained

Slide 48

Slide 48 text

Apply It! ⚡ Step 3: Overlay your bounded contexts Next up - with a different color pen or marker, draw lines around system boundaries / bounded contexts.

Slide 49

Slide 49 text

Apply It! ⚡ Step 3: Overlay your bounded contexts Next up - with a different color pen or marker, draw lines around system boundaries / bounded contexts. You may also nd other system boundaries like: External cloud providers Other teams' services or systems

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Congrats! You just made a Context Map! A Context Map gives us a place to see the current system as-is (the problem space), the strategic domains, and their dependencies.

Slide 54

Slide 54 text

Making sense of the Context Map We may notice a few things:

Slide 55

Slide 55 text

Making sense of the Context Map We may notice a few things: One bounded context contains multiple sub-(supporting) domains

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

Making sense of the Context Map We may notice a few things: One bounded context contains multiple sub-(supporting) domains Multiple bounded contexts are required to support a single domain

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

An Ideal Architecture Each Domain should have its own Bounded Context Key concept in DDD!

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

Increased cohesion! We just found the areas where code "naturally" ts together, because they are serving the same business goal.

Slide 62

Slide 62 text

Apply It! ⚡ Break your application into domain modules Incremental refactoring, using Ruby Modules to lead the way!

Slide 63

Slide 63 text

class Trip < ActiveRecord::Base belongs_to :vehicle belongs_to :passenger belongs_to :driver end class TripsController < ApplicationController # ... end

Slide 64

Slide 64 text

module Ridesharing class Trip < ActiveRecord::Base belongs_to :vehicle belongs_to :passenger belongs_to :driver end end module Ridesharing class TripsController < ApplicationController # ... end end

Slide 65

Slide 65 text

Find references to newly modulized classes and change them.

Slide 66

Slide 66 text

# config/routes.rb resources :trips

Slide 67

Slide 67 text

# config/routes.rb namespace :ridesharing, path: '/' do resources :trips end

Slide 68

Slide 68 text

class Invoice belongs_to :trip end

Slide 69

Slide 69 text

class Invoice belongs_to :trip, class_name: Ridesharing::Trip end

Slide 70

Slide 70 text

Creating domain-oriented folders app/domains/ridesharing/trip.rb app/domains/ridesharing/service_tier.rb app/domains/ridesharing/vehicle.rb app/domains/ridesharing/trips_controller.rb app/domains/ridesharing/trips/show.html.erb

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

Hence: modulizing increases cohesion Let's move on to coupling...

Slide 73

Slide 73 text

ActiveRecord relationships can be abused! Objects start knowing too much about the entire world. "God Objects"

Slide 74

Slide 74 text

class PaymentConfirmation belongs_to :trip, class_name: Ridesharing::Trip belongs_to :passenger, class_name: Ridesharing::Passen belongs_to :credit_card has_many :menu_items belongs_to :coupon_code has_one :retriable_email_job # ad infinitum... end

Slide 75

Slide 75 text

No content

Slide 76

Slide 76 text

Aggregate Roots Aggregate Roots are top-level domain models that reveal an object graph of related entities beneath them.

Slide 77

Slide 77 text

Aggregate Roots Aggregate Roots are top-level domain models that reveal an object graph of related entities beneath them. Can be considered a Facade

Slide 78

Slide 78 text

No content

Slide 79

Slide 79 text

Decrease coupling by only exposing aggregate roots Make it a rule in your system that you may only access another domain's Aggregate Root.

Slide 80

Slide 80 text

Decrease coupling by only exposing aggregate roots Make it a rule in your system that you may only access another domain's Aggregate Root. Internally, it's OK to reach for whatever you need.

Slide 81

Slide 81 text

Make service objects that provide Aggregate Roots Your source domain can provide a service (Adapter) that returns the Aggregate Root

Slide 82

Slide 82 text

module Ridesharing class FetchTrip def call(id) Trip .includes(:passenger, :trip, ...) .find(id) # Alternatively, return something custom # OpenStruct.new(trip: Trip.find(id), ...) end end end

Slide 83

Slide 83 text

class PaymentConfirmation belongs_to :trip, class_name: Ridesharing::Trip belongs_to :passenger, class_name: Ridesharing::Passen # ... end

Slide 84

Slide 84 text

class PaymentConfirmation def trip # Returns the Trip aggregate root Ridesharing::FetchTrip.new.find(payment_id) end end # OLD: payment_confirmation.passenger # NEW: payment_confirmation.trip.passenger

Slide 85

Slide 85 text

Decrease coupling by publishing events for async dependencies Domains that only need unidirectional data ow work well here!

Slide 86

Slide 86 text

Just send an event notifying the outside world! Instead of needing to know about the outside world, we simply publish an event.

Slide 87

Slide 87 text

# Old way class TripController def create # ... ReallySpecificGoogleAnalyticsThing .tag_manager_logging('trip_created', ENV['GA_ID'], trip) end end

Slide 88

Slide 88 text

class TripController def create # ... EventPublisher.publish(:trip_created, trip.id) end end

Slide 89

Slide 89 text

Conway's Law and DDD Conway's Law, paraphrased: "Software systems tend to look like the organizations that produce them"

Slide 90

Slide 90 text

Conway's Law and DDD Conway's Law, paraphrased: "Software systems tend to look like the organizations that produce them" DDD modeling oftentimes reveals domains that follow organizational layouts. Your software systems follow organizational optimizations.

Slide 91

Slide 91 text

Conway's Law and DDD Conway's Law, paraphrased: "Software systems tend to look like the organizations that produce them" DDD modeling oftentimes reveals domains that follow organizational layouts. Your software systems follow organizational optimizations. Thus this is a very natural place to draw a seam!

Slide 92

Slide 92 text

Warning: Limitations apply! Don't try to do this on every project!

Slide 93

Slide 93 text

Warning: Limitations apply! Don't try to do this on every project! I've been guilty of overdesigning.

Slide 94

Slide 94 text

Warning: Limitations apply! Don't try to do this on every project! I've been guilty of overdesigning. Try it out, step by step

Slide 95

Slide 95 text

Warning: Limitations apply! Don't try to do this on every project! I've been guilty of overdesigning. Try it out, step by step Back it out if this doesn't " t"

Slide 96

Slide 96 text

What we did tonight Visualized our system with a Context Map ✏ Drew boundaries in our code! Do a little bit of refactoring with domain modules, Aggregate Roots and Domain Events.

Slide 97

Slide 97 text

Sample code: https://www.github.com/andrewhao/delorean

Slide 98

Slide 98 text

Thanks! Github: andrewhao Twitter: @andrewhao Email: andrew@carbon ve.com

Slide 99

Slide 99 text

Prior Art W. P. Stevens ; G. J. Myers ; L. L. Constantine. "Structured Design" - IBM Systems Journal, Vol 13 Issue 2, 1974 Evans, Eric. Domain Driven Design Vernon, Vaughan. Implementing Domain-Driven Design http://www.win.tue.nl/~wstomv/quotes/structured- design.html#6 https://www.infoq.com/articles/ddd-contextmapping http://gorodinski.com/blog/2013/04/29/sub-domains-and- bounded-contexts-in-domain-driven-design-ddd/