SlideShare ist ein Scribd-Unternehmen logo
1 von 59
Downloaden Sie, um offline zu lesen
Unbreakable
Domain Models
@mathiasverraes
A Map of the World
London
3h train rides

Kortrijk, Belgium

Paris

Amsterdam
All models are wrong,
but some are useful.
I’m Mathias Verraes
I'm an independent
consultant.
I help teams build
enterprise web
applications.
Blog
verraes.net
!

Podcast with @everzet
elephantintheroom.io
!

DDD in PHP
bit.ly/dddinphp
Domain
Problem Space
Domain Model
Solution Space
Data Model ~= Structural Model ~= State
!

Domain Model ~= Behavioral Model
!
Protect your invariants
The domain expert says

“A customer must
always have an
email address.”
* Could be different for your domain
** All examples are simpliïŹed
Test fails

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 passes

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 fails

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’);!
!

}!
}
final 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') !
);!
}!
}
Use objects as
consistency boundaries
final class ProspectiveCustomer !
{!
public function __construct()!
{!
// no email!
}!
}!
!

final class PayingCustomer !
{ !
public function __construct($email)!
{!
$this->email = $email;!
}!
}
Make the implicit
explicit
final class ProspectiveCustomer !
{!
/** @return PayingCustomer */!
public function convertToPayingCustomer($email)!
{ !
//...!
}!
}!
!

final class PayingCustomer !
{ !
//...!
}
The domain expert meant

“A customer must
always have a valid
email address.”
$customerValidator = new CustomerValidator;!
if($customerValidator->isValid($customer)){!
// ...!
}
Test fails

class CustomerTest extends PHPUnit_Framework_TestCase!
{!
/** @test */!
public function should_always_have_a_valid_email()!
{!
!

$this->setExpectedException(!
'InvalidArgumentException'!
);!
!

new Customer('malformed@email');!
!

}!
}
Test passes

final class Customer !
{!
public function __construct($email)!
{!
if( /* boring validation stuff */) {!
throw new InvalidArgumentException();!
}!
$this->email = $email;!
}!
}
Violates
Single Responsibility
Principle
Test passes

final class Email!
{!
private $email;!
!

public function __construct($email)!
{!
if( /* boring validation stuff */) {!
throw new InvalidArgumentException();!
}!
$this->email = $email;!
}!
!

public function __toString() !
{!
return $this->email;!
} !
}
Test passes

final 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’));!
!

}!
}
Entity
!

Equality by
Identity
Lifecycle
Mutable

Value
Object
Equality by
Value
!

Immutable
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->payFor(!
$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!
!

final class CustomerWith3OrdersIsPremium !
implements CustomerIsPremium!
!

final class CustomerWith500EuroTotalIsPremium!
implements CustomerIsPremium!
!

final class CustomerWhoBoughtLuxuryProductsIsPremium!
implements CustomerIsPremium!
!

...!
final 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 speciïŹcations 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);!
}!
Use repositories to
create the illusion of
in-memory collections
interface CustomerRepository!
{!
!

/** @return Customer[] */!
public function findSatisfying(!
CustomerSpecification $customerSpecification!
);!
!

}!
!
!

// generalized:!
$objects = $repository->findSatisfying($specification);!
class DbCustomerRepository implements CustomerRepository!
{!
/** @return Customer[] */!
public function findSatisfying(!
CustomerSpecification $specification) !
{!
!

return array_filter(!
$this->findAll(),!
function(Customer $customer) use($specification) {!
return $specification->isSatisfiedBy($customer);!
}
!
);!
!

}!
}!
final 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 = array_filter(!
$repository->findAll(),!
// filter
!
);!
!

$actualCustomers = !
$repository->findSatisfying($specification);!
!

assertThat($expectedCustomers, equalTo($actualCustomers));
Test by comparing
different representations
of the same rule
Protect your invariants
!

Objects as
consistency boundaries
!

Encapsulate
state and behavior
Thanks! Questions?
I ♄ Feedback	

joind.in/10690
!

Blog, Slides, other talks:	

verraes.net	

@mathiasverraes

Weitere Àhnliche Inhalte

Was ist angesagt?

Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDDKonstantin Kudryashov
 
A Z Introduction To Ruby On Rails
A Z Introduction To Ruby On RailsA Z Introduction To Ruby On Rails
A Z Introduction To Ruby On Railsrailsconf
 
A-Z Intro To Rails
A-Z Intro To RailsA-Z Intro To Rails
A-Z Intro To RailsRobert Dempsey
 
Best practices for crafting high quality PHP apps - PHP UK 2019
Best practices for crafting high quality PHP apps - PHP UK 2019Best practices for crafting high quality PHP apps - PHP UK 2019
Best practices for crafting high quality PHP apps - PHP UK 2019James Titcumb
 
Crafting Quality PHP Applications (ConFoo YVR 2017)
Crafting Quality PHP Applications (ConFoo YVR 2017)Crafting Quality PHP Applications (ConFoo YVR 2017)
Crafting Quality PHP Applications (ConFoo YVR 2017)James Titcumb
 
iOS best practices
iOS best practicesiOS best practices
iOS best practicesMaxim Vialyx
 
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)James Titcumb
 
Javascript
JavascriptJavascript
JavascriptAditya Gaur
 
Domain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDomain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDeclan Whelan
 
Advisor Jumpstart: JavaScript
Advisor Jumpstart: JavaScriptAdvisor Jumpstart: JavaScript
Advisor Jumpstart: JavaScriptdominion
 
Programming Primer EncapsulationVB
Programming Primer EncapsulationVBProgramming Primer EncapsulationVB
Programming Primer EncapsulationVBsunmitraeducation
 
Java script
Java scriptJava script
Java scriptJay Patel
 
Philipp Von Weitershausen Untested Code Is Broken Code
Philipp Von Weitershausen   Untested Code Is Broken CodePhilipp Von Weitershausen   Untested Code Is Broken Code
Philipp Von Weitershausen Untested Code Is Broken CodeVincenzo Barone
 
Java script basics
Java script basicsJava script basics
Java script basicsJohn Smith
 
Complete Notes on Angular 2 and TypeScript
Complete Notes on Angular 2 and TypeScriptComplete Notes on Angular 2 and TypeScript
Complete Notes on Angular 2 and TypeScriptEPAM Systems
 
Java script
Java scriptJava script
Java scriptShyam Khant
 

Was ist angesagt? (19)

Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 
A Z Introduction To Ruby On Rails
A Z Introduction To Ruby On RailsA Z Introduction To Ruby On Rails
A Z Introduction To Ruby On Rails
 
A-Z Intro To Rails
A-Z Intro To RailsA-Z Intro To Rails
A-Z Intro To Rails
 
Best practices for crafting high quality PHP apps - PHP UK 2019
Best practices for crafting high quality PHP apps - PHP UK 2019Best practices for crafting high quality PHP apps - PHP UK 2019
Best practices for crafting high quality PHP apps - PHP UK 2019
 
Crafting Quality PHP Applications (ConFoo YVR 2017)
Crafting Quality PHP Applications (ConFoo YVR 2017)Crafting Quality PHP Applications (ConFoo YVR 2017)
Crafting Quality PHP Applications (ConFoo YVR 2017)
 
iOS best practices
iOS best practicesiOS best practices
iOS best practices
 
Html JavaScript and CSS
Html JavaScript and CSSHtml JavaScript and CSS
Html JavaScript and CSS
 
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)
 
Javascript
JavascriptJavascript
Javascript
 
Domain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDomain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with Rails
 
Java script
Java scriptJava script
Java script
 
Advisor Jumpstart: JavaScript
Advisor Jumpstart: JavaScriptAdvisor Jumpstart: JavaScript
Advisor Jumpstart: JavaScript
 
Programming Primer EncapsulationVB
Programming Primer EncapsulationVBProgramming Primer EncapsulationVB
Programming Primer EncapsulationVB
 
Java script
Java scriptJava script
Java script
 
Philipp Von Weitershausen Untested Code Is Broken Code
Philipp Von Weitershausen   Untested Code Is Broken CodePhilipp Von Weitershausen   Untested Code Is Broken Code
Philipp Von Weitershausen Untested Code Is Broken Code
 
Refactoring
RefactoringRefactoring
Refactoring
 
Java script basics
Java script basicsJava script basics
Java script basics
 
Complete Notes on Angular 2 and TypeScript
Complete Notes on Angular 2 and TypeScriptComplete Notes on Angular 2 and TypeScript
Complete Notes on Angular 2 and TypeScript
 
Java script
Java scriptJava script
Java script
 

Ähnlich wie Unbreakable Domain Models PHPUK 2014 London

Unbreakable Domain Models - DPC13
Unbreakable Domain Models - DPC13Unbreakable Domain Models - DPC13
Unbreakable Domain Models - DPC13Mathias Verraes
 
Beyond MVC: from Model to Domain
Beyond MVC: from Model to DomainBeyond MVC: from Model to Domain
Beyond MVC: from Model to DomainJeremy Cook
 
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)James Titcumb
 
Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)James Titcumb
 
Crafting Quality PHP Applications (PHPkonf 2018)
Crafting Quality PHP Applications (PHPkonf 2018)Crafting Quality PHP Applications (PHPkonf 2018)
Crafting Quality PHP Applications (PHPkonf 2018)James Titcumb
 
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014Barry Kooij
 
Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!
Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!
Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!Kacper Gunia
 
Clean tests good tests
Clean tests   good testsClean tests   good tests
Clean tests good testsShopsys Framework
 
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)Best practices for crafting high quality PHP apps (ScotlandPHP 2018)
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)James Titcumb
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework appsMichelangelo van Dam
 
Broadleaf Presents Thymeleaf
Broadleaf Presents ThymeleafBroadleaf Presents Thymeleaf
Broadleaf Presents ThymeleafBroadleaf Commerce
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Michelangelo van Dam
 
PHP Unit Testing
PHP Unit TestingPHP Unit Testing
PHP Unit TestingTagged Social
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014Guillaume POTIER
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everythingnoelrap
 
Data Validation models
Data Validation modelsData Validation models
Data Validation modelsMarcin Czarnecki
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxMichelangelo van Dam
 
Best practices for crafting high quality PHP apps (Bulgaria 2019)
Best practices for crafting high quality PHP apps (Bulgaria 2019)Best practices for crafting high quality PHP apps (Bulgaria 2019)
Best practices for crafting high quality PHP apps (Bulgaria 2019)James Titcumb
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 

Ähnlich wie Unbreakable Domain Models PHPUK 2014 London (20)

Unbreakable Domain Models - DPC13
Unbreakable Domain Models - DPC13Unbreakable Domain Models - DPC13
Unbreakable Domain Models - DPC13
 
Beyond MVC: from Model to Domain
Beyond MVC: from Model to DomainBeyond MVC: from Model to Domain
Beyond MVC: from Model to Domain
 
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
 
Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)
 
Crafting Quality PHP Applications (PHPkonf 2018)
Crafting Quality PHP Applications (PHPkonf 2018)Crafting Quality PHP Applications (PHPkonf 2018)
Crafting Quality PHP Applications (PHPkonf 2018)
 
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
 
Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!
Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!
Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!
 
Clean tests good tests
Clean tests   good testsClean tests   good tests
Clean tests good tests
 
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)Best practices for crafting high quality PHP apps (ScotlandPHP 2018)
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
Broadleaf Presents Thymeleaf
Broadleaf Presents ThymeleafBroadleaf Presents Thymeleaf
Broadleaf Presents Thymeleaf
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
 
PHP Unit Testing
PHP Unit TestingPHP Unit Testing
PHP Unit Testing
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
Data Validation models
Data Validation modelsData Validation models
Data Validation models
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
Steady with ruby
Steady with rubySteady with ruby
Steady with ruby
 
Best practices for crafting high quality PHP apps (Bulgaria 2019)
Best practices for crafting high quality PHP apps (Bulgaria 2019)Best practices for crafting high quality PHP apps (Bulgaria 2019)
Best practices for crafting high quality PHP apps (Bulgaria 2019)
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 

Mehr von Mathias Verraes

Towards Modelling Processes
Towards Modelling ProcessesTowards Modelling Processes
Towards Modelling ProcessesMathias Verraes
 
Modelling Heuristics
Modelling HeuristicsModelling Heuristics
Modelling HeuristicsMathias Verraes
 
Small Controlled Experiments
Small Controlled ExperimentsSmall Controlled Experiments
Small Controlled ExperimentsMathias Verraes
 
Managed Technical Debt
Managed Technical DebtManaged Technical Debt
Managed Technical DebtMathias 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
 
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
 
DDDBE Modellathon 2013
DDDBE Modellathon 2013DDDBE Modellathon 2013
DDDBE Modellathon 2013Mathias Verraes
 

Mehr von Mathias Verraes (11)

Towards Modelling Processes
Towards Modelling ProcessesTowards Modelling Processes
Towards Modelling Processes
 
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
 
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
 
DDDBE Modellathon 2013
DDDBE Modellathon 2013DDDBE Modellathon 2013
DDDBE Modellathon 2013
 

KĂŒrzlich hochgeladen

🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
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 Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGSujit Pal
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
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
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
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
 

KĂŒrzlich hochgeladen (20)

🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
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 Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAG
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
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
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
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
 

Unbreakable Domain Models PHPUK 2014 London

  • 2. A Map of the World
  • 3. London 3h train rides Kortrijk, Belgium Paris Amsterdam
  • 4. All models are wrong, but some are useful.
  • 5. I’m Mathias Verraes I'm an independent consultant. I help teams build enterprise web applications.
  • 8. Data Model ~= Structural Model ~= State ! Domain Model ~= Behavioral Model !
  • 10. The domain expert says “A customer must always have an email address.” * Could be different for your domain ** All examples are simpliïŹed
  • 11. Test fails class CustomerTest extends PHPUnit_Framework_TestCase! {! /** @test */! public function should_always_have_an_email()! {! ! $customer = new Customer();! ! assertThat(! $customer->getEmail(),! equalTo('jim@example.com') ! );! ! }! }
  • 12. Test passes 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') ! );! }! }
  • 13. Test fails 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’);! ! }! }
  • 14. final class Customer! {! private $email;! ! public function __construct($email)! {! $this->email = $email;! }! ! public function getEmail()! {! return $this->email;! }! }
  • 15. 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') ! );! }! }
  • 17. final class ProspectiveCustomer ! {! public function __construct()! {! // no email! }! }! ! final class PayingCustomer ! { ! public function __construct($email)! {! $this->email = $email;! }! }
  • 19. final class ProspectiveCustomer ! {! /** @return PayingCustomer */! public function convertToPayingCustomer($email)! { ! //...! }! }! ! final class PayingCustomer ! { ! //...! }
  • 20. The domain expert meant “A customer must always have a valid email address.”
  • 21. $customerValidator = new CustomerValidator;! if($customerValidator->isValid($customer)){! // ...! }
  • 22. Test fails class CustomerTest extends PHPUnit_Framework_TestCase! {! /** @test */! public function should_always_have_a_valid_email()! {! ! $this->setExpectedException(! 'InvalidArgumentException'! );! ! new Customer('malformed@email');! ! }! }
  • 23. Test passes final class Customer ! {! public function __construct($email)! {! if( /* boring validation stuff */) {! throw new InvalidArgumentException();! }! $this->email = $email;! }! }
  • 25. Test passes final class Email! {! private $email;! ! public function __construct($email)! {! if( /* boring validation stuff */) {! throw new InvalidArgumentException();! }! $this->email = $email;! }! ! public function __toString() ! {! return $this->email;! } ! }
  • 26. Test passes final class Customer! {! /** @var Email */! private $email;! ! public function __construct(Email $email)! {! $this->email = $email;! }! }
  • 27. 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’));! ! }! }
  • 30. The domain expert says “A customer orders products and pays for them.”
  • 31. $order = new Order;! $order->setCustomer($customer);! $order->setProducts($products);! $order->setStatus(Order::UNPAID);! ! ! // ...! ! ! $order->setPaidAmount(500);! $order->setPaidCurrency(‘EUR’);! ! $order->setStatus(Order::PAID);! !
  • 32. $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)! );
  • 33. $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)! );
  • 34. $order = new Order($customer, $products);! // set PaymentStatus in Order::__construct()! ! ! ! ! ! ! ! $order->setPaidMonetary(! new Money(500, new Currency(‘EUR’))! );! $order->setStatus(! new PaymentStatus(PaymentStatus::PAID)! );
  • 35. $order = new Order($customer, $products);! ! ! ! ! ! ! ! ! $order->pay(! new Money(500, new Currency(‘EUR’))! );! // set PaymentStatus in Order#pay()! !
  • 38. The domain expert says “Premium customers get special offers.”
  • 40. The domain expert says “Order 3 times to become a premium customer.”
  • 41. interface CustomerSpecification ! {! /** @return bool */! public function isSatisfiedBy(Customer $customer); ! }
  • 42. 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! }!
  • 43. $customerIsPremium = new CustomerIsPremium;! ! $aCustomerWith2Orders = ...! $aCustomerWith3Orders = ...! ! assertFalse(! $customerIsPremium->isSatisfiedBy($aCustomerWith2Orders)! );! ! assertTrue(! $customerIsPremium->isSatisfiedBy($aCustomerWith3Orders)! );! ! !
  • 44. The domain expert says “Different rules apply for different tenants.”
  • 45. interface CustomerIsPremium ! extends CustomerSpecification! ! final class CustomerWith3OrdersIsPremium ! implements CustomerIsPremium! ! final class CustomerWith500EuroTotalIsPremium! implements CustomerIsPremium! ! final class CustomerWhoBoughtLuxuryProductsIsPremium! implements CustomerIsPremium! ! ...!
  • 46. final class SpecialOfferSender! {! private $customerIsPremium;! ! ! public function __construct(! CustomerIsPremium $customerIsPremium) {...}! ! ! public function sendOffersTo(Customer $customer) ! {! if($this->customerIsPremium->isSatisfiedBy(! $customer! )) ! {! // send offers...! }! }! }!
  • 47. ! <!-- 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>
  • 48. Use speciïŹcations to encapsulate rules about object selection
  • 49. The domain expert says “Get a list of all premium customers.”
  • 50. 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);! }!
  • 51. Use repositories to create the illusion of in-memory collections
  • 52. interface CustomerRepository! {! ! /** @return Customer[] */! public function findSatisfying(! CustomerSpecification $customerSpecification! );! ! }! ! ! // generalized:! $objects = $repository->findSatisfying($specification);!
  • 53. class DbCustomerRepository implements CustomerRepository! {! /** @return Customer[] */! public function findSatisfying(! CustomerSpecification $specification) ! {! ! return array_filter(! $this->findAll(),! function(Customer $customer) use($specification) {! return $specification->isSatisfiedBy($customer);! } ! );! ! }! }!
  • 54. final class CustomerWith3OrdersIsPremium! implements CustomerSpecification! {! public function asSql() {! return ‘SELECT * FROM Customer...’;! }! }! ! ! // class DbCustomerRepository ! public function findSatisfying($specification) ! {! return $this->db->query($specification->asSql()); } !
  • 55. Use double dispatch to preserve encapsulation
  • 56. $expectedCustomers = array_filter(! $repository->findAll(),! // filter
! );! ! $actualCustomers = ! $repository->findSatisfying($specification);! ! assertThat($expectedCustomers, equalTo($actualCustomers));
  • 57. Test by comparing different representations of the same rule
  • 58. Protect your invariants ! Objects as consistency boundaries ! Encapsulate state and behavior
  • 59. Thanks! Questions? I ♄ Feedback joind.in/10690 ! Blog, Slides, other talks: verraes.net @mathiasverraes