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

DCI And Rails (Jakarta.rb February 2014)

DCI And Rails (Jakarta.rb February 2014)

My presentation during Jakarta.rb meetup on February 2014 about "DCI and Rails"

Giovanni Sakti

February 19, 2014
Tweet

More Decks by Giovanni Sakti

Other Decks in Programming

Transcript

  1. I’m having problem with remembering people faces and names !

    self-diagnosed with mild OCD ! 70% of my time during work is spent on reviewing codes from collaborators 6JKUKUOG
  2. My Main Concern Ensure that codes is as comprehendible as

    possible, make sure that any people whom participate in the project can understand and contribute effortlessly even though the project grows larger and larger and larger ! RGTHQTOCPEGOC[GXGPDGCUGEQPFEKVK\GPKPUQOGECUG
  3. I’m (currently) not a DCI proponent ! I’m (still) not

    using it for production (only experimenting with it) ! I understand that Rails still can’t support it 100% yet
  4. Common Rails developer evolution on “where to put my logic?”

    ! QTKPUGTVCP[QVJGTYGD/8%HTCOGYQTMU .GVņUVCMGCNQQMCV
  5. <% people = Person.where("added_at > ? AND deleted = ?",

    Time.now.utc, false).order("last_name, first_name") %> <% people.reject { |p| p.address.nil? }.each do |person| %> <div id="person-<%= person.new_record? ? "new" : person.id %>"> <span class="name"> <%= person.last_name %>, <%= person.first_name %> </span> <span class="age"> <%= (Date.today - person.birthdate) / 365 %> </span> </div> <% end %>
  6. <% @people.each do |person| %> <div id="person-<%= person.new_record? ? "new"

    : person.id %>"> <span class="name"> <%= person.last_name %>, <%= person.first_name %> </span> <span class="age"> <%= (Date.today - person.birthdate) / 365 %> </span> </div> <% end %> class PeopleController < ActionController::Base def index @people = Person.where("added_at > ? AND deleted = ?", Time.now.utc, false).order("last_name, first_name") @people = @people.reject { |p| p.address.nil? } end end
  7. class Person < ActiveRecord::Base def self.find_recent people = Person.where("added_at >

    ? AND deleted = ?", Time.now.utc, false).order("last_name, first_name") people = people.reject { |p| p.address.nil? } end ! def name "#{last_name}, #{first_name}" end ! def age (Date.today - person.birthdate) / 365 end ! def pseudo_id new_record? ? "new" : id end end
  8. <% @people.each do |person| %> <div id="person-<%= person.pseudo_id %>"> <span

    class="name"><%= person.name %></span> <span class="age"><%= person.age %></span> </div> <% end %> class PeopleController < ActionController::Base def index @people = Person.find_recent end end
  9. But, was anything fat ever good in our app codebase?

    ! +UVKNNFQPņVMPQYVJGCPUYGTHQTVJKU
  10. Class  User  <<  ActiveRecord::Base   !   def  process1_method1  

      …     end   !   def  process1_method2     …     end   ! end
  11. Class  User  <<  ActiveRecord::Base   !   def  process1_method1  

      …     end   !   def  process1_method2     …     end   !   def  process2_method1     …     end   !   def  process2_method2     …     end   ! end
  12. Class  User  <<  ActiveRecord::Base   !   def  process1_method1  

      …     end   !   def  process1_method2     …     end   !   def  process2_method1     …     end   !   def  process2_method2     …     end   !   def  process1_method3     …     end   !   def  process1_method4     …     end   !   def  process3_method1     …     end   !   def  process3_method2     …     end   ! end
  13. Class  User  <<  ActiveRecord::Base   !   def  process1_method1  

      …     end   !   def  process1_method2     …     end   !   def  process2_method1     …     end   !   def  process2_method2     …     end   !   def  process1_method3     …     end   !   def  process1_method4     …     end   !   def  process3_method1     …     end   !   def  process3_method2     …     end   !   def  process1_method1     …     end   !   def  process1_method2     …     end   !   def  process2_method1     …     end   !   def  process2_method2     …     end   !   def  process1_method3     …     end   !   def  process1_method4     …     end   !   def  process3_method1     …     end   !   def  process3_method2     …     end   !   def  process1_method1     …     end   !   def  process1_method2     …     end   !   def  process2_method1     …     end   !   def  process2_method2     …     end   !   def  process1_method3     …     end   !   def  process1_method4     …     end   !   def  process3_method1     …     end   !   def  process3_method2     …     end   ! end
  14. Class  User  <<  ActiveRecord::Base     include  Commentable    

    include  Taggable     include  Manageable     include  ShoppingCartable     include  BlahBlah     …etc   end
  15. 9JCVņUYTQPIYKVJVJKU! Class  User  <<  ActiveRecord::Base     include  Commentable  

      include  Taggable     include  Manageable     include  ShoppingCartable     include  BlahBlah     …etc   end
  16. Developer still have to makes sense what goes where !

    Business Process cannot be evaluated procedurally
  17. Wait.. ! If we use BDD we can see and

    build business processes using the integration test, right?
  18. Wait.. ! If we use BDD we can see and

    build business processes using the integration test, right? 6JCVKU%QTTGEV
  19. DCI is about assigning roles to a model in a

    context /[WPFGTUVCPFKPIQH&%+ EQPVGZV#-#WUGTUVQT[
  20. A model must not have any business process related methods..

    ! business process and model must be tightly separated..
  21. Class  User  <<  ActiveRecord::Base     include  Commentable    

    include  Taggable     include  Manageable     include  ShoppingCartable     include  BlahBlah     …etc   end ;GCJVJKUKUYTQPIKP&%+
  22. So where should we put our code then? ! not

    in views.. not in controllers.. not in models.. not in concerns..
  23. class User < ActiveRecord::Base validates :name, :presence => true end

    ! class Book < ActiveRecord::Base validates :title, :presence => true end 6JKUKUQWTOQFGNU
  24. 6JKUKUQWTEQPVGZV class AddToCartContext attr_reader :user, :book ! def self.call(user, book)

    AddToCartContext.new(user, book).call end ! def initialize(user, book) @user, @book = user, book @user.extend Customer end ! def call @user.add_to_cart(@book) end end
  25. class AddToCartContext attr_reader :user, :book ! def self.call(user, book) AddToCartContext.new(user,

    book).call end ! def initialize(user, book) @user, @book = user, book @user.extend Customer end ! def call @user.add_to_cart(@book) end end
  26. class AddToCartContext attr_reader :user, :book ! def self.call(user, book) AddToCartContext.new(user,

    book).call end ! def initialize(user, book) @user, @book = user, book @user.extend Customer end ! def call @user.add_to_cart(@book) end end 2GTHQTOCPEGRTQDNGOJGTG
  27. What I showed you was not an official method to

    do DCI in Rails. ! In fact, no official method exist yet.
  28. “This approach to breaking up domain logic into concerns is

    similar in some ways to the DCI notion of Roles. It doesn’t have the run-time mixin acrobatics nor does it have the “thy models shall be completely devoid of logic themselves” prescription, but other than that, it’ll often result in similar logic extracted using similar names.”
  29. Imagine a time when business process related code can be

    placed exactly within the actual code (not the test). ! And tightly separated from the model. 9GNN+ņOIQPPCNQUGO[LQDDGECWUGO[ENKGPVUECPEQFG EQORNKECVGFDWUKPGUURTQEGUUD[VJGOUGNXGUKHVJCVJCRRGPU
  30. Ruby and Rails still have long ways to go to

    support DCI completely ! But you can still use it now if you want to.. (QQFHQTVJQWIJVU