ActiveDomain
                             An Experimental SOLID ORM




Thursday, December 2, 2010
Me!

                    • Franck Verrot
                    • http://twitter.com/cesariogw
                    • http://github.com/cesario

                    • Currently awesoming at Leadformance

Thursday, December 2, 2010
This is all about

                    • Software architecture
                    • Principles I believe in
                    • Current implementations and their limits
                    • ActiveDomain!

Thursday, December 2, 2010
Software Architecture



Thursday, December 2, 2010
SOLID
                               principles
                    • Single Responsibility
                    • Open/Close
                    • Liskov Substitution
                    • Interface Segregation
                    • Dependency Inversion

Thursday, December 2, 2010
Inspired by DDD

                    • The cover sucks
                    • DDD > Your Mom
                    • Entities,Value Objects
                    • Context Boundaries, ...

Thursday, December 2, 2010
Current
                             implementations


Thursday, December 2, 2010
You do that
                                                 ActiveRecord
     1 class Post                     1 class Post < ActiveRecord::Base
   2   include DataMapper::Resource   2   has_many :comments
   3                                  3 end
   4   property :id, Serial
   5
   6   has n, :comments
   7 end




Thursday, December 2, 2010
Pros                     Cons

        •      Convention                  •   Fine-tuning

        •      High Level of Abstraction   •   Magic can be misleading

        •      Easy to learn               •   Hard to specialize




Thursday, December 2, 2010
You might do that too
                                                   ActiveRecord
     1 class Post                       1 class Post < ActiveRecord::Base
   2    include DataMapper::Resource    2   vestal_version
   3    is :nested_set,                 3   acts_as_paranoid
             :scope => [ :user_id ]     4   set_table_name ‘articles’
   4    property :id,    Serial         5   set_primary_key ‘arti_id’
   5    property :title, String         6   has_many :comments,
   6    has n, :comments                7          :through => :articles,
   7 end                                8          :foreign_key => :id
                                        9   before_validate :do_stuff
                                       10   def do_stuff
                                       11     ...
                                       12   end
                                       13 end
Thursday, December 2, 2010
Architecture at risk
                                  (scary title I know)




Thursday, December 2, 2010
Issues

                    • Separation of concerns
                    • Testing the layers independently
                    • Efficiency ( Transactions, Memory footprint)
                    • Side effects (Lazy loading, non-“PORO”)


Thursday, December 2, 2010
ActiveDomain



Thursday, December 2, 2010
Features

                    • “Plain Old Ruby Object”
                    • Unit of Work
                    • DB agnostic from the start
                    • Could be used to build an AR-like ORM
                    • Identity Mapping

Thursday, December 2, 2010
In Action 1/4
                             describe "User" do
                               it "has many posts" do
                                 user = Entity::User.new :email => 'test@test.com'
                                 user.posts << Entity::Post.new(:title => 'Test post')
                                 user.should have(1).post
                               end
                             end

                             module Entity
                               class User
                                 include ActiveModel::Validations
                                 attr_reader :id
                                 attr_accessor :email, :name, :posts
                                 validates :email, :presence => true

                                 def who
                                   "i am #{name} (#{email})"
                                 end

                                 def initialize args = {}
                                   self.email = args.fetch(:email, nil)
                                   self.name = args.fetch(:name, nil)
                                   self.posts = args.fetch(:posts, [])
                                 end
                               end
                             end

Thursday, December 2, 2010
In Action 2/4

                             module Repository
                               class User < ActiveDomain::Repository::Base
                                 adapter :mysql
                                 table   :users
                                 dsn     'mysql://root:@localhost:3306/test'
                               end
                             end




Thursday, December 2, 2010
In Action 3/4
                             module Mapping
                               class User < ActiveDomain::Mapper::Base
                                 entity       Entity::User
                                 repository   Repository::User

                                 map {
                                   id    :id
                                   email :email
                                 }

                                 associations {
                                   has_many Entity::Post,
                                      :as => :posts,
                                      :conditions => { :id => :author_id }
                                 }
                               end
                             end




Thursday, December 2, 2010
In Action 4/4
                             $EM = ActiveDomain::EntityManager::Base.new do
                               register :users, Mapping::User
                             end

                             user = Entity::User.new :email => 'test@test.com'
                             $EM.users.attach(user) #object marked as new
                             $EM.flush #object persisted

                             user_found = $EM.users.find(:email => "test@test.com")
                             user == user_found # => true




Thursday, December 2, 2010
Is it ready yet?

                    • No!
                    • Associations don’t work yet
                    • Identity mapper in progress
                    • Will be released when it’ll work :)

Thursday, December 2, 2010
Thanks for listening!
                                    Q&A


Thursday, December 2, 2010

Active domain

  • 1.
    ActiveDomain An Experimental SOLID ORM Thursday, December 2, 2010
  • 2.
    Me! • Franck Verrot • http://twitter.com/cesariogw • http://github.com/cesario • Currently awesoming at Leadformance Thursday, December 2, 2010
  • 3.
    This is allabout • Software architecture • Principles I believe in • Current implementations and their limits • ActiveDomain! Thursday, December 2, 2010
  • 4.
  • 5.
    SOLID principles • Single Responsibility • Open/Close • Liskov Substitution • Interface Segregation • Dependency Inversion Thursday, December 2, 2010
  • 6.
    Inspired by DDD • The cover sucks • DDD > Your Mom • Entities,Value Objects • Context Boundaries, ... Thursday, December 2, 2010
  • 7.
    Current implementations Thursday, December 2, 2010
  • 8.
    You do that ActiveRecord 1 class Post 1 class Post < ActiveRecord::Base 2 include DataMapper::Resource 2 has_many :comments 3 3 end 4 property :id, Serial 5 6 has n, :comments 7 end Thursday, December 2, 2010
  • 9.
    Pros Cons • Convention • Fine-tuning • High Level of Abstraction • Magic can be misleading • Easy to learn • Hard to specialize Thursday, December 2, 2010
  • 10.
    You might dothat too ActiveRecord 1 class Post 1 class Post < ActiveRecord::Base 2 include DataMapper::Resource 2 vestal_version 3 is :nested_set, 3 acts_as_paranoid :scope => [ :user_id ] 4 set_table_name ‘articles’ 4 property :id, Serial 5 set_primary_key ‘arti_id’ 5 property :title, String 6 has_many :comments, 6 has n, :comments 7 :through => :articles, 7 end 8 :foreign_key => :id 9 before_validate :do_stuff 10 def do_stuff 11 ... 12 end 13 end Thursday, December 2, 2010
  • 11.
    Architecture at risk (scary title I know) Thursday, December 2, 2010
  • 12.
    Issues • Separation of concerns • Testing the layers independently • Efficiency ( Transactions, Memory footprint) • Side effects (Lazy loading, non-“PORO”) Thursday, December 2, 2010
  • 13.
  • 14.
    Features • “Plain Old Ruby Object” • Unit of Work • DB agnostic from the start • Could be used to build an AR-like ORM • Identity Mapping Thursday, December 2, 2010
  • 15.
    In Action 1/4 describe "User" do   it "has many posts" do     user = Entity::User.new :email => 'test@test.com'     user.posts << Entity::Post.new(:title => 'Test post')     user.should have(1).post   end end module Entity   class User     include ActiveModel::Validations     attr_reader :id     attr_accessor :email, :name, :posts     validates :email, :presence => true     def who       "i am #{name} (#{email})"     end     def initialize args = {}       self.email = args.fetch(:email, nil)       self.name = args.fetch(:name, nil)       self.posts = args.fetch(:posts, [])     end   end end Thursday, December 2, 2010
  • 16.
    In Action 2/4 module Repository   class User < ActiveDomain::Repository::Base     adapter :mysql     table :users     dsn 'mysql://root:@localhost:3306/test'   end end Thursday, December 2, 2010
  • 17.
    In Action 3/4 module Mapping   class User < ActiveDomain::Mapper::Base     entity Entity::User     repository Repository::User     map {       id :id       email :email     }     associations {       has_many Entity::Post, :as => :posts, :conditions => { :id => :author_id }     }   end end Thursday, December 2, 2010
  • 18.
    In Action 4/4 $EM = ActiveDomain::EntityManager::Base.new do   register :users, Mapping::User end user = Entity::User.new :email => 'test@test.com' $EM.users.attach(user) #object marked as new $EM.flush #object persisted user_found = $EM.users.find(:email => "test@test.com") user == user_found # => true Thursday, December 2, 2010
  • 19.
    Is it readyyet? • No! • Associations don’t work yet • Identity mapper in progress • Will be released when it’ll work :) Thursday, December 2, 2010
  • 20.
    Thanks for listening! Q&A Thursday, December 2, 2010