dci – DataContextInteraction



dci – DataContextInteraction

0 0


dci-presentation

Presentation on DCI for ROROSyd

On Github suranyami / dci-presentation

dci

DataContextInteraction

DCI: What is it?

  • Created by Trygve Reenskaug (he came up with MVC)
  • Complement to MVC
  • Attempts to encapsulate the behaviour of a system

DCI: What is it?

DCI: What is it?

DATA roughly = Model CONTEXT where roles exist (a 'use case') INTERACTION the role played between models (methods)

Typical situation

  class Account < ActiveRecord::Base
    validate :name, presence: true
    validate :amount, presence: true, numericality: true
  end
            
To transfer money from one account to another:
  class Account < ActiveRecord::Base
    validate :name, presence: true
    validate :amount, presence: true, numericality: true

    def transfer(trans_amt, other)
      self.amount -= trans_amt
      other.amount += trans_amt
    end
  end
            

What's wrong with THAT?

Can't test the behaviour independently of the data You end up with lots of methods like this scattered through an ever-increasing model.

In DCI

Make a context for a transaction
  class TransferContext
    def initialize(source_id, dest_id)
      @source = Account.find(source_id)
      @destination = Account.find(dest_id)
    end
    def execute(amount)
      @source.amount -= amount
      @destination.amount += amount
    end
  end
            

Why?

Now the behaviour can be independently tested:
  describe TransferContext do
    before do
      @source = Account.new(amount: 2000)
      @destination = Account.new(amount: 0)
      Account.expects(:find).with(1).returns(@source)
      Account.expects(:find).with(2).returns(@destination)
      @transfer = TransferContext.new(1, 2)
    end
    describe "executing transfer" do
      before {@transfer.execute(99)}
      it "subtracts amount from source, adds to dest" do
        @source.amount.should eql 1901
        @destination.amount.should eql 99
      end
    end
  end
            

How does my Controller look now?

Pretty thin!
  class AccountController < ActiveSupport::TestCase
    def transfer
      transfer_context = TransferContext.new(params[:source], params[:dest])
      transfer_context.execute(params(:amount))
    end
  end
            

What about my Model?

Slenderific!
  class Account < ActiveRecord::Base
    validate :name, presence: true
    validate :amount, presence: true, numericality: true
  end
            

The most important results?

  • Small, independently testable blocks of code
  • Each describe a particular piece of behaviour
  • That's all they describe
  • Small, easy tests for each piece of behaviour
  • Less interdependencies
  • Thin Controllers
  • Thin Models

More info?

  • "Clean Ruby" book by Jim Gay http://www.clean-ruby.com
  • @suranyami
  • github.com/suranyami
  • This presentation: https://github.com/suranyami/dci-presentation