1. Defining the domain!
2. Communicating the domain!
3. Relationships between models!
4. Aggregates!
5. Data access!
6. Value Objects!
7. Domain Services
Slide 22
Slide 22 text
the domain
1
Slide 23
Slide 23 text
App Store
Slide 24
Slide 24 text
App Store
brainstorm
Slide 25
Slide 25 text
App Store
App
brainstorm
Slide 26
Slide 26 text
Developer 1 n
App Store
App
brainstorm
Slide 27
Slide 27 text
Developer 1 n
App Store
App
Version
1
n
brainstorm
Slide 28
Slide 28 text
Developer 1 n
App Store
App
Version
1
n
1..6
Screenshot
1
brainstorm
Slide 29
Slide 29 text
Developer 1 n
App Store
Customer
App
Version
1
n
1..6
Screenshot
1
brainstorm
Slide 30
Slide 30 text
Developer 1 n
App Store
Customer
App
n
n
Purchase
Version
1
n
1..6
Screenshot
1
brainstorm
Slide 31
Slide 31 text
Developer 1 n
App Store
Customer
App
Install
n
n
n
n
Purchase
Version
1
n
1..6
Screenshot
1
brainstorm
Slide 32
Slide 32 text
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
Slide 33
Slide 33 text
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
Slide 34
Slide 34 text
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
Slide 35
Slide 35 text
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
Slide 36
Slide 36 text
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
Slide 37
Slide 37 text
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
Slide 38
Slide 38 text
communication
2
Slide 39
Slide 39 text
COMMUNICATION
Slide 40
Slide 40 text
COMMUNICATION
Domain!
Expert
Slide 41
Slide 41 text
COMMUNICATION
Domain!
Expert
Software!
Expert
Slide 42
Slide 42 text
COMMUNICATION
Domain!
Expert
Software!
Expert
brainstorm → draw diagrams → !
speak out assumptions → let them correct you → refine
Slide 43
Slide 43 text
RESPONSIBILITIES
-- Eric Evans
Slide 44
Slide 44 text
RESPONSIBILITIES
“Domain experts
should object to terms
or structures that are
awkward or inadequate
to convey domain
understanding”
-- Eric Evans
Slide 45
Slide 45 text
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
Slide 46
Slide 46 text
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
Slide 47
Slide 47 text
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
Slide 48
Slide 48 text
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
Slide 49
Slide 49 text
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
Slide 50
Slide 50 text
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
Slide 51
Slide 51 text
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
Slide 52
Slide 52 text
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
Slide 53
Slide 53 text
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
Slide 54
Slide 54 text
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
Slide 55
Slide 55 text
relationships
3
Slide 56
Slide 56 text
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
Slide 57
Slide 57 text
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
Slide 58
Slide 58 text
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
Slide 59
Slide 59 text
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
Slide 60
Slide 60 text
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
Slide 61
Slide 61 text
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
Slide 62
Slide 62 text
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
Slide 63
Slide 63 text
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
Slide 64
Slide 64 text
aggregates
4
Slide 65
Slide 65 text
aggregates
4
Slide 66
Slide 66 text
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
Slide 67
Slide 67 text
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
Slide 68
Slide 68 text
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
Slide 69
Slide 69 text
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
Slide 70
Slide 70 text
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
Slide 71
Slide 71 text
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
Slide 72
Slide 72 text
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
Slide 73
Slide 73 text
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
Slide 74
Slide 74 text
App
1
Comment
1
n
Review!
comment!
rating
n
1..6
Screenshot
1
AGGREGATE
Release!
version_number
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
Slide 77
Slide 77 text
data access
5
Slide 78
Slide 78 text
Data Access
App.where(
"create_at > ? and purchase_count > ?",
1.week.ago, 10000).all
Slide 79
Slide 79 text
Data Access
App.where(
"create_at > ? and purchase_count > ?",
1.week.ago, 10000).all
Slide 80
Slide 80 text
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
Slide 81
Slide 81 text
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
Slide 82
Slide 82 text
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
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
Slide 85
Slide 85 text
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.
ENTITY VALUE OBJECT
a thing describes a thing
class Customer class Name
Slide 93
Slide 93 text
ENTITY VALUE OBJECT
a thing describes a thing
class Customer class Name
Me
“Mike AbiEzzi”
Slide 94
Slide 94 text
ENTITY VALUE OBJECT
a thing describes a thing
class Customer class Name
Me
“Mike AbiEzzi”
My little
cousin
“Mike AbiEzzi”
Slide 95
Slide 95 text
ENTITY VALUE OBJECT
a thing describes a thing
unique independent!
of attributes
class Customer class Name
Slide 96
Slide 96 text
ENTITY VALUE OBJECT
a thing describes a thing
unique independent!
of attributes
has a lifecycle
class Customer class Name
Slide 97
Slide 97 text
ENTITY VALUE OBJECT
a thing describes a thing
can change state
unique independent!
of attributes
has a lifecycle
class Customer class Name
Slide 98
Slide 98 text
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”
Slide 99
Slide 99 text
ENTITY VALUE OBJECT
a thing describes a thing
can change state
unique independent!
of attributes
immutable
has a lifecycle
class Customer class Name
Slide 100
Slide 100 text
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
Slide 101
Slide 101 text
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
Slide 102
Slide 102 text
App
1
Comment
1
n
Review!
comment!
rating
n
1..6
Screenshot
1 Release!
version_number
What’s what?
Slide 103
Slide 103 text
App
1
Comment
1
n
Review!
comment!
rating
n
1..6
Screenshot
1
Entity
Release!
version_number
What’s what?
Slide 104
Slide 104 text
App
1
Comment
1
n
Review!
comment!
rating
n
1..6
Screenshot
1
Entity
Release!
version_number
Entity
What’s what?
Slide 105
Slide 105 text
App
1
Comment
1
n
Review!
comment!
rating
n
1..6
Screenshot
1
Value
Entity
Release!
version_number
Entity
What’s what?
Slide 106
Slide 106 text
App
1
Comment
1
n
Review!
comment!
rating
n
1..6
Screenshot
1
Value
Entity
Release!
version_number
Entity
What’s what?
?
Slide 107
Slide 107 text
App
1
Comment
1
n
Review!
comment!
rating
n
1..6
Screenshot
1
Value
Entity
Release!
version_number
Entity
What’s what?
?
Value
Slide 108
Slide 108 text
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
Slide 109
Slide 109 text
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
Slide 110
Slide 110 text
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
Slide 111
Slide 111 text
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
Slide 112
Slide 112 text
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
Slide 113
Slide 113 text
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
Slide 114
Slide 114 text
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
Slide 115
Slide 115 text
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
Slide 116
Slide 116 text
3 ways to persist value objects
Slide 117
Slide 117 text
3 ways to persist value objects
A. Inline on the Entity’s table
Slide 118
Slide 118 text
3 ways to persist value objects
A. Inline on the Entity’s table
B. Serialized on the Entity’s table
Slide 119
Slide 119 text
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
Slide 120
Slide 120 text
Release!
VersionNumber!
Entity
Value
1
1
A. Inline on the Entity’s table
releases
version_major
version_minor
version_build
…
Slide 121
Slide 121 text
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
Slide 122
Slide 122 text
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
Slide 123
Slide 123 text
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
Slide 124
Slide 124 text
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
Slide 125
Slide 125 text
Release!
Screenshot!
1
1..6
B. Serialized on the Entity’s table
releases
…
screenshots
…
Slide 126
Slide 126 text
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
Slide 127
Slide 127 text
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
Slide 128
Slide 128 text
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
Slide 129
Slide 129 text
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
Slide 130
Slide 130 text
Release!
Review!
1
n
C. In its own table
releases
…
reviews
release_id
…
Slide 131
Slide 131 text
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
Slide 132
Slide 132 text
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
Slide 133
Slide 133 text
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
Slide 134
Slide 134 text
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
Slide 135
Slide 135 text
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
Slide 136
Slide 136 text
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
Slide 137
Slide 137 text
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
Slide 138
Slide 138 text
domain services
7
Slide 139
Slide 139 text
Gift an App SERVICE
Slide 140
Slide 140 text
Gift an App SERVICE
1. Charge the gifter that’s purchasing
the app.
Slide 141
Slide 141 text
Gift an App SERVICE
1. Charge the gifter that’s purchasing
the app.
2. Assign access rights to the giftee
receiving the app.
Slide 142
Slide 142 text
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
Slide 143
Slide 143 text
Purchase
1
n
Customer
Slide 144
Slide 144 text
Purchase
1
n
Customer
AccessRight
1
n
Slide 145
Slide 145 text
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
Slide 146
Slide 146 text
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
Slide 147
Slide 147 text
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
Slide 148
Slide 148 text
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
Slide 149
Slide 149 text
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