Page 1
@skwp @reverbdotcom #wcr14
Yan PritzkerCTO, Reverb.com
@skwp @reverbdotcom
Domain Driven Rails
https://speakerdeck.com/skwp
Page 3
@skwp @reverbdotcom #wcr14
Are you happy with the SIZE and COMPLEXITY
of your models?
Page 4
@skwp @reverbdotcom #wcr14
Are you happy with the SCALABILITY of your team?
Page 5
@skwp @reverbdotcom #wcr14
Are you happy with the ADAPTABILITY
of your business?
Page 6
@skwp @reverbdotcom #wcr14
What are you building?
Page 7
@skwp @reverbdotcom #wcr14
Rails is a detail!"
Decouple all things"
Is the code better?"
Keep it Railsy"
Page 8
@skwp @reverbdotcom #wcr14
Somewhere in between
Simple CRUD"Apps"
Complex Enterprise
Logic"
Page 9
@skwp @reverbdotcom #wcr14
I want to discover relevant compromises
rather than defend ideals
Page 10
@skwp @reverbdotcom #wcr14
Page 11
@skwp @reverbdotcom #wcr14
Over 115
Model Classes
Page 12
@skwp @reverbdotcom #wcr14
Over 1000 Total
Classes
Page 13
@skwp @reverbdotcom #wcr14
Page 14
@skwp @reverbdotcom #wcr14
Not great, but we have only three chubby models
Page 15
@skwp @reverbdotcom #wcr14
Are monoliths bad for business?
Page 16
@skwp @reverbdotcom #wcr14
Quick IterationsLow Operational
ComplexityRefactoring
Page 17
Monolith
ServiceService
Service Service
Service
Early 2013 - Startup / Proof of Concept
Page 18
Monolith
ServiceService
Service Service
Service
2014 - Growth Phase
Page 19
@skwp @reverbdotcom #wcr14
http://martinfowler.com/articles/distributed-objects-microservices.html
“I'm wary of distribution and my default inclination is to prefer
a monolithic design”
“While small microservices are certainly simpler to reason about, I worry that this pushes complexity into the
interconnections between services”
“Refactoring becomes much harder when you have to do it across remote boundaries.”
Page 20
@skwp @reverbdotcom #wcr14
Quick IterationsLow Operational
ComplexityMay lead to a BBOM
Page 21
@skwp @reverbdotcom #wcr14
Maintainable Monoliths
Can Be Achieved
Page 22
@skwp @reverbdotcom #wcr14
Page 23
@skwp @reverbdotcom #wcr14
Page 24
@skwp @reverbdotcom #wcr14
Product 400 LOC
~150 LOC non-ActiveRecord Churn: 49 changes this year"
Order 333 LOC
~200 LOC non-ActiveRecord Churn: 36 changes this year
User 338 LOC
~200 LOC non-ActiveRecord Churn: 29 changes this year
Page 25
CHURN
Product
OrderUser
Page 26
@skwp @reverbdotcom #wcr14
Commonly used classes are hard
to refactor
Page 27
@skwp @reverbdotcom #wcr14
Stop modifying
code!(Open/Closed Principle)
Page 28
@skwp @reverbdotcom #wcr14
Don’t put different rates
of change together"
Kent Beck - Smalltalk Best Practice Patterns(see also: Single Responsibility Principle)
Page 29
@skwp @reverbdotcom #wcr14
Don’t put different rates
of change together"
Kent Beck - Smalltalk Best Practice Patterns
Data Model
Business Logic
Page 30
@skwp @reverbdotcom #wcr14
Separate what the system is "
from what the system does"
James Copelien & Trygve Reenskaug(Data, Context, Interaction)
Page 31
@skwp @reverbdotcom #wcr14
Where does business logic go?
Page 32
@skwp @reverbdotcom #wcr14
Controller
2005
ActiveRecord
Mailers
Services
User
Page 33
@skwp @reverbdotcom #wcr14
Console?"Rake task?"
Background jobs?"API layer?"Testing?
What about…
Page 34
@skwp @reverbdotcom #wcr14
Order
Fat models?
Page 35
@skwp @reverbdotcom #wcr14
OrderRefund
Page 36
@skwp @reverbdotcom #wcr14
OrderRefund
Ship
Page 37
@skwp @reverbdotcom #wcr14
OrderRefund
ShipCheck Fraud Risk
Page 38
@skwp @reverbdotcom #wcr14
“Active Record is a good choice for domain logic that isn't too
complex, such as creates, reads, updates, and deletes”
Martin Fowler
Page 39
@skwp @reverbdotcom #wcr14
If Controller and Model are all you have then
one has to be skinny and one has to be fat
Page 40
@skwp @reverbdotcom #wcr14
Domain Layerskinny framework,
healthy business logic,"no fat anywhere
http://joncairns.com/2013/04/fat-model-skinny-controller-is-a-load-of-rubbish/
Page 41
Model
Controller
View
The Rails WayThe
WayActiveRecord
Use Cases
Grape API
ControllersCron
Redis
Rake
HTTP Services
Workers
REntities
Roles
DB
ListenersEvents
Page 42
@skwp @reverbdotcom #wcr14
app/reverbfor app specific
Page 43
@skwp @reverbdotcom #wcr14
lib/reverbfor Open Source / Generic
Page 44
@skwp @reverbdotcom #wcr14
Reverb::Namespace to avoid collisions with gems
Page 45
@skwp @reverbdotcom #wcr14
Page 46
@skwp @reverbdotcom #wcr14
This is amarketplace!
http://blog.8thlight.com/uncle-
bob/2011/09/30/Screaming-
Architecture.html
Page 47
@skwp @reverbdotcom #wcr14
http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
Page 48
@skwp @reverbdotcom #wcr14
see also Hexagonal, Ports & Adapters, DCI
Page 49
@skwp @reverbdotcom #wcr14
Clean Enough
Architecture"
Page 50
@skwp @reverbdotcom #wcr14
Choosing the right fight
Page 51
@skwp @reverbdotcom #wcr14
http://blog.8thlight.com/mike-ebert/2013/03/23/the-repository-pattern.html
Page 52
@skwp @reverbdotcom #wcr14
http://blog.8thlight.com/mike-ebert/2013/03/23/the-repository-pattern.html
(but you should still read this)
Page 53
@skwp @reverbdotcom #wcr14
User.where(…)User.activeUser.find(1)
This is easy to replace with something other than AR. Repository not required.
Don’t leak SQL outside of AR
Page 54
@skwp @reverbdotcom #wcr14
Domain Logic in ActiveRecord and
Controllers leads to Churn
Page 55
@skwp @reverbdotcom #wcr14
Use CasesReify complex business logic
into classes
Page 56
@skwp @reverbdotcom #wcr14
Page 57
@skwp @reverbdotcom #wcr14
This is not"Rails
Page 58
@skwp @reverbdotcom #wcr14
Page 59
@skwp @reverbdotcom #wcr14
Explicitly Require Dependency
http://myronmars.to/n/dev-blog/2012/12/5-reasons-to-avoid-bundler-require
Invoke It
Page 60
@skwp @reverbdotcom #wcr14
Code Reuse!
Thin Shell
Page 61
@skwp @reverbdotcom #wcr14
Stubby
Happy Path
Page 62
@skwp @reverbdotcom #wcr14
Testing conditionals and side effects has nothing to do with Rails
Page 63
@skwp @reverbdotcom #wcr14
NamingThe hardest problem in computer science
Page 64
@skwp @reverbdotcom #wcr14
OrderService
Page 65
@skwp @reverbdotcom #wcr14
OrderService
Page 66
@skwp @reverbdotcom #wcr14
OrderService
NounService Grows Unbounded
Page 67
@skwp @reverbdotcom #wcr14
Use verbs to narrow your scope
Page 68
@skwp @reverbdotcom #wcr14
Order
Page 69
@skwp @reverbdotcom #wcr14
Order
ShipOrder
Page 70
@skwp @reverbdotcom #wcr14
Order
ShipOrder Cancel Order
Page 71
@skwp @reverbdotcom #wcr14
Order
ShipOrder Cancel Order
RefundOrder
Page 72
@skwp @reverbdotcom #wcr14
Order
ShipOrder Cancel Order
RefundOrder
Does notgrow over time
Page 73
@skwp @reverbdotcom #wcr14
ShipOrder Cancel Order
RefundOrder
Don’t change"once you write them
Page 74
@skwp @reverbdotcom #wcr14
Ubiquitous Language
Page 75
@skwp @reverbdotcom #wcr14
Page 76
@skwp @reverbdotcom #wcr14
What is this?
Page 77
@skwp @reverbdotcom #wcr14
RolesAdd methods to objects on demand
in the context of a Use Case
Page 78
@skwp @reverbdotcom #wcr14
Decorator
Page 79
@skwp @reverbdotcom #wcr14
We added these methods
Page 80
@skwp @reverbdotcom #wcr14
Page 81
@skwp @reverbdotcom #wcr14
Methods related to each"other but loosely related"
to the parent concept"and used only in a few"
Use Cases
Page 82
@skwp @reverbdotcom #wcr14
Page 83
@skwp @reverbdotcom #wcr14
EventsAdd behavior with listeners
without modifying code
Page 84
@skwp @reverbdotcom #wcr14
Order
ActiveRecord Callbacks
Send Email
Page 85
@skwp @reverbdotcom #wcr14
Order
ActiveRecord Callbacks
Send Email
Call External Service
Page 86
@skwp @reverbdotcom #wcr14
Order
ActiveRecord Callbacks
Send Email
Call External Service
ConditionalCallbacks
Page 87
@skwp @reverbdotcom #wcr14
AR callbacks become more complex as the
system supports more use cases
Page 88
@skwp @reverbdotcom #wcr14
Different use cases may trigger different events
even when working with the same model
Instead
Page 89
@skwp @reverbdotcom #wcr14
Page 90
@skwp @reverbdotcom #wcr14
Page 91
@skwp @reverbdotcom #wcr14
Add another listener to add behavior The core class doesn’t change
Page 92
@skwp @reverbdotcom #wcr14
Global listeners for cross-cutting concerns without littering code
Wisper::GlobalListeners.add(Reverb::Listeners::AnalyticsListener.new)"
Page 93
@skwp @reverbdotcom #wcr14
Controller is a listener too
Page 94
@skwp @reverbdotcom #wcr14
Policy ObjectsReify complex business rules
into objects
Page 95
@skwp @reverbdotcom #wcr14
Likely to change
Unlikely to change
Page 96
@skwp @reverbdotcom #wcr14
refactored for readability
Page 97
@skwp @reverbdotcom #wcr14
Injectable, but has a default
Simple code, only need to test one pathfor the imperative side effect"
(sending an email)
Page 98
@skwp @reverbdotcom #wcr14
Distillation Time
Page 99
@skwp @reverbdotcom #wcr14
Language of codebase reflects language of business
Page 100
@skwp @reverbdotcom #wcr14
Separate behaviors (what the system does) from models (what the system is)
Page 101
@skwp @reverbdotcom #wcr14
Rates of Change
Page 102
@skwp @reverbdotcom #wcr14
Is this all Unicorns and Rainbows?
Page 103
@skwp @reverbdotcom #wcr14
Sprawl?"Onboarding?"
Naming?"Documentation?"
Page 104
@skwp @reverbdotcom #wcr14
http://blog.mattwynne.net/category/hexagonal-rails/
http://clean-ruby.com/
Resourceshttp://confreaks.com/videos/759-rubymidwest2011-keynote-
architecture-the-lost-years
http://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html
http://www.artima.com/articles/dci_vision.html
http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
https://speakerdeck.com/skwp
Page 105
@skwp @reverbdotcom #wcr14
We Are Hiring Always"
jobs.reverb.com
Ruby, ElasticSearch, DevOps, Designers, Android, and more!
[email protected]