DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Data Engineering
  3. Databases
  4. DCI in Ruby (Part 1)

DCI in Ruby (Part 1)

Victor Savkin user avatar by
Victor Savkin
·
May. 25, 12 · Interview
Like (0)
Save
Tweet
Share
3.51K Views

Join the DZone community and get the full member experience.

Join For Free

DCI (Data Context Interaction) is a new way to look at object-oriented programming. If you’d like to read some theory to see the difference between DCI and traditional OOP there is a nice article covering the topic:

http://www.artima.com/articles/dci_vision.html

And this presentation can be very helpful too:

http://www.infoq.com/presentations/The-DCI-Architecture

It isn’t easy to use DCI in Java as by its nature DCI requires sharing behavior between classes and Java doesn’t provide any decent ways to do it. But many modern languages do including Ruby. To demonstrate how to use mixins in Ruby for implementing DCI I’ll write a simple app.

Requirements:

  • We have users
  • We have companies
  • Users can follow users
  • Users can follow companies
  • Users are entities stored in a database
  • Companies are entities stored in a database

Basically, we have two domain classes: users and companies and use cases: when a user starts following a company and he starts following another user.

Firstly, let’s create our domain classes:

class User
  attr_reader :id, :name, :age, :followers

  def initialize id, name, age, followers = []
    @id, @name, @age, @followers = id, name, age, followers
  end
end

class Company
  attr_reader :id, :name, :country, :followers

  def initialize id, name, country, followers = []
    @id, @name, @country, @followers = id, name, country, followers
  end
end

And a simple Database class representing persistent infrastructure of a real application:

class Database

  USERS = {
    1 => User.new(1, "John", 25),
    2 => User.new(2, "Sam", 26)
  }

  COMPANIES = {
    1 => Company.new(1, "Big Company", "Canada")
  }

  def find_user_by_id id
    USERS[id]
  end

  def find_company_by_id id
    COMPANIES[id]
  end

  def update_user user
    USERS[user.id] = user
  end

  def update_company company
    COMPANIES[company.id] = company
  end
end

Domain objects in DCI aren’t smart. They don’t provide methods for all possible use cases. They don’t interact with each other in complex ways. Instead, they have a set of fields and a bunch of convenient methods to access them.

All our business logic is concentrated in roles. Role is a piece of behavior that we can mix into our domain classes to solve business problems. We’ll need two roles for our toy application:

module Follower
end

module Following
  def add_follower follower
    followers << follower
  end
end

Follower is a marker role. It isn’t necessary to create such a kind of a role but I like to do it as it clarifies my intent.

The only part that left is a context, which will extract domain objects from the database, assign some roles to them and perform a business transaction:

class FollowersListContext

  def initialize db
    @db = db
  end

  def add_follower_to_user following_user_id, follower_user_id
    following = @db.find_user_by_id following_user_id
    follower = @db.find_user_by_id follower_user_id

    following.extend Following
    follower.extend Follower

    following.add_follower follower

    @db.update_user following
  end

  def add_follower_to_company following_company_id, follower_user_id
    following = @db.find_company_by_id following_company_id
    follower = @db.find_user_by_id follower_user_id

    following.extend Following
    follower.extend Follower

    following.add_follower follower

    @db.update_company following
  end
end

#using our context
db = Database.new
context = FollowersListContext.new db
context.add_follower_to_user 1, 2

It may not be the most impressive example as we share only one line of code but it shows how all pieces work together. In a real word example roles will do much more than just adding an item to a collection. As a result this kind of decomposition will allow us to split complex behavior and avoid monster classes with thousands lines of code.

Now let’s take a look at a few ways of doing our code look a bit better.

The simplest way is to define an alias for the extend method. So instead of extending objects we’ll assign roles.

class Object
  define_method :add_role do |role|
    self.extend role
  end
end

following = db.find_company_by_id 1
follower = db.find_user_by_id 2
following.add_role Following
follower.add_role Follower

It’s also not a problem to make it the other way around and make our roles responsible for modifying objects.

class Module
  def played_by obj
    obj.extend self
  end
end

user_a = db.find_company_by_id 1
user_b = db.find_user_by_id 2
perform_operation(Following.played_by(user_a), Follower.played_by(user_b))

If you need more flexibility you can always add a function that will add required roles to an object.

module FollowerRole
  #...
end

def Follower object
  object.extend FollowerRole
end

follower = Follower(user_a)

Another fancy way of doing it is returning an object with a role assigned to it.

following = user_a.in_role Following
follower = user_b.in_role Follower

To make it look a bit more declarative we can specify what roles we want to assign to our domain objects in a array. The using method will iterate over the array and add all necessary roles. So our context will look like this:

using [user_a, :as, Following,
          user_b, :as, Follower]   do |following, follower|

end

To Sum Up

Though Ruby doesn’t have the concept of a role you can easily mimic it with mixins. There are tons of ways to do it, from the most simple ones to robust DSLs.

DCI is definitely gaining some popularity right now, so there is a chance that the next Rails app you’ll work on will use it in some form. But even if it don’t DCI will give you one more way to think about OO design.

Database

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Keep Your Application Secrets Secret
  • What Is the Temporal Dead Zone In JavaScript?
  • Best Practices for Writing Clean and Maintainable Code
  • Integrating AWS Secrets Manager With Spring Boot

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: