@skwp @reverbdotcom #wcr14
Yan PritzkerCTO, Reverb.com
@skwp @reverbdotcom
Domain Driven Rails
https://speakerdeck.com/skwp
@skwp @reverbdotcom #wcr14
Are you happy with the SIZE and COMPLEXITY
of your models?
@skwp @reverbdotcom #wcr14
Are you happy with the SCALABILITY of your team?
@skwp @reverbdotcom #wcr14
Are you happy with the ADAPTABILITY
of your business?
@skwp @reverbdotcom #wcr14
What are you building?
@skwp @reverbdotcom #wcr14
Rails is a detail!"
Decouple all things"
Is the code better?"
Keep it Railsy"
@skwp @reverbdotcom #wcr14
Somewhere in between
Simple CRUD"Apps"
Complex Enterprise
Logic"
@skwp @reverbdotcom #wcr14
I want to discover relevant compromises
rather than defend ideals
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
Over 115
Model Classes
@skwp @reverbdotcom #wcr14
Over 1000 Total
Classes
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
Not great, but we have only three chubby models
@skwp @reverbdotcom #wcr14
Are monoliths bad for business?
@skwp @reverbdotcom #wcr14
Quick IterationsLow Operational
ComplexityRefactoring
Monolith
ServiceService
Service Service
Service
Early 2013 - Startup / Proof of Concept
Monolith
ServiceService
Service Service
Service
2014 - Growth Phase
@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.”
@skwp @reverbdotcom #wcr14
Quick IterationsLow Operational
ComplexityMay lead to a BBOM
@skwp @reverbdotcom #wcr14
Maintainable Monoliths
Can Be Achieved
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
@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
CHURN
Product
OrderUser
@skwp @reverbdotcom #wcr14
Commonly used classes are hard
to refactor
@skwp @reverbdotcom #wcr14
Stop modifying
code!(Open/Closed Principle)
@skwp @reverbdotcom #wcr14
Don’t put different rates
of change together"
Kent Beck - Smalltalk Best Practice Patterns(see also: Single Responsibility Principle)
@skwp @reverbdotcom #wcr14
Don’t put different rates
of change together"
Kent Beck - Smalltalk Best Practice Patterns
Data Model
Business Logic
@skwp @reverbdotcom #wcr14
Separate what the system is "
from what the system does"
James Copelien & Trygve Reenskaug(Data, Context, Interaction)
@skwp @reverbdotcom #wcr14
Where does business logic go?
@skwp @reverbdotcom #wcr14
Controller
2005
ActiveRecord
Mailers
Services
User
@skwp @reverbdotcom #wcr14
Console?"Rake task?"
Background jobs?"API layer?"Testing?
What about…
@skwp @reverbdotcom #wcr14
Order
Fat models?
@skwp @reverbdotcom #wcr14
OrderRefund
@skwp @reverbdotcom #wcr14
OrderRefund
Ship
@skwp @reverbdotcom #wcr14
OrderRefund
ShipCheck Fraud Risk
@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
@skwp @reverbdotcom #wcr14
If Controller and Model are all you have then
one has to be skinny and one has to be fat
@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/
Model
Controller
View
The Rails WayThe
WayActiveRecord
Use Cases
Grape API
ControllersCron
Redis
Rake
HTTP Services
Workers
REntities
Roles
DB
ListenersEvents
@skwp @reverbdotcom #wcr14
app/reverbfor app specific
@skwp @reverbdotcom #wcr14
lib/reverbfor Open Source / Generic
@skwp @reverbdotcom #wcr14
Reverb::Namespace to avoid collisions with gems
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
This is amarketplace!
http://blog.8thlight.com/uncle-
bob/2011/09/30/Screaming-
Architecture.html
@skwp @reverbdotcom #wcr14
http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
@skwp @reverbdotcom #wcr14
see also Hexagonal, Ports & Adapters, DCI
@skwp @reverbdotcom #wcr14
Clean Enough
Architecture"
@skwp @reverbdotcom #wcr14
Choosing the right fight
@skwp @reverbdotcom #wcr14
http://blog.8thlight.com/mike-ebert/2013/03/23/the-repository-pattern.html
@skwp @reverbdotcom #wcr14
http://blog.8thlight.com/mike-ebert/2013/03/23/the-repository-pattern.html
(but you should still read this)
@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
@skwp @reverbdotcom #wcr14
Domain Logic in ActiveRecord and
Controllers leads to Churn
@skwp @reverbdotcom #wcr14
Use CasesReify complex business logic
into classes
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
This is not"Rails
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
Explicitly Require Dependency
http://myronmars.to/n/dev-blog/2012/12/5-reasons-to-avoid-bundler-require
Invoke It
@skwp @reverbdotcom #wcr14
Code Reuse!
Thin Shell
@skwp @reverbdotcom #wcr14
Stubby
Happy Path
@skwp @reverbdotcom #wcr14
Testing conditionals and side effects has nothing to do with Rails
@skwp @reverbdotcom #wcr14
NamingThe hardest problem in computer science
@skwp @reverbdotcom #wcr14
OrderService
@skwp @reverbdotcom #wcr14
OrderService
@skwp @reverbdotcom #wcr14
OrderService
NounService Grows Unbounded
@skwp @reverbdotcom #wcr14
Use verbs to narrow your scope
@skwp @reverbdotcom #wcr14
Order
@skwp @reverbdotcom #wcr14
Order
ShipOrder
@skwp @reverbdotcom #wcr14
Order
ShipOrder Cancel Order
@skwp @reverbdotcom #wcr14
Order
ShipOrder Cancel Order
RefundOrder
@skwp @reverbdotcom #wcr14
Order
ShipOrder Cancel Order
RefundOrder
Does notgrow over time
@skwp @reverbdotcom #wcr14
ShipOrder Cancel Order
RefundOrder
Don’t change"once you write them
@skwp @reverbdotcom #wcr14
Ubiquitous Language
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
What is this?
@skwp @reverbdotcom #wcr14
RolesAdd methods to objects on demand
in the context of a Use Case
@skwp @reverbdotcom #wcr14
Decorator
@skwp @reverbdotcom #wcr14
We added these methods
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
Methods related to each"other but loosely related"
to the parent concept"and used only in a few"
Use Cases
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
EventsAdd behavior with listeners
without modifying code
@skwp @reverbdotcom #wcr14
Order
ActiveRecord Callbacks
Send Email
@skwp @reverbdotcom #wcr14
Order
ActiveRecord Callbacks
Send Email
Call External Service
@skwp @reverbdotcom #wcr14
Order
ActiveRecord Callbacks
Send Email
Call External Service
ConditionalCallbacks
@skwp @reverbdotcom #wcr14
AR callbacks become more complex as the
system supports more use cases
@skwp @reverbdotcom #wcr14
Different use cases may trigger different events
even when working with the same model
Instead
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
@skwp @reverbdotcom #wcr14
Add another listener to add behavior The core class doesn’t change
@skwp @reverbdotcom #wcr14
Global listeners for cross-cutting concerns without littering code
Wisper::GlobalListeners.add(Reverb::Listeners::AnalyticsListener.new)"
@skwp @reverbdotcom #wcr14
Controller is a listener too
@skwp @reverbdotcom #wcr14
Policy ObjectsReify complex business rules
into objects
@skwp @reverbdotcom #wcr14
Likely to change
Unlikely to change
@skwp @reverbdotcom #wcr14
refactored for readability
@skwp @reverbdotcom #wcr14
Injectable, but has a default
Simple code, only need to test one pathfor the imperative side effect"
(sending an email)
@skwp @reverbdotcom #wcr14
Distillation Time
@skwp @reverbdotcom #wcr14
Language of codebase reflects language of business
@skwp @reverbdotcom #wcr14
Separate behaviors (what the system does) from models (what the system is)
@skwp @reverbdotcom #wcr14
Rates of Change
@skwp @reverbdotcom #wcr14
Is this all Unicorns and Rainbows?
@skwp @reverbdotcom #wcr14
Sprawl?"Onboarding?"
Naming?"Documentation?"
@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
@skwp @reverbdotcom #wcr14
We Are Hiring Always"
jobs.reverb.com
Ruby, ElasticSearch, DevOps, Designers, Android, and more!
[email protected]