Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clean Architecture on Rails - RubyConf Brasil 2015

01684ebe74590fa2d4c6f37e7a47c043?s=47 bezelga
September 19, 2015

Clean Architecture on Rails - RubyConf Brasil 2015

01684ebe74590fa2d4c6f37e7a47c043?s=128

bezelga

September 19, 2015
Tweet

Transcript

  1. CLEAN ARCHITECTURE ON RAILS

  2. None
  3. It’s impressive how this talk spread like wild fire!

  4. CHAPTER I What is Clean Architecture?

  5. but, who am I?

  6. Fabiano Beselga CTO magnetis.com.br WRITER medium.com/@fbzga TWITTER @fbzga

  7. bit.ly/clean-rails

  8. CHAPTER I What is Clean Architecture?

  9. the PROBLEM

  10. None
  11. your top level structure is screaming the web FRAMEWORK

  12. The web is a delivery mechanism

  13. the web is a COMMODITY

  14. yet it DOMINATES your code

  15. image you’ve just joined a PROJECT

  16. None
  17. None
  18. what makes a GOOD architecture?

  19. it is not about frameworks and tools

  20. IVAR JACOBSON

  21. business rules INTERACTORS (aka use cases)

  22. domain model ENTITIES

  23. communication interfaces BOUNDARIES

  24. diagrams: Robert C Martin

  25. None
  26. None
  27. None
  28. None
  29. None
  30. What about MVC?

  31. MVC as a Web architecture

  32. this is NOT WHAT I MEANT

  33. WEB FRAMEWORK APP

  34. CHAPTER II HOW to use it with Rails?

  35. BRAAVOS

  36. None
  37. WE ARE OPENING A BANK! WE NEED TO HIRE A

    GREAT DEVELOPER!
  38. None
  39. WHAT ABOUT THIS ONE?

  40. None
  41. None
  42. None
  43. HIRED!

  44. first day at WORK

  45. THE SYSTEM MUST TRANSFER MONEY BETWEEN THE FAMILIES ACCOUNTS

  46. OK! ANY EXCEPTION COURSES?

  47. WHEN THERE ISN’T ENOUGH MONEY TO TRANSFER.

  48. that’s easy, I can do it in 7 STEPS!

  49. GREAT! YOU HAVE 10 MINUTES!

  50. challenge accepted!

  51. 1 create the PROJECT

  52. rails new braavos

  53. mkdir core

  54. module Core end core.rb

  55. 2 define BOUNDARIES

  56. README Driven Development

  57. Usage Transfer money Core.transfer_money(amount: 10_000, source_account_id: 1, destination_account_id: 2) README

    => true # when transfer successful => false # when not enough balance
  58. 3 define behaviour Driven Development

  59. context 'enough money to transfer' do it 'responds true' do

    expect(transfer).to be true end it 'debits the source account' do expect{ transfer }.to change{ source_account_balance }.by(- amount) end it 'credits the destination account' do expect{ transfer }.to change{ destination_account_balance }.by(amount) end end PRIMARY course
  60. context 'not enough money to transfer' do it 'cancels the

    transfer and responds false' do expect(transfer).to be false end it 'does not change the accounts balance' do expect{ transfer }.to_not change{ source_account_balance } expect{ transfer }.to_not change{ destination_account_balance } end end EXCEPTION COURSE
  61. FFF

  62. define DATA MODEL 4

  63. CAN YOU TELL ME A LITTLE MORE ABOUT THE DOMAIN?

  64. EACH FAMILY HAS AN ACCOUNT. EACH ACCOUNT HAS A HISTORY

    OF TRADES MADE.
  65. rails g model account name rails g model trade account:references

    amount:decimal date:date
  66. trades debit: -1000 credit: +1000

  67. the ENTRY POINT 5

  68. module Core class << self delegate :transfer_money, to: TransferMoney end

    end core.rb
  69. the USE CASE 6 Image: HENRY HEMMING

  70. module Core class TransferMoney def self.transfer_money(source_account_id:, destination_account_id:, amount:) balance =

    Core.get_balance(account_id: source_account_id) return false if balance < amount ActiveRecord::Base.transaction do Trade.create!(account_id: source_account_id, amount: -amount) Trade.create!(account_id: destination_account_id, amount: amount) end end end end core/transfer_money.rb
  71. None
  72. REFACTOR!

  73. module Core class << self delegate :transfer_money, to: TransferMoney end

    end core.rb before
  74. CAZE DSL to elegantly declare use cases

  75. Gemfile gem 'caze'

  76. module Core include Caze has_use_case :transfer_money, TransferMoney end core.rb AFTER

  77. module Core include Caze has_use_case :transfer_money, TransferMoney, transactional: true end

    core.rb AFTER
  78. g github.com/magne/s/caze

  79. BREAKING NEWS! 
 WE HAVE MONEY TO HIRE AGAIN!

  80. WHAT ABOUT THIS SNOW?

  81. WHAT?

  82. None
  83. None
  84. Usage Transfer money Core.transfer_money(amount: 10_000, source_account_id: 1, destination_account_id: 2) README

    => true # when transfer successful => false # when not enough balance
  85. None
  86. expose it to the web 7

  87. let’s call it on a CONTROLLER

  88. class TransferMoneyController < ApplicationController before_action :load_balance def create if Core.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 = Core.get_balance(account_id: current_account_id) end end
  89. None
  90. bit.ly/braavos

  91. what just happened?

  92. Core represents WHAT THE SYSTEM Does CHANGES

  93. core/ get_balance.rb transfer_money.rb BEHAVIOUR CHANGES

  94. the models represent WHAT THE SYSTEM IS STABLE

  95. app/ models/ account.rb trade.rb DATA STABLE

  96. a key, longstanding hallmark of a good program is that

    it separates what is stable from what changes in the interest of good maintenance. - Trygve Reenskaug
  97. Core represents WHAT THE SYSTEM Does CHANGES

  98. the models represent WHAT THE SYSTEM IS STABLE

  99. Models Core Rails delivery mechanism behaviour MODULE data

  100. the ALTERNATIVES

  101. Models Core Rails delivery mechanism behaviour RAILS ENGINE

  102. The database is a detail

  103. Isolate it!

  104. Models Core Rails delivery mechanism behaviour gem rails engine data

  105. Core Web Models UI DB Controllers Views

  106. but what makes a GOOD architecture?

  107. CLEAN ARCHITECTURE HEXAGONAL ARCHITECTURE DOMAIN DRIVEN DESIGN DATA CONTEXT INTERACTION

    TRAILBLAZER
  108. all these architectures have SIMILAR OBJECTIVES

  109. what about real life?

  110. magnetis.com.br

  111. ~ 4 years old codebase

  112. experimenting with clean architecture ~ 2 years

  113. None
  114. the first seeds magnetis core

  115. a module for each DOMAIN CONCEPT

  116. we have a module for investments recommendation Advisor

  117. Advisor.recommend_investments(params)

  118. None
  119. we have a module for accounting Accountant

  120. None
  121. magnetis/ domain/ accountant/ advisor/

  122. BOUNDED CONTEXTs Advisor Accountant Order Manager Account Manager Trader Pricing

    Backtesting
  123. why not MICROSERVICES?

  124. we also use the Rails WAY

  125. FINAL CHAPTER when to use it?

  126. YOUR PRODUCT

  127. core business your product rails way clean architecture

  128. Eric Evans Don’t try yo apply DDD to everything. Draw

    a context map and decide on where you will make a push for DDD and where you will not .
  129. THE distance from the view

  130. the domain logic is close to the view Rails way

  131. the domain logic is far from the view Clean architecture

  132. TIMING

  133. image: Kent Beck

  134. Lessons learned

  135. no good

  136. Over-engineering when the domain logic is close to the view

  137. getting out of the rails conventions may be hard

  138. the cool STUFF

  139. defer decisions until the last responsible moment

  140. it SCREAMS BEHAVIOR

  141. decoupled business rules

  142. tests RUNNING FASTER!

  143. what makes a GOOD architecture?

  144. Architecture is about Intent

  145. Intent = what the SYSTEM DOES

  146. reflect THE USER’S MENTAL MODEL in your architecture

  147. domain as a 1st class citizen

  148. ONE MORE THING…

  149. None
  150. None
  151. we are hiring fabiano@magne/s.com.br magne/s.com.br/dev

  152. Developer #1 Developer #2 The Boss The Mastermind Tio Roberto

    Domain Father Advisor Presenter CAST TYRION LANNISTER Jon SNOW SHERKLOCKS’ BROTHER IVAR JACOBSON UNCLE BOB ERIC EVANS ROBERT DUVAL FABIANO BESELGA by @fbzga