SlideShare ist ein Scribd-Unternehmen logo
1 von 56
Downloaden Sie, um offline zu lesen
Unbreakable
Domain Models
Mathias Verraes
FrOSCon
Sankt-Augustin, DE
August 24, 2013
@mathiasverraes
http://verraes.net
I'm an independent
consultant.
I build enterprise
web applications.
I help teams
escape from
survival mode.
Cofounder of the
Belgian
Domain-Driven
Design community
http://domaindriven.be
@DDDBE
Modellathon on
September 3rd, 2013
Ghent
Domain
Problem Space
Domain Model
Solution Space
(Data Model
The model’s state)
Protect your invariants
The domain expert says
“A customer must
always have an
email address.”
* Could be different for your domain
** All examples are simplified
class CustomerTest extends PHPUnit_Framework_TestCase
{
/** @test */
public function should_always_have_an_email()
{
$customer = new Customer();
assertThat(
$customer->getEmail(),
equalTo('jim@example.com')
);
}
}
Test fails
class CustomerTest extends PHPUnit_Framework_TestCase
{
/** @test */
public function should_always_have_an_email()
{
$customer = new Customer();
$customer->setEmail('jim@example.com');
assertThat(
$customer->getEmail(),
equalTo('jim@example.com')
);
}
}
Test passes
class CustomerTest extends PHPUnit_Framework_TestCase
{
/** @test */
public function should_always_have_an_email()
{
$customer = new Customer();
assertThat(
$customer->getEmail(),
equalTo(‘jim@example.com')
);
$customer->setEmail(‘jim@example.com’);
}
}
Test fails
class Customer
{
private $email;
public function __construct($email)
{
$this->email = $email;
}
public function getEmail()
{
return $this->email;
}
}
Test passes
class CustomerTest extends PHPUnit_Framework_TestCase
{
/** @test */
public function should_always_have_an_email()
{
$customer = new Customer(‘jim@example.com’);
assertThat(
$customer->getEmail(),
equalTo(‘jim@example.com')
);
}
}
Test passes
Use objects as
consistency boundaries
class ProspectiveCustomer
{
//...
/** @return PayingCustomer */
public function convertToPayingCustomer(){
}
}
class PayingCustomer { ... }
Make the implicit
explicit
The domain expert meant
“A customer must
always have a valid
email address.”
$customerValidator = new CustomerValidator;
if($customerValidator->isValid($customer)){
// ...
}
class CustomerTest extends PHPUnit_Framework_TestCase
{
/** @test */
public function should_always_have_a_valid_email()
{
$this->setExpectedException(
'InvalidArgumentException'
);
new Customer('malformed@email');
}
}
Test fails
class Customer
{
public function __construct($email)
{
if( /* ugly regex here */) {
throw new InvalidArgumentException();
}
$this->email = $email;
}
}
Test passes
Violates
Single Responsibility
Principle
class Email
{
private $email;
public function __construct($email)
{
if( /* ugly regex here */) {
throw new InvalidArgumentException();
}
$this->email = $email;
}
public function __toString()
{
return $this->email;
}
}
Test passes
class Customer
{
/** @var Email */
private $email;
public function __construct(Email $email)
{
$this->email = $email;
}
}
Test passes
class CustomerTest extends PHPUnit_Framework_TestCase
{
/** @test */
public function should_always_have_a_valid_email()
{
$this->setExpectedException(
‘InvalidArgumentException’
);
new Customer(new Email(‘malformed@email’));
}
}
Test passes
Encapsulate
state and behavior
with Value Objects
The domain expert says
“A customer
orders products
and pays for them.”
$order = new Order;
$order->setCustomer($customer);
$order->setProducts($products);
$order->setStatus(Order::UNPAID);
// ...
$order->setPaidAmount(500);
$order->setPaidCurrency(‘EUR’);
$order->setStatus(Order::PAID);
$order = new Order;
$order->setCustomer($customer);
$order->setProducts($products);
$order->setStatus(
new PaymentStatus(PaymentStatus::UNPAID)
);
$order->setPaidAmount(500);
$order->setPaidCurrency(‘EUR’);
$order->setStatus(
new PaymentStatus(PaymentStatus::PAID)
);
$order = new Order;
$order->setCustomer($customer);
$order->setProducts($products);
$order->setStatus(
new PaymentStatus(PaymentStatus::UNPAID)
);
$order->setPaidMonetary(
new Money(500, new Currency(‘EUR’))
);
$order->setStatus(
new PaymentStatus(PaymentStatus::PAID)
);
$order = new Order($customer, $products);
// set PaymentStatus in Order::__construct()
$order->setPaidMonetary(
new Money(500, new Currency(‘EUR’))
);
$order->setStatus(
new PaymentStatus(PaymentStatus::PAID)
);
$order = new Order($customer, $products);
$order->pay(
new Money(500, new Currency(‘EUR’))
);
// set PaymentStatus in Order#pay()
Encapsulate operations
$order = $customer->order($products);
$customer->pay(
$order,
new Money(500, new Currency(‘EUR’))
);
The domain expert says
“Premium customers
get special offers.”
if($customer->isPremium()) {
// send special offer
}
The domain expert says
“Order 3 times
to become a
premium customer.”
interface CustomerSpecification
{
/** @return bool */
public function isSatisfiedBy(Customer $customer);
}
class CustomerIsPremium implements CustomerSpecification
{
private $orderRepository;
public function __construct(
OrderRepository $orderRepository
) {...}
/** @return bool */
public function isSatisfiedBy(Customer $customer)
{
$count = $this->orderRepository->countFor($customer);
return $count >= 3;
}
}
$customerIsPremium = new CustomerIsPremium($orderRepository)
if($customerIsPremium->isSatisfiedBy($customer)) {
// send special offer
}
$customerIsPremium = new CustomerIsPremium;
$aCustomerWith2Orders = ...
$aCustomerWith3Orders = ...
assertFalse(
$customerIsPremium->isSatisfiedBy($aCustomerWith2Orders)
);
assertTrue(
$customerIsPremium->isSatisfiedBy($aCustomerWith3Orders)
);
The domain expert says
“Different rules apply
for different tenants.”
interface CustomerIsPremium
extends CustomerSpecification
class CustomerWith3OrdersIsPremium
implements CustomerIsPremium
class CustomerWith500EuroTotalIsPremium
implements CustomerIsPremium
class CustomerWhoBoughtLuxuryProductsIsPremium
implements CustomerIsPremium
...
class SpecialOfferSender
{
private $customerIsPremium;
public function __construct(
CustomerIsPremium $customerIsPremium) {...}
public function sendOffersTo(Customer $customer)
{
if($this->customerIsPremium->isSatisfiedBy(
$customer
))
{
// send offers...
}
}
}
<!-- if you load services_amazon.xml: -->
<service id="customer.is.premium"
class="CustomerWith500EuroTotalIsPremium">
<!-- if you load services_ebay.xml: -->
<service id="customer.is.premium"
class="CustomerWith3OrdersIsPremium">
<!-- elsewhere -->
<service
id=”special.offer.sender”
class=”SpecialOfferSender”>
<argument type=”service” id=”customer.is.premium”/>
</service>
Use specifications to
encapsulate rules
about object selection
The domain expert says
“Get a list of
all premium
customers.”
interface CustomerRepository
{
public function add(Customer $customer);
public function remove(Customer $customer);
/** @return Customer */
public function find(CustomerId $customerId);
/** @return Customer[] */
public function findAll();
/** @return Customer[] */
public function findRegisteredIn(Year $year);
}
interface CustomerRepository
{
/** @return Customer[] */
public function findSatisfying(
CustomerSpecification $customerSpecification
);
}
// generalized:
$objects = $repository->findSatisfying($specification);
class DbCustomerRepository implements CustomerRepository
{
/** @return Customer[] */
public function findSatisfying(
CustomerSpecification $customerSpecification)
{
// filter Customers (see next slide)
}
}
// class DbCustomerRepository
public function findSatisfying($specification)
{
$foundCustomers = array();
foreach($this->findAll() as $customer) {
if($specification->isSatisfiedBy($customer)) {
$foundCustomers[] = $customer;
}
}
return $foundCustomers;
}
class CustomerWith3OrdersIsPremium
implements CustomerSpecification
{
public function asSql() {
return ‘SELECT * FROM Customer...’;
}
}
// class DbCustomerRepository
public function findSatisfying($specification)
{
return $this->db->query($specification->asSql());
}
Use double dispatch
to preserve encapsulation
$expectedCustomers = // filtered using isSatisfiedBy
$actualCustomers =
$repository->findSatisfying($specification);
assertThat($expectedCustomers, equalTo($actualCustomers));
Test by comparing
different representations
Protect your invariants
Objects as
consistency boundaries
Encapsulate
state and behavior
More? google for:
Eric Evans
Vaugh Vernon
Martin Fowler
Greg Young
Udi Dahan
Sandro Marcuso
Yves Reynhout
Szymon Pobiega
Alberto Brandolini
...
Thanks! Questions?
@mathiasverraes
http://verraes.net
https://joind.in/9020

Weitere ähnliche Inhalte

Mehr von Mathias Verraes

Small Controlled Experiments
Small Controlled ExperimentsSmall Controlled Experiments
Small Controlled ExperimentsMathias Verraes
 
DDD Basics: Bounded Contexts, Modelling - Kortrijk Edition
DDD Basics: Bounded Contexts, Modelling - Kortrijk EditionDDD Basics: Bounded Contexts, Modelling - Kortrijk Edition
DDD Basics: Bounded Contexts, Modelling - Kortrijk EditionMathias Verraes
 
Why Domain-Driven Design Matters
Why Domain-Driven Design MattersWhy Domain-Driven Design Matters
Why Domain-Driven Design MattersMathias Verraes
 
Practical Event Sourcing
Practical Event SourcingPractical Event Sourcing
Practical Event SourcingMathias Verraes
 
Unbreakable Domain Models PHPUK 2014 London
Unbreakable Domain Models PHPUK 2014 LondonUnbreakable Domain Models PHPUK 2014 London
Unbreakable Domain Models PHPUK 2014 LondonMathias Verraes
 
Domain-Driven Design Basics
Domain-Driven Design BasicsDomain-Driven Design Basics
Domain-Driven Design BasicsMathias Verraes
 
Model Storming Workshop PHP Benelux 2014
Model Storming Workshop PHP Benelux 2014Model Storming Workshop PHP Benelux 2014
Model Storming Workshop PHP Benelux 2014Mathias Verraes
 
Fighting Bottlencks with CQRS - ResearchGate
Fighting Bottlencks with CQRS - ResearchGateFighting Bottlencks with CQRS - ResearchGate
Fighting Bottlencks with CQRS - ResearchGateMathias Verraes
 

Mehr von Mathias Verraes (10)

Modelling Heuristics
Modelling HeuristicsModelling Heuristics
Modelling Heuristics
 
Small Controlled Experiments
Small Controlled ExperimentsSmall Controlled Experiments
Small Controlled Experiments
 
Managed Technical Debt
Managed Technical DebtManaged Technical Debt
Managed Technical Debt
 
DDD Basics: Bounded Contexts, Modelling - Kortrijk Edition
DDD Basics: Bounded Contexts, Modelling - Kortrijk EditionDDD Basics: Bounded Contexts, Modelling - Kortrijk Edition
DDD Basics: Bounded Contexts, Modelling - Kortrijk Edition
 
Why Domain-Driven Design Matters
Why Domain-Driven Design MattersWhy Domain-Driven Design Matters
Why Domain-Driven Design Matters
 
Practical Event Sourcing
Practical Event SourcingPractical Event Sourcing
Practical Event Sourcing
 
Unbreakable Domain Models PHPUK 2014 London
Unbreakable Domain Models PHPUK 2014 LondonUnbreakable Domain Models PHPUK 2014 London
Unbreakable Domain Models PHPUK 2014 London
 
Domain-Driven Design Basics
Domain-Driven Design BasicsDomain-Driven Design Basics
Domain-Driven Design Basics
 
Model Storming Workshop PHP Benelux 2014
Model Storming Workshop PHP Benelux 2014Model Storming Workshop PHP Benelux 2014
Model Storming Workshop PHP Benelux 2014
 
Fighting Bottlencks with CQRS - ResearchGate
Fighting Bottlencks with CQRS - ResearchGateFighting Bottlencks with CQRS - ResearchGate
Fighting Bottlencks with CQRS - ResearchGate
 

Kürzlich hochgeladen

TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Principled Technologies
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024The Digital Insurer
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyKhushali Kathiriya
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 

Kürzlich hochgeladen (20)

TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 

Unbreakable Domain Models - FrOSCon 2013