Build Complex Domains in Rails

7ac2a66004880abc5032b1894bb12fda?s=47 Mike AbiEzzi
September 25, 2014

Build Complex Domains in Rails

Rails models are simple, but your domain’s models might not be as simple as Rails would like them to be.

Modeling large, complex domains "the Rails way” can cause some serious pain. Ruby and Rails are supposed to make developers happy. Let's not allow “the Rails way” and complex domains to take the “happy” out of ruby development.

Join me as I’ll walk through a set of easy to implement Domain Driven Design (DDD) pointers. The goal is to make sure your model’s business logic stay under control no matter how complex your domain is or gets. Your application will be able to sustain steady growth and dramatically reduce business rule related defects, all the while, staying easy to grok.

I'll walk you through:

How communicating the domain properly will make an imprint on your codebase and product.
How creating boundaries around clusters of models will make your code easier to understand, manage, and interact with.
How immutable objects eliminate unnecessary complexities.
How data store access expresses the domain's intentions.
How to represent natural business transactions between models.

7ac2a66004880abc5032b1894bb12fda?s=128

Mike AbiEzzi

September 25, 2014
Tweet

Transcript

  1. BUILD ! COMPLEX DOMAINS! IN RAILS Mike AbiEzzi

  2. BUILD ! COMPLEX DOMAINS! IN RAILS Mike AbiEzzi

  3. None
  4. WHAT’S A DOMAIN?

  5. WHAT’S A DOMAIN?

  6. WHAT’S A DOMAIN?

  7. WHAT’S A DOMAIN?

  8. WHY DDD?

  9. WHY DDD?

  10. WHY DDD?

  11. WHY DDD?

  12. WHY DDD?

  13. WHY DDD?

  14. WHY DDD?

  15. WHY DDD? software becomes complex quickly

  16. AIM OF THIS TALK ✘ Custom DDD architectures! ✘ DDD

    design patterns! ✘ Advanced DDD topics (e.g. Repository Pattern) (e.g. Bounded Context)
  17. AIM OF THIS TALK ✘ Custom DDD architectures! ✘ DDD

    design patterns! ✘ Advanced DDD topics (e.g. Repository Pattern) (e.g. Bounded Context) custom is expensive!
  18. AIM OF THIS TALK ✓ DDD principles ✓ DDD+Rails w/o

    a heavy investment
  19. AIM OF THIS TALK ✓ DDD principles ✓ DDD+Rails w/o

    a heavy investment we want to hit the ground running!
  20. ▾ app/! ▸ assets/! ▸ controllers/! ▸ helpers/! ▸ mailers/!

    ▸ models/! ▸ views/
  21. 1. Defining the domain! 2. Communicating the domain! 3. Relationships

    between models! 4. Aggregates! 5. Data access! 6. Value Objects! 7. Domain Services
  22. the domain 1

  23. App Store

  24. App Store brainstorm

  25. App Store App brainstorm

  26. Developer 1 n App Store App brainstorm

  27. Developer 1 n App Store App Version 1 n brainstorm

  28. Developer 1 n App Store App Version 1 n 1..6

    Screenshot 1 brainstorm
  29. Developer 1 n App Store Customer App Version 1 n

    1..6 Screenshot 1 brainstorm
  30. Developer 1 n App Store Customer App n n Purchase

    Version 1 n 1..6 Screenshot 1 brainstorm
  31. Developer 1 n App Store Customer App Install n n

    n n Purchase Version 1 n 1..6 Screenshot 1 brainstorm
  32. Developer 1 n App Store Customer App Install n n

    n n Purchase Comment 1 n n 1 Version 1 n 1..6 Screenshot 1 brainstorm
  33. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Version 1 n 1..6 Screenshot 1 refine
  34. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Version 1 n 1..6 Screenshot 1 Developer/ Company refine
  35. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Version 1 n 1..6 Screenshot 1 Developer/ Company Seller refine
  36. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller refine
  37. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller refine
  38. communication 2

  39. COMMUNICATION

  40. COMMUNICATION Domain! Expert

  41. COMMUNICATION Domain! Expert Software! Expert

  42. COMMUNICATION Domain! Expert Software! Expert brainstorm → draw diagrams →

    ! speak out assumptions → let them correct you → refine
  43. RESPONSIBILITIES -- Eric Evans

  44. RESPONSIBILITIES “Domain experts should object to terms or structures that

    are awkward or inadequate to convey domain understanding” -- Eric Evans
  45. RESPONSIBILITIES “Domain experts should object to terms or structures that

    are awkward or inadequate to convey domain understanding” “Developers should watch for ambiguity or inconsistency that will trip up design.” -- Eric Evans
  46. UBIQUITOUS LANGUAGE “a common, rigorous language between developers and users

    […] the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler
  47. UBIQUITOUS LANGUAGE User “a common, rigorous language between developers and

    users […] the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler
  48. UBIQUITOUS LANGUAGE Product! Owner User “a common, rigorous language between

    developers and users […] the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler
  49. UBIQUITOUS LANGUAGE Domain! Expert Product! Owner User “a common, rigorous

    language between developers and users […] the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler
  50. Tester UBIQUITOUS LANGUAGE Domain! Expert Product! Owner User “a common,

    rigorous language between developers and users […] the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler
  51. Tester UBIQUITOUS LANGUAGE Domain! Expert Product! Owner User Designer “a

    common, rigorous language between developers and users […] the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler
  52. Developer Tester UBIQUITOUS LANGUAGE Domain! Expert Product! Owner User Designer

    “a common, rigorous language between developers and users […] the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler
  53. Code Developer Tester UBIQUITOUS LANGUAGE Domain! Expert Product! Owner User

    Designer “a common, rigorous language between developers and users […] the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler
  54. Code Developer Tester UBIQUITOUS LANGUAGE Domain! Expert Product! Owner User

    Designer “a common, rigorous language between developers and users […] the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler Ensure one consistent language
  55. relationships 3

  56. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller relationships
  57. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller class App < ActiveRecord::Base has_many :customers, through: :purchases ... end ! ! class Customer < ActiveRecord::Base has_many :apps, through: :purchases ... end relationships
  58. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller class App < ActiveRecord::Base has_many :customers, through: :purchases ... end ! ! class Customer < ActiveRecord::Base has_many :apps, through: :purchases ... end ✓ relationships
  59. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller class App < ActiveRecord::Base has_many :customers, through: :purchases ... end ! ! class Customer < ActiveRecord::Base has_many :apps, through: :purchases ... end ✓ ✘ relationships
  60. App Store Developer 1 n Customer App Install n n

    n n Purchase Comment 1 n n 1 Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller relationships
  61. 1 n App Store Developer 1 n Customer App Install

    n n Purchase Comment 1 n n 1 Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller relationships
  62. 1 n App Store Developer 1 n Customer App Purchase

    Comment 1 n n 1 Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller relationships
  63. 1 n App Store Developer 1 n Customer App Purchase

    Comment 1 n Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ Company Seller relationships
  64. aggregates 4

  65. aggregates 4

  66. release = Release.new( version_major: 1, version_minor: 1, ...) release.screenshots <<

    [ Screenshot.new(file: "shot1.png"), Screenshot.new(file: "shot2.png")] release.status = :submitted ! app.releases << release Submit a new release of an app
  67. release = Release.new( version_major: 1, version_minor: 1, ...) release.screenshots <<

    [ Screenshot.new(file: "shot1.png"), Screenshot.new(file: "shot2.png")] release.status = :submitted ! app.releases << release Submit a new release of an app
  68. release = Release.new( version_major: 1, version_minor: 1, ...) release.screenshots <<

    [ Screenshot.new(file: "shot1.png"), Screenshot.new(file: "shot2.png")] release.status = :submitted ! app.releases << release Submit a new release of an app
  69. release = Release.new( version_major: 1, version_minor: 1, ...) release.screenshots <<

    [ Screenshot.new(file: "shot1.png"), Screenshot.new(file: "shot2.png")] release.status = :submitted ! app.releases << release Submit a new release of an app
  70. release = Release.new( version_major: 1, version_minor: 1, ...) release.screenshots <<

    [ Screenshot.new(file: "shot1.png"), Screenshot.new(file: "shot2.png")] release.status = :submitted ! app.releases << release Submit a new release of an app
  71. release = Release.new( version_major: 1, version_minor: 1, ...) release.screenshots <<

    [ Screenshot.new(file: "shot1.png"), Screenshot.new(file: "shot2.png")] release.status = :submitted ! app.releases << release A BETTER WAY? Submit a new release of an app
  72. app.submit_release(“1.1.0”, screenshots: ["shot1.png", "shot2.png"]) release = Release.new( version_major: 1, version_minor:

    1, ...) release.screenshots << [ Screenshot.new(file: "shot1.png"), Screenshot.new(file: "shot2.png")] release.status = :submitted ! app.releases << release A BETTER WAY? Submit a new release of an app
  73. app.submit_release(“1.1.0”, screenshots: ["shot1.png", "shot2.png"]) release = Release.new( version_major: 1, version_minor:

    1, ...) release.screenshots << [ Screenshot.new(file: "shot1.png"), Screenshot.new(file: "shot2.png")] release.status = :submitted ! app.releases << release A BETTER WAY? Describe domain behaviors with methods Submit a new release of an app
  74. App 1 Comment 1 n Review! comment! rating n 1..6

    Screenshot 1 AGGREGATE Release! version_number
  75. class App < ActiveRecord::Base ... ! def submit_release ... def

    approve_release ... def flag_for_abuse ... ! def mark_as_staff_favorite ... ! end
  76. class App < ActiveRecord::Base ... ! def submit_release ... def

    approve_release ... def flag_for_abuse ... ! def mark_as_staff_favorite ... ! end Tells a story of how the domain works
  77. data access 5

  78. Data Access App.where( "create_at > ? and purchase_count > ?",

    1.week.ago, 10000).all
  79. Data Access App.where( "create_at > ? and purchase_count > ?",

    1.week.ago, 10000).all
  80. Data Access App.where( "create_at > ? and purchase_count > ?",

    1.week.ago, 10000).all require 'fig_leaf' ! class App < ActiveRecord::Base scope :new_and_noteworthy, -> { where("create_at > ? and purchases > ?", 1.week.ago, 10000) } end Use scopes
  81. Data Access App.where( "create_at > ? and purchase_count > ?",

    1.week.ago, 10000).all require 'fig_leaf' ! class App < ActiveRecord::Base scope :new_and_noteworthy, -> { where("create_at > ? and purchases > ?", 1.week.ago, 10000) } end Use scopes
  82. Data Access App.where( "create_at > ? and purchase_count > ?",

    1.week.ago, 10000).all require 'fig_leaf' ! class App < ActiveRecord::Base scope :new_and_noteworthy, -> { where("create_at > ? and purchases > ?", 1.week.ago, 10000) } end Use scopes
  83. class App < ActiveRecord::Base scope :new_and_noteworthy, ... scope :staff_picks, ...

    scope :most_popular, ... ... ! def submit_release ... def approve_release ... ... end
  84. class App < ActiveRecord::Base scope :new_and_noteworthy, ... scope :staff_picks, ...

    scope :most_popular, ... ... ! def submit_release ... def approve_release ... ... end One expressive point of entry
  85. class App < ActiveRecord::Base scope :new_and_noteworthy, ... scope :staff_picks, ...

    scope :most_popular, ... ... ! def submit_release ... def approve_release ... ... end One expressive point of entry Aggregate roots are the domain’s only ! point of entry for data access.
  86. ▾ app/! ▾ models/! ▾ apps/! app.rb! release.rb! review.rb! screenshot.rb!

    version_number.rb! ▸ customers/! ▸ sellers/
  87. ▾ app/! ▾ models/! ▾ apps/! app.rb! release.rb! review.rb! screenshot.rb!

    version_number.rb! ▸ customers/! ▸ sellers/ Aggregate Aggregate Aggregate
  88. value objects 6

  89. ENTITY VALUE OBJECT

  90. ENTITY VALUE OBJECT a thing

  91. ENTITY VALUE OBJECT a thing describes a thing

  92. ENTITY VALUE OBJECT a thing describes a thing class Customer

    class Name
  93. ENTITY VALUE OBJECT a thing describes a thing class Customer

    class Name Me
 “Mike AbiEzzi”
  94. ENTITY VALUE OBJECT a thing describes a thing class Customer

    class Name Me
 “Mike AbiEzzi” My little cousin
 “Mike AbiEzzi”
  95. ENTITY VALUE OBJECT a thing describes a thing unique independent!

    of attributes class Customer class Name
  96. ENTITY VALUE OBJECT a thing describes a thing unique independent!

    of attributes has a lifecycle class Customer class Name
  97. ENTITY VALUE OBJECT a thing describes a thing can change

    state unique independent! of attributes has a lifecycle class Customer class Name
  98. ENTITY VALUE OBJECT a thing describes a thing can change

    state unique independent! of attributes has a lifecycle class Customer class Name “Mike AbiEzzi” “Mike AbiEzzi”
  99. ENTITY VALUE OBJECT a thing describes a thing can change

    state unique independent! of attributes immutable has a lifecycle class Customer class Name
  100. ENTITY VALUE OBJECT a thing describes a thing can change

    state doesn’t reference anything unique independent! of attributes immutable has a lifecycle class Customer class Name
  101. ENTITY VALUE OBJECT a thing describes a thing can change

    state doesn’t reference anything unique independent! of attributes avoids design complexities immutable has a lifecycle class Customer class Name
  102. App 1 Comment 1 n Review! comment! rating n 1..6

    Screenshot 1 Release! version_number What’s what?
  103. App 1 Comment 1 n Review! comment! rating n 1..6

    Screenshot 1 Entity Release! version_number What’s what?
  104. App 1 Comment 1 n Review! comment! rating n 1..6

    Screenshot 1 Entity Release! version_number Entity What’s what?
  105. App 1 Comment 1 n Review! comment! rating n 1..6

    Screenshot 1 Value Entity Release! version_number Entity What’s what?
  106. App 1 Comment 1 n Review! comment! rating n 1..6

    Screenshot 1 Value Entity Release! version_number Entity What’s what? ?
  107. App 1 Comment 1 n Review! comment! rating n 1..6

    Screenshot 1 Value Entity Release! version_number Entity What’s what? ? Value
  108. class Screenshot attr_reader :file, :position ! def initialize(file, position) @file,

    @position = file, position end ! def ==(other) file == other.file && position == other.position end alias_method :eql?, :== def hash; file.hash ^ position.hash; end end VALUE OBJECT immutable / equality
  109. class Screenshot attr_reader :file, :position ! def initialize(file, position) @file,

    @position = file, position end ! def ==(other) file == other.file && position == other.position end alias_method :eql?, :== def hash; file.hash ^ position.hash; end end VALUE OBJECT immutable / equality
  110. class Screenshot attr_reader :file, :position ! def initialize(file, position) @file,

    @position = file, position end ! def ==(other) file == other.file && position == other.position end alias_method :eql?, :== def hash; file.hash ^ position.hash; end end VALUE OBJECT immutable / equality
  111. class Screenshot attr_reader :file, :position ! def initialize(file, position) @file,

    @position = file, position end ! def ==(other) file == other.file && position == other.position end alias_method :eql?, :== def hash; file.hash ^ position.hash; end end VALUE OBJECT immutable / equality
  112. class Screenshot attr_reader :file, :position ! def initialize(file, position) @file,

    @position = file, position end ! def ==(other) file == other.file && position == other.position end alias_method :eql?, :== def hash; file.hash ^ position.hash; end end VALUE OBJECT immutable / equality
  113. class Screenshot attr_reader :file, :position ! def initialize(file, position) @file,

    @position = file, position end ! def ==(other) file == other.file && position == other.position end alias_method :eql?, :== def hash; file.hash ^ position.hash; end end VALUE OBJECT immutable / equality
  114. class VersionNumber attr_reader :major, :minor, :build ... def next_major_version new

    VersionNumber( major + 1, minor, build) end ! def next_minor_version ... def next_build_version ... end VALUE OBJECT factory methods
  115. class VersionNumber attr_reader :major, :minor, :build ... def next_major_version new

    VersionNumber( major + 1, minor, build) end ! def next_minor_version ... def next_build_version ... end VALUE OBJECT factory methods
  116. 3 ways to persist value objects

  117. 3 ways to persist value objects A. Inline on the

    Entity’s table
  118. 3 ways to persist value objects A. Inline on the

    Entity’s table B. Serialized on the Entity’s table
  119. 3 ways to persist value objects A. Inline on the

    Entity’s table B. Serialized on the Entity’s table C. In its own table
  120. Release! VersionNumber! Entity Value 1 1 A. Inline on the

    Entity’s table releases version_major version_minor version_build …
  121. A. Inline on the Entity’s table class Release < ActiveRecord::Base

    def version_number=(vn) @version_number = vn self[:version_major] = vn.major self[:version_minor] = vn.minor self[:version_build] = vn.build end ! def version_number @version_number ||= new VersionNumber( self[:version_major], ... ) end ! private attr_accessor :version_major, ... ! end
  122. A. Inline on the Entity’s table class Release < ActiveRecord::Base

    def version_number=(vn) @version_number = vn self[:version_major] = vn.major self[:version_minor] = vn.minor self[:version_build] = vn.build end ! def version_number @version_number ||= new VersionNumber( self[:version_major], ... ) end ! private attr_accessor :version_major, ... ! end
  123. A. Inline on the Entity’s table class Release < ActiveRecord::Base

    def version_number=(vn) @version_number = vn self[:version_major] = vn.major self[:version_minor] = vn.minor self[:version_build] = vn.build end ! def version_number @version_number ||= new VersionNumber( self[:version_major], ... ) end ! private attr_accessor :version_major, ... ! end
  124. A. Inline on the Entity’s table class Release < ActiveRecord::Base

    def version_number=(vn) @version_number = vn self[:version_major] = vn.major self[:version_minor] = vn.minor self[:version_build] = vn.build end ! def version_number @version_number ||= new VersionNumber( self[:version_major], ... ) end ! private attr_accessor :version_major, ... ! end
  125. Release! Screenshot! 1 1..6 B. Serialized on the Entity’s table

    releases … screenshots …
  126. require 'oj' ! class Release < ActiveRecord::Base ! def screenshots=(screenshots)

    @screenshots = screenshots self[:screenshots] = Oj.dump(screenshots) end ! def screenshots @screenshots ||= Oj.load(self[:screenshots]) end ! end B. Serialized on the Entity’s table
  127. require 'oj' ! class Release < ActiveRecord::Base ! def screenshots=(screenshots)

    @screenshots = screenshots self[:screenshots] = Oj.dump(screenshots) end ! def screenshots @screenshots ||= Oj.load(self[:screenshots]) end ! end B. Serialized on the Entity’s table
  128. require 'oj' ! class Release < ActiveRecord::Base ! def screenshots=(screenshots)

    @screenshots = screenshots self[:screenshots] = Oj.dump(screenshots) end ! def screenshots @screenshots ||= Oj.load(self[:screenshots]) end ! end B. Serialized on the Entity’s table
  129. require 'oj' ! class Release < ActiveRecord::Base ! def screenshots=(screenshots)

    @screenshots = screenshots self[:screenshots] = Oj.dump(screenshots) end ! def screenshots @screenshots ||= Oj.load(self[:screenshots]) end ! end B. Serialized on the Entity’s table
  130. Release! Review! 1 n C. In its own table releases

    … reviews release_id …
  131. C. In its own table class Review < ActiveRecord::Base after_save

    { |record| immutable!(record) } after_find { |record| immutable!(record) } ! def immutable!(record) record.readonly! attributes.each do |attr, _| record.define_singleton_method :"#{attr}=" do |_| raise "readonly" end end end ! def ==(other) ... alias_method :eql?, :== def hash ... end
  132. C. In its own table class Review < ActiveRecord::Base after_save

    { |record| immutable!(record) } after_find { |record| immutable!(record) } ! def immutable!(record) record.readonly! attributes.each do |attr, _| record.define_singleton_method :"#{attr}=" do |_| raise "readonly" end end end ! def ==(other) ... alias_method :eql?, :== def hash ... end
  133. C. In its own table class Review < ActiveRecord::Base after_save

    { |record| immutable!(record) } after_find { |record| immutable!(record) } ! def immutable!(record) record.readonly! attributes.each do |attr, _| record.define_singleton_method :"#{attr}=" do |_| raise "readonly" end end end ! def ==(other) ... alias_method :eql?, :== def hash ... end
  134. C. In its own table class Review < ActiveRecord::Base after_save

    { |record| immutable!(record) } after_find { |record| immutable!(record) } ! def immutable!(record) record.readonly! attributes.each do |attr, _| record.define_singleton_method :"#{attr}=" do |_| raise "readonly" end end end ! def ==(other) ... alias_method :eql?, :== def hash ... end
  135. C. In its own table class Review < ActiveRecord::Base after_save

    { |record| immutable!(record) } after_find { |record| immutable!(record) } ! def immutable!(record) record.readonly! attributes.each do |attr, _| record.define_singleton_method :"#{attr}=" do |_| raise "readonly" end end end ! def ==(other) ... alias_method :eql?, :== def hash ... end
  136. C. In its own table class Review < ActiveRecord::Base after_save

    { |record| immutable!(record) } after_find { |record| immutable!(record) } ! def immutable!(record) record.readonly! attributes.each do |attr, _| record.define_singleton_method :"#{attr}=" do |_| raise "readonly" end end end ! def ==(other) ... alias_method :eql?, :== def hash ... end
  137. C. In its own table class Review < ActiveRecord::Base after_save

    { |record| immutable!(record) } after_find { |record| immutable!(record) } ! def immutable!(record) record.readonly! attributes.each do |attr, _| record.define_singleton_method :"#{attr}=" do |_| raise "readonly" end end end ! def ==(other) ... alias_method :eql?, :== def hash ... end
  138. domain services 7

  139. Gift an App SERVICE

  140. Gift an App SERVICE 1. Charge the gifter that’s purchasing

    the app.
  141. Gift an App SERVICE 1. Charge the gifter that’s purchasing

    the app. 2. Assign access rights to the giftee receiving the app.
  142. Gift an App SERVICE 1. Charge the gifter that’s purchasing

    the app. 2. Assign access rights to the giftee receiving the app. Facilitates a transaction between two aggregates
  143. Purchase 1 n Customer

  144. Purchase 1 n Customer AccessRight 1 n

  145. class GiftApp def self.execute(app, gifter, giftee) Purchase.create( app: app, customer:

    gifter, ...) AccessRight.create( app: app, customer: giftee, purchase: purchase, ...) end end Purchase 1 n Customer AccessRight 1 n
  146. class GiftApp def self.execute(app, gifter, giftee) Purchase.create( app: app, customer:

    gifter, ...) AccessRight.create( app: app, customer: giftee, purchase: purchase, ...) end end Purchase 1 n Customer AccessRight 1 n
  147. class GiftApp def self.execute(app, gifter, giftee) Purchase.create( app: app, customer:

    gifter, ...) AccessRight.create( app: app, customer: giftee, purchase: purchase, ...) end end Purchase 1 n Customer AccessRight 1 n
  148. class GiftApp def self.execute(app, gifter, giftee) Purchase.create( app: app, customer:

    gifter, ...) AccessRight.create( app: app, customer: giftee, purchase: purchase, ...) end end Purchase 1 n Customer AccessRight 1 n
  149. class GiftApp def self.execute(app, gifter, giftee) Purchase.create( app: app, customer:

    gifter, ...) AccessRight.create( app: app, customer: giftee, purchase: purchase, ...) end end Purchase 1 n Customer AccessRight 1 n
  150. ▾ app/! ▸ models/! ▾ services/! gift_app.rb! refund_purchase.rb! purchase_app.rb! ...

  151. IN SUMMARY

  152. Continuously refine the domain with a domain expert 1

  153. Insist on having a ubiquitous language for seamless communication 2

  154. Constrain relationships to only what the domain needs 3

  155. Create aggregates that express domain concepts and manage complexity 4

  156. Express the domain’s intent through! well defined data access 5

  157. Create value objects to eliminate unnecessary complexity 6

  158. Create domain services to express transactions ! between aggregates 7

  159. ▾ app/! ▸ assets/! ▸ controllers/! ▸ helpers/! ▸ mailers/!

    ▸ models/! ▸ views/
  160. ▾ app/! ▾ models/! ▸ apps/! app.rb! ...! ▸ customers/!

    ▸ sellers/! ▸ ...! ▸ services/! gift_app.rb! refund_purchase.rb! ...
  161. ▾ app/! ▾ models/! ▸ apps/! app.rb! ...! ▸ customers/!

    ▸ sellers/! ▸ ...! ▸ services/! gift_app.rb! refund_purchase.rb! ... aggregates
  162. ▾ app/! ▾ models/! ▸ apps/! app.rb! ...! ▸ customers/!

    ▸ sellers/! ▸ ...! ▸ services/! gift_app.rb! refund_purchase.rb! ... aggregates entry point to domain behaviors & ! data retrieval
  163. ▾ app/! ▾ models/! ▸ apps/! app.rb! ...! ▸ customers/!

    ▸ sellers/! ▸ ...! ▸ services/! gift_app.rb! refund_purchase.rb! ... aggregates cross aggregate transactions entry point to domain behaviors & ! data retrieval
  164. Thanks for Listening!

  165. Thanks for Listening! We’re hiring!

  166. Thanks for Listening! www.virtual-genius.com Special thanks to We’re hiring!

  167. @mjezzi www.mikeabiezzi.com

  168. @mjezzi www.mikeabiezzi.com /#mentorship