CHAPTERS
1. What is Clean Architecture?
2. How to use it with Rails?
3. When to use it?
Slide 4
Slide 4 text
CHAPTER I
What
is Clean Architecture?
Slide 5
Slide 5 text
the
PROBLEM
Slide 6
Slide 6 text
No content
Slide 7
Slide 7 text
your top level structure is
screaming
the web FRAMEWORK
Slide 8
Slide 8 text
The web is a
delivery
mechanism
Slide 9
Slide 9 text
yet it
DOMINATES
your code
Slide 10
Slide 10 text
image you’ve just joined
a
PROJECT
Slide 11
Slide 11 text
No content
Slide 12
Slide 12 text
what makes a
GOOD
architecture?
Slide 13
Slide 13 text
No content
Slide 14
Slide 14 text
Architecture
is about
Intent
Slide 15
Slide 15 text
Ivar Jacobson
Slide 16
Slide 16 text
BUE
• boundaries
• USE CASES
• entities
Slide 17
Slide 17 text
application specific
business rules
USE CASES
(aka Interactors)
Slide 18
Slide 18 text
ENTITIES
application independent
business rules
Slide 19
Slide 19 text
communication interfaces
Boundaries
Slide 20
Slide 20 text
diagrams: Robert C Martin
Slide 21
Slide 21 text
No content
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
No content
Slide 26
Slide 26 text
What about MVC?
Slide 27
Slide 27 text
MVC as a Web
architecture
Slide 28
Slide 28 text
What about the
database?
Slide 29
Slide 29 text
No content
Slide 30
Slide 30 text
The database
is a detail
Slide 31
Slide 31 text
Isolate it!
Slide 32
Slide 32 text
images: Robert C Martin
Slide 33
Slide 33 text
CHAPTER II
HOW
to use it with Rails?
Slide 34
Slide 34 text
Let’s create an accounting
app called
Lannister
Slide 35
Slide 35 text
No content
Slide 36
Slide 36 text
Lannisters and Starks
have accounts at the
Iron Bank of Braavos
Slide 37
Slide 37 text
Sometimes they want to
transfer money
between their accounts
Slide 38
Slide 38 text
trades
debit: -1000
credit: +1000
Slide 39
Slide 39 text
No content
Slide 40
Slide 40 text
7 steps
Slide 41
Slide 41 text
bundle
gem Lannister
1. Create project
Slide 42
Slide 42 text
RDD
(README driven development)
1. Create project
2. Define API
Slide 43
Slide 43 text
Usage:
Transfer money
Lannister.
transfer_money(source_account_id: 1,
destination_account_id: 2,
amount: 10_000)
Slide 44
Slide 44 text
BDD
1. Create project
2. Define API
3. Define behavior
Slide 45
Slide 45 text
No content
Slide 46
Slide 46 text
the
ENTRY POINT
1. Create project
2. Define API
3. Define behavior
4. Entry point
Slide 47
Slide 47 text
module Lannister
class << self
delegate :transfer_money, to: UseCases::TransferMoney
end
end
Slide 48
Slide 48 text
it would be better to run this use case
inside a
transaction
Slide 49
Slide 49 text
require 'caze'
module Lannister
include Caze
has_use_case :transfer_money, UseCases::TransferMoney, transactional: true
end
github.com/magnetis/caze
Slide 50
Slide 50 text
the
USE CASE
1. Create project
2. Define API
3. Define behavior
4. Entry point
5. Use case
Slide 51
Slide 51 text
module Lannister
module UseCases
class TransferMoney
def transfer
return false if get_balance(account_id: source_account_id) < amount
trade_repo.persist Entities::Trade.new(account_id: source_account_id, amount: - amount)
trade_repo.persist Entities::Trade.new(account_id: destination_account_id, amount: amount)
end
end
end
end
Slide 52
Slide 52 text
the
TRADE Entity
1. Create project
2. Define API
3. Define behavior
4. Entry point
5. Use case
6. Entity
Slide 53
Slide 53 text
require 'active_model'
module Lannister
module Entities
class Trade
include ActiveModel::Model
attr_accessor :id, :account_id, :amount, :date
validates_presence_of :account_id, :amount, :date
end
end
end
Slide 54
Slide 54 text
the
REPOSITORY
1. Create project
2. Define DSL
3. Define behavior
4. Entry point
5. Use case
6. Entity
7. Repository
Slide 55
Slide 55 text
let’s
isolate DATA
within a
RAILS ENGINE
Slide 56
Slide 56 text
rails plugin new lannister_data --mountable
Slide 57
Slide 57 text
rails g model account name
rails g model trade account:references amount:decimal date:date
Slide 58
Slide 58 text
engine/
app/
models/
repositories/
Slide 59
Slide 59 text
module LannisterData
class TradeRepo
def self.persist(entity)
row = Trade.create!(account_id: entity.account_id,
amount: entity.amount)
entity.id = row.id
entity
end
end
end
Slide 60
Slide 60 text
Lannister
data
Lannister
behavior
Let’s connect them
Slide 61
Slide 61 text
lannister.gemspec
Gem::Specification.new do |spec|
spec.add_dependency 'lannister_data'
spec.add_development_dependency ‘sqlite3'
end
Slide 62
Slide 62 text
require 'lannister_data/engine'
module Lannister
def self.trade_repo
LannisterData::TradeRepo
end
end
class TransferMoneyController < ApplicationController
before_action :load_balance
def create
if Lannister.transfer_money(source_account_id: source_account_id,
destination_account_id: destination_account_id,
amount: amount)
redirect_to new_transfer_money_path
else
flash[:error] = 'Not enough money on the source account'
render :new
end
end
private
def load_balance
@balance = Lannister.get_balance(account_id: current_account_id)
end
end
Slide 73
Slide 73 text
what just happened?
Slide 74
Slide 74 text
the gem represents
WHAT THE
SYSTEM Does
(behavior)
Slide 75
Slide 75 text
No content
Slide 76
Slide 76 text
the engine represents
WHAT THE
SYSTEM IS
(data)
Slide 77
Slide 77 text
Lannister
data
Lannister
Rails
CORE
delivery mechanism behavior
stable API
we have a gem for
portfolio recommendation
Advisor
Slide 90
Slide 90 text
Advisor.recommend_portfolio_for(params)
Slide 91
Slide 91 text
No content
Slide 92
Slide 92 text
we have a gem for accounting
Accountant
Slide 93
Slide 93 text
No content
Slide 94
Slide 94 text
a gem for
ENTITIES
Slide 95
Slide 95 text
we also use
vanilla Rails
Slide 96
Slide 96 text
CHAPTER III
when
to use it?
Slide 97
Slide 97 text
3
Heuristics
Slide 98
Slide 98 text
#1
distance from the view
Slide 99
Slide 99 text
the domain logic is
far from the view
Clean architecture
#1
Slide 100
Slide 100 text
the domain logic is
close to the view
Rails way
#1
Slide 101
Slide 101 text
#2
your company’s
context
Slide 102
Slide 102 text
core
business
your product
#2
rails way
clean
architecture
Slide 103
Slide 103 text
#3
TIMING
Slide 104
Slide 104 text
#3
image: Kent Beck
Slide 105
Slide 105 text
Lessons learned
Slide 106
Slide 106 text
NOT so good
• more files to handle in your cognitive memory
• over engineering when the domain logic is close to the view
• getting out of the rails way may hurt in the beginning
Slide 107
Slide 107 text
• delay decisions until the last responsible moment
• makes clear app’s intent (not how it’s built)
• business rules isolated from the web, framework and database
• consequence: tests running faster
the cool STUFF
Slide 108
Slide 108 text
I am not saying that
EVERYONE
should be using this approach