According to Wikipedia Data, context and interaction (DCI) is a paradigm used in computer software to program systems of communicating objects. Here I am not clear about the problem which DCI tries to solve. Can you explain it with simple example? What is Data, Context and Interactions in your example?
Data, context, and interaction (DCI) is a paradigm used in computer software to program systems of communicating objects.
Context is a span surrounding statements and expressions. Context defines the set of actions you can take, i.e. methods to invoke, variables to use, etc. The following, among others, create a context: a class, a method (or a function, a procedure, etc.), a block of code.
An easy way for me to understand it is with the classic banking application example. In this example, I'll use Rails.
Let's say there's a feature of our app where users can transfer money from one account to another account.
We might have a controller that looks like this:
# app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
def transfer
@source_account = Account.find(params[:id])
@destination_account = Account.find(params[:destination_id])
@amount = params[:amount]
if @source_account.transfer_to(@destination_account, @amount)
flash[:success] = "Successfully transferred #{@amount} to #{@destination_account}"
redirect_to @source_account
else
flash[:error] = "There was a problem transferring money to #{@destination_account}"
render :transfer
end
end
end
In here, we're calling transfer_to
on one of our Account
objects. This method is defined in the Account
model.
# app/models/account.rb
class Account < ActiveRecord::Base
def transfer_to(destination_account, amount)
destination_account.balance += amount
self.balance -= amount
save
end
end
This is a traditional MVC solution - when the transfer
method on the controller is called, we instantiate a couple of model objects and call the behavior defined on the model. Like Robert said, the business logic is split up, and we have to look in a couple different places to understand the code.
The downside of this approach is that we can end up with a lot of behavior defined inside of our models that they don't always need and lacks context. If you've worked on a large project before, it's not long before model files grow to be several hundred or even a couple thousand lines of code because all of the behavior is defined inside of them.
DCI can help solve this problem by giving our data models behavior only within certain contexts that they need to use that behavior. Let's apply this to our bank application example.
In our example, the Context is transferring money. The Data would be the Account
objects. The Behavior is the ability to transfer money. The Interaction is the actual action of transferring money from one account to the other. It could look something like this:
# app/contexts/transferring_money.rb
class TransferringMoney # this is our Context
def initialize(source_account, destination_account) # these objects are our Data
@source_account = source_account
@destination_account = destination_account
assign_roles(source_account)
end
def transfer(amount) # here is the Interaction
@source_account.transfer_to(@destination_account, amount)
end
private
def assign_roles(source_account)
source_account.extend Transferrer
end
module Transferrer
def transfer_to(destination_account, amount)
destination_account.balance += amount
self.balance -= amount
save
end
end
end
As you can see from the example, the Data is given its behavior within the Context at runtime, when we call source_account.extend Transferrer
. The transfer
method is where the Interaction takes place. This prevents us from splitting logic into separate files and it's all contained inside one Context class.
We would call it from the controller like this:
# app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
def transfer
@source_account = Account.find(params[:id])
@destination_account = Account.find(params[:destination_id])
@amount = params[:amount]
if TransferringMoney.new(@source_account, @destination_account).transfer(@amount)
flash[:success] = "Successfully transferred #{@amount} to #{@destination_account}"
redirect_to @source_account
else
flash[:error] = "There was a problem transferring money to #{@destination_account}"
render :transfer
end
end
end
With such a simple example, this might seem like more trouble than it's worth, but when apps grow really large and we add more and more behavior to our models, DCI becomes more useful because we only add behavior to our models within the context of some specific interaction. This way, model behavior is contextual and our controllers and models are a lot smaller.
The key aspects of the DCI architecture are:
I highlighted user's mental model because that's what it's really about. The system architecture should be based on the users thought processes, not the engineers.
Of course the mental model should be discussed and produced by everyone related to the project, but that's rare. Usually the engineers will code according to patterns, decomposition, inheritance, polymorphism, and the part of the code that makes sense to the user is obfuscated behind layers of structure.
This is what DCI tries to remedy. It has run across some resistance over the years, in my opinion because engineers love their structure and classes, so they focus primarily on that.
An illustrating code example would be too lengthy to post here, but the mindset is more important anyway. It's about objects dynamically working together, to solve specific problems. I have made a larger tutorial here, with some code: https://github.com/ciscoheat/haxedci-example
Also, I highly recommend the video A glimpse of Trygve for further explanation, by one of the DCI authors, James Coplien.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With