SlideShare ist ein Scribd-Unternehmen logo
1 von 54
Downloaden Sie, um offline zu lesen
Technically DDD
@pelshoff @phpfwdays
Questions?
Context
Technical building blocks
Quiz
Case study I
Case study II
Context
Technical building blocks
Quiz
Case study I
Case study II
Value objects
final class Name {
private $lastName;
private $firstName;
private $insertion;
public function __construct(string $lastName, string $firstName, string $insertion) {
$this->lastName = $lastName;
$this->firstName = $firstName;
$this->insertion = $insertion;
$this->nameMustHaveLastName();
}
private function nameMustHaveLastName(): void {
if (!$this->lastName) {
throw InvalidName::becauseLastNameIsMissing();
}
}
public function getLastName(): string { /**/ }
public function getFirstName(): string { /**/ }
public function getInsertion(): string { /**/ }
}
final class EmailAddress {
private $emailAddress;
public function __construct(string $emailAddress) {
$this->emailAddress = $emailAddress;
$this->emailAddressMustBeAnActualEmailAddress();
}
private function emailAddressMustBeAnActualEmailAddress(): void {
if (!filter_var($this->emailAddress, FILTER_VALIDATE_EMAIL)) {
throw InvalidEmailAddress::becauseThisIsNotAnEmailAddress();
}
}
public function asString(): string {
return $this->emailAddress;
}
}
Value objects
● Express a value
● Define allowed values
● Are immutable
● Are easy to test
Entities
final class Attendee {
private $id;
private $name;
private $emailAddress;
public function __construct(Uuid $id, Name $name, EmailAddress $emailAddress) {
$this->id = $id;
$this->name = $name;
$this->emailAddress = $emailAddress;
}
public function getId(): Uuid { /**/ }
public function getName(): Name { /**/ }
public function setName(Name $name): Attendee { /**/ }
public function getEmailAddress(): EmailAddress { /**/ }
public function setEmailAddress(EmailAddress $emailAddress): Attendee {
$this->emailAddress = $emailAddress;
return $this;
}
}
Entities
● Have identity
● Are more than their attributes
● Evolve over time
● Are slightly harder to test
Services
final class Registration {
private $repository;
public function __construct(AttendeeRepository $repository) {
$this->repository = $repository;
}
public function registerNew(Attendee $attendee): void {
$this->emailAddressMustBeUnique($attendee->getEmailAddress());
$this->repository->add($attendee);
}
private function emailAddressMustBeUnique(EmailAddress $emailAddress): void {
try {
$this->repository->findByEmailAddress($emailAddress);
} catch (AttendeeNotFound $e) {
return;
}
throw RegistrationFailed::becauseEmailAddressIsNotUnique();
}
}
Services
● Have no identity or attributes
(Are not a “thing”)
● Tackle cross-concern operations
● Are harder to test
Context
Technical building blocks
Quiz
Case study I
Case study II
final class Address
final class BicycleService
final class StreetAddress
final class Investment
Context
Technical building blocks
Quiz
Case study I
Case study II
final class Meeting {
private $meetingId;
private $title;
private $description;
private $code;
private $startDate;
private $endDate;
private $startTime;
private $endTime;
private $isPublished;
private $subTitle;
private $program;
public function __construct(Uuid $meetingId, string $title, string $description, string
$code, string $startDate, string $endDate, string $startTime, string $endTime, bool
$isPublished, string $subTitle, array $program) {
$this->meetingId = $meetingId;
$this->title = $title;
// ...
}
}
new Meeting(
Uuid::generate(), 'PHP fwdays'20 online conference', '...', '#PHPfwdays',
'2020-05-30', '2020-05-31', '09:50', '17:50', false,
'Online conference for PHP developers',
[
[
'date' => '2020-05-30',
'startTime' => '15:35',
'endTime' => '15:50',
'title' => 'Break',
'room' => 'Pim’s living room',
],
[
'date' => '2020-05-30',
'startTime' => '15:50',
'endTime' => '16:40',
'title' => 'Final Class Aggregate',
'room' => 'Pim’s living room',
],
]
);
Meetings cannot end before they start
The Approach™
1. Implement in Entity
2. Extract Value Object
3. Refactor Value Object
final class Meeting {
public function __construct(/**/) {
// ..
$this->meetingCannotEndBeforeStart();
}
private function meetingCannotEndBeforeStart(): void {
if ($this->startDate < $this->endDate) {
return;
}
if ($this->startDate > $this->endDate) {
throw InvalidMeeting::becauseMeetingEndsBeforeStarting();
}
if ($this->startTime > $this->endTime) {
throw InvalidMeeting::becauseMeetingEndsBeforeStarting();
}
}
}
final class Meeting {
private $meetingId;
private $title;
private $description;
private $code;
private $duration;
private $isPublished;
private $subTitle;
private $program;
public function __construct(Uuid $meetingId, string $title, string $description, string
$code, MeetingDuration $duration, bool $isPublished, string $subTitle, array $program) {
$this->meetingId = $meetingId;
$this->title = $title;
$this->description = $description;
$this->code = $code;
$this->duration = $duration;
$this->isPublished = $isPublished;
$this->subTitle = $subTitle;
$this->program = $program;
}
}
final class MeetingDuration {
public function __construct(string $startDate, string $endDate, string $startTime, string
$endTime) {
$this->startDate = $startDate;
$this->endDate = $endDate;
$this->startTime = $startTime;
$this->endTime = $endTime;
$this->meetingCannotEndBeforeStart();
}
private function meetingCannotEndBeforeStart(): void {
if ($this->startDate < $this->endDate) {
return;
}
if ($this->startDate > $this->endDate) {
throw InvalidMeetingDuration::becauseDurationEndsBeforeStarting();
}
if ($this->startTime > $this->endTime) {
throw InvalidMeetingDuration::becauseDurationEndsBeforeStarting();
}
}
}
final class MeetingDuration {
private $start;
private $end;
public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) {
$this->start = $start;
$this->end = $end;
$this->meetingCannotEndBeforeStart();
}
private function meetingCannotEndBeforeStart(): void {
if ($this->start > $this->end) {
throw InvalidMeetingDuration::becauseDurationEndsBeforeStarting();
}
}
}
new Meeting(
Uuid::generate(), 'PHP fwdays'20 online conference', '...', '#PHPfwdays',
new MeetingDuration(
new DateTimeImmutable('2020-05-30' . ' ' . '09:50'),
new DateTimeImmutable('2020-05-31' . ' ' . '17:50')
), false, 'Online conference for PHP developers',
[
[
'date' => '2020-05-30',
'startTime' => '15:35',
'endTime' => '15:50',
'title' => 'Break',
'room' => 'Pim’s living room',
],
[
'date' => '2020-05-30',
'startTime' => '15:50',
'endTime' => '16:40',
'title' => 'Final Class Aggregate',
'room' => 'Pim’s living room',
], // ..
Context
Technical building blocks
Quiz
Case study I
Case study II
Program slots cannot occur in the same
room at the same time
[
[
'date' => '2020-05-30',
'startTime' => '15:35',
'endTime' => '15:50',
'title' => 'Break',
'room' => 'Pim’s living room',
],
[
'date' => '2020-05-30',
'startTime' => '15:50',
'endTime' => '16:40',
'title' => 'Final Class Aggregate',
'room' => 'Pim’s living room',
],
]
private function programSlotsCannotOccurInTheSameRoomAtTheSameTime(): void {
foreach ($this->program as $index => $slot) {
foreach (array_slice($this->program, $index + 1) as $comparison) {
if ($slot['room'] !== $comparison['room']) {
continue;
}
if ($slot['date'] !== $comparison['date']) {
continue;
}
if ($slot['startTime'] >= $comparison['endTime']) {
continue;
}
if ($slot['endTime'] <= $comparison['startTime']) {
continue;
}
throw InvalidProgram::becauseProgramSlotsOverlap();
}
}
}
final class Meeting {
private $meetingId;
private $title;
private $description;
private $code;
private $duration;
private $isPublished;
private $subTitle;
private $program;
public function __construct(Uuid $meetingId, string $title, string $description, string
$code, MeetingDuration $duration, bool $isPublished, string $subTitle, Program $program){
$this->meetingId = $meetingId;
$this->title = $title;
$this->description = $description;
$this->code = $code;
$this->duration = $duration;
$this->isPublished = $isPublished;
$this->subTitle = $subTitle;
$this->program = $program;
}
}
final class Program {
// ..
private function programSlotsCannotOccurInTheSameRoomAtTheSameTime(): void {
foreach ($this->program as $index => $slot) {
foreach (array_slice($this->program, $index + 1) as $comparison) {
if ($slot['room'] !== $comparison['room']) {
continue;
}
if ($slot['date'] !== $comparison['date']) {
continue;
}
if ($slot['startTime'] >= $comparison['endTime']) {
continue;
}
if ($slot['endTime'] <= $comparison['startTime']) {
continue;
}
throw InvalidProgram::becauseProgramSlotsOverlap();
}
}
} // ..
final class Program {
private $program;
/** @param Slot[] $program */
public function __construct(array $program) {
$this->program = $program;
$this->programSlotsCannotOccurInTheSameRoomAtTheSameTime();
}
private function programSlotsCannotOccurInTheSameRoomAtTheSameTime(): void {
foreach ($this->program as $index => $thisSlot) {
foreach (array_slice($this->program, $index + 1) as $thatSlot) {
if ($thisSlot->overlapsWith($thatSlot)) {
throw InvalidProgram::becauseProgramSlotsOverlap();
}
}
}
}
}
final class Slot {
private $duration;
private $title;
private $room;
public function __construct(MeetingDuration $duration, string $title, string $room) {
$this->duration = $duration;
$this->title = $title;
$this->room = $room;
}
public function overlapsWith(Slot $that): bool {
return $this->room === $that->room
&& $this->duration->overlapsWith($that->duration);
}
}
final class MeetingDuration {
private $start;
private $end;
public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ }
private function meetingCannotEndBeforeStart(): void { /**/ }
public function overlapsWith(MeetingDuration $that): bool {
return $this->start >= $that->start && $this->start <= $that->end
|| $this->end >= $that->start && $this->end <= $that->end
|| $that->start >= $this->start && $that->start <= $this->end
|| $that->end >= $this->start && $that->end <= $this->end;
}
}
final class MeetingDuration {
private $start;
private $end;
public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ }
private function meetingCannotEndBeforeStart(): void { /**/ }
public function overlapsWith(MeetingDuration $that): bool {
return $that->contains($this->start) || $that->contains($this->end)
|| $this->contains($that->start) || $this->contains($that->end);
}
private function contains(DateTimeImmutable $date): bool {
return $date >= $this->start && $date <= $this->end;
}
}
1. 3.
2. 4.
This slot
That slot
This slot
That slot
This slot
That slot
This slot
That slot
Time
This slot That slot
This slotThat slot
Time
1.
2.
final class MeetingDuration {
private $start;
private $end;
public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ }
private function meetingCannotEndBeforeStart(): void { /**/ }
public function overlapsWith(MeetingDuration $that): bool {
return !($this->start > $that->end || $that->start > $this->end);
}
}
final class MeetingDuration {
private $start;
private $end;
public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ }
private function meetingCannotEndBeforeStart(): void { /**/ }
public function overlapsWith(MeetingDuration $that): bool {
return !$this->before($that) && !$that->before($this);
}
private function before(MeetingDuration $that): bool {
return $that->start > $this->end;
}
}
[
'date' => '2020-05-30',
'startTime' => '15:50',
'endTime' => '16:40',
'title' => 'Final Class Aggregate',
'room' => 'Pim’s living room',
],
private function before(MeetingDuration $that): bool {
return $that->start > $this->end;
}
final class SlotDuration {
private $start;
private $end;
public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ }
private function slotCannotEndBeforeStart(): void { /**/ }
private function slotMustStartAndEndOnTheSameDay(): void {
if ($this->start->diff($this->end)->days >= 1) {
throw InvalidSlotDuration::becauseSlotEndsOnDifferentDay();
}
}
public function overlapsWith(SlotDuration $that): bool {
return !$this->before($that) && !$that->before($this);
}
private function before(SlotDuration $that): bool {
return $that->start >= $this->end;
}
}
new Meeting(
Uuid::generate(), 'PHP fwdays'20 online conference', '...', '#PHPfwdays',
'2020-05-30', '2020-05-31', '09:50', '17:50', false,
'Online conference for PHP developers',
[
[
'date' => '2020-05-30',
'startTime' => '15:35',
'endTime' => '15:50',
'title' => 'Break',
'room' => 'Pim’s living room',
],
[
'date' => '2020-05-30',
'startTime' => '15:50',
'endTime' => '16:40',
'title' => 'Final Class Aggregate',
'room' => 'Pim’s living room',
],
]
);
new Meeting(
Uuid::generate(), 'PHP fwdays'20 online conference', '...', '#PHPfwdays',
new MeetingDuration(
new DateTimeImmutable('2020-05-30' . ' ' . '09:50'),
new DateTimeImmutable('2020-05-31' . ' ' . '17:50')
), false, 'Online conference for PHP developers',
new Program([
new Slot(
new SlotDuration(
new DateTimeImmutable('2020-05-30' . ' ' . '15:35'),
new DateTimeImmutable('2020-05-30' . ' ' . '15:50')
),
'Break', 'Pim’s living room'
),
new Slot(
new SlotDuration(
new DateTimeImmutable('2020-05-30' . ' ' . '15:50'),
new DateTimeImmutable('2020-05-30' . ' ' . '16:40')
),
'Final Class Aggregate', 'Pim’s living room'
),
]) // ..
It must be possible to reschedule a
meeting
Pim Elshoff
Work: developer.procurios.com
Follow: @pelshoff
Coaching: pelshoff.com
Slides:

Weitere ähnliche Inhalte

Was ist angesagt?

R57shell
R57shellR57shell
R57shell
ady36
 
Design Patterns in PHP5
Design Patterns in PHP5 Design Patterns in PHP5
Design Patterns in PHP5
Wildan Maulana
 

Was ist angesagt? (20)

Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Smelling your code
Smelling your codeSmelling your code
Smelling your code
 
R57shell
R57shellR57shell
R57shell
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHP
 
PHP and MySQL
PHP and MySQLPHP and MySQL
PHP and MySQL
 
PHP Language Trivia
PHP Language TriviaPHP Language Trivia
PHP Language Trivia
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object Calisthenics
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix it
 
Things I Believe Now That I'm Old
Things I Believe Now That I'm OldThings I Believe Now That I'm Old
Things I Believe Now That I'm Old
 
循環参照のはなし
循環参照のはなし循環参照のはなし
循環参照のはなし
 
Object Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPObject Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHP
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
 
Design Patterns in PHP5
Design Patterns in PHP5 Design Patterns in PHP5
Design Patterns in PHP5
 
Presentation1
Presentation1Presentation1
Presentation1
 
Drupal 8 database api
Drupal 8 database apiDrupal 8 database api
Drupal 8 database api
 
Revisiting SOLID Principles
Revisiting  SOLID Principles Revisiting  SOLID Principles
Revisiting SOLID Principles
 
J slider
J sliderJ slider
J slider
 
Writing Sensible Code
Writing Sensible CodeWriting Sensible Code
Writing Sensible Code
 
Extending Moose
Extending MooseExtending Moose
Extending Moose
 
Object::Franger: Wear a Raincoat in your Code
Object::Franger: Wear a Raincoat in your CodeObject::Franger: Wear a Raincoat in your Code
Object::Franger: Wear a Raincoat in your Code
 

Ähnlich wie Pim Elshoff "Technically DDD"

Ähnlich wie Pim Elshoff "Technically DDD" (20)

How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
Symfony2 - extending the console component
Symfony2 - extending the console componentSymfony2 - extending the console component
Symfony2 - extending the console component
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Code moi une RH! (PHP tour 2017)
Code moi une RH! (PHP tour 2017)Code moi une RH! (PHP tour 2017)
Code moi une RH! (PHP tour 2017)
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolve
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 
The Origin of Lithium
The Origin of LithiumThe Origin of Lithium
The Origin of Lithium
 
Gérer vos objets
Gérer vos objetsGérer vos objets
Gérer vos objets
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Oops in php
Oops in phpOops in php
Oops in php
 
Code me a HR
Code me a HRCode me a HR
Code me a HR
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
 
Be pragmatic, be SOLID
Be pragmatic, be SOLIDBe pragmatic, be SOLID
Be pragmatic, be SOLID
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
Separation of concerns - DPC12
Separation of concerns - DPC12Separation of concerns - DPC12
Separation of concerns - DPC12
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
 

Mehr von Fwdays

Mehr von Fwdays (20)

"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y..."How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
 
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
 
"Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl..."Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl...
 
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
 
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ..."The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
 
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu..."[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
 
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care..."[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
 
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"..."4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
 
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast..."Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
 
"Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others..."Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others...
 
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
 
"Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv..."Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv...
 
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin..."How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
 

Kürzlich hochgeladen

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
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
Safe Software
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 

Kürzlich hochgeladen (20)

MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source Milvus
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
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
 
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...
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
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...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
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
 
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
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 

Pim Elshoff "Technically DDD"

  • 1.
  • 3.
  • 4.
  • 7.
  • 10. final class Name { private $lastName; private $firstName; private $insertion; public function __construct(string $lastName, string $firstName, string $insertion) { $this->lastName = $lastName; $this->firstName = $firstName; $this->insertion = $insertion; $this->nameMustHaveLastName(); } private function nameMustHaveLastName(): void { if (!$this->lastName) { throw InvalidName::becauseLastNameIsMissing(); } } public function getLastName(): string { /**/ } public function getFirstName(): string { /**/ } public function getInsertion(): string { /**/ } }
  • 11. final class EmailAddress { private $emailAddress; public function __construct(string $emailAddress) { $this->emailAddress = $emailAddress; $this->emailAddressMustBeAnActualEmailAddress(); } private function emailAddressMustBeAnActualEmailAddress(): void { if (!filter_var($this->emailAddress, FILTER_VALIDATE_EMAIL)) { throw InvalidEmailAddress::becauseThisIsNotAnEmailAddress(); } } public function asString(): string { return $this->emailAddress; } }
  • 12. Value objects ● Express a value ● Define allowed values ● Are immutable ● Are easy to test
  • 14. final class Attendee { private $id; private $name; private $emailAddress; public function __construct(Uuid $id, Name $name, EmailAddress $emailAddress) { $this->id = $id; $this->name = $name; $this->emailAddress = $emailAddress; } public function getId(): Uuid { /**/ } public function getName(): Name { /**/ } public function setName(Name $name): Attendee { /**/ } public function getEmailAddress(): EmailAddress { /**/ } public function setEmailAddress(EmailAddress $emailAddress): Attendee { $this->emailAddress = $emailAddress; return $this; } }
  • 15. Entities ● Have identity ● Are more than their attributes ● Evolve over time ● Are slightly harder to test
  • 17. final class Registration { private $repository; public function __construct(AttendeeRepository $repository) { $this->repository = $repository; } public function registerNew(Attendee $attendee): void { $this->emailAddressMustBeUnique($attendee->getEmailAddress()); $this->repository->add($attendee); } private function emailAddressMustBeUnique(EmailAddress $emailAddress): void { try { $this->repository->findByEmailAddress($emailAddress); } catch (AttendeeNotFound $e) { return; } throw RegistrationFailed::becauseEmailAddressIsNotUnique(); } }
  • 18. Services ● Have no identity or attributes (Are not a “thing”) ● Tackle cross-concern operations ● Are harder to test
  • 25. final class Meeting { private $meetingId; private $title; private $description; private $code; private $startDate; private $endDate; private $startTime; private $endTime; private $isPublished; private $subTitle; private $program; public function __construct(Uuid $meetingId, string $title, string $description, string $code, string $startDate, string $endDate, string $startTime, string $endTime, bool $isPublished, string $subTitle, array $program) { $this->meetingId = $meetingId; $this->title = $title; // ... } }
  • 26. new Meeting( Uuid::generate(), 'PHP fwdays'20 online conference', '...', '#PHPfwdays', '2020-05-30', '2020-05-31', '09:50', '17:50', false, 'Online conference for PHP developers', [ [ 'date' => '2020-05-30', 'startTime' => '15:35', 'endTime' => '15:50', 'title' => 'Break', 'room' => 'Pim’s living room', ], [ 'date' => '2020-05-30', 'startTime' => '15:50', 'endTime' => '16:40', 'title' => 'Final Class Aggregate', 'room' => 'Pim’s living room', ], ] );
  • 27. Meetings cannot end before they start
  • 28. The Approach™ 1. Implement in Entity 2. Extract Value Object 3. Refactor Value Object
  • 29. final class Meeting { public function __construct(/**/) { // .. $this->meetingCannotEndBeforeStart(); } private function meetingCannotEndBeforeStart(): void { if ($this->startDate < $this->endDate) { return; } if ($this->startDate > $this->endDate) { throw InvalidMeeting::becauseMeetingEndsBeforeStarting(); } if ($this->startTime > $this->endTime) { throw InvalidMeeting::becauseMeetingEndsBeforeStarting(); } } }
  • 30. final class Meeting { private $meetingId; private $title; private $description; private $code; private $duration; private $isPublished; private $subTitle; private $program; public function __construct(Uuid $meetingId, string $title, string $description, string $code, MeetingDuration $duration, bool $isPublished, string $subTitle, array $program) { $this->meetingId = $meetingId; $this->title = $title; $this->description = $description; $this->code = $code; $this->duration = $duration; $this->isPublished = $isPublished; $this->subTitle = $subTitle; $this->program = $program; } }
  • 31. final class MeetingDuration { public function __construct(string $startDate, string $endDate, string $startTime, string $endTime) { $this->startDate = $startDate; $this->endDate = $endDate; $this->startTime = $startTime; $this->endTime = $endTime; $this->meetingCannotEndBeforeStart(); } private function meetingCannotEndBeforeStart(): void { if ($this->startDate < $this->endDate) { return; } if ($this->startDate > $this->endDate) { throw InvalidMeetingDuration::becauseDurationEndsBeforeStarting(); } if ($this->startTime > $this->endTime) { throw InvalidMeetingDuration::becauseDurationEndsBeforeStarting(); } } }
  • 32. final class MeetingDuration { private $start; private $end; public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { $this->start = $start; $this->end = $end; $this->meetingCannotEndBeforeStart(); } private function meetingCannotEndBeforeStart(): void { if ($this->start > $this->end) { throw InvalidMeetingDuration::becauseDurationEndsBeforeStarting(); } } }
  • 33. new Meeting( Uuid::generate(), 'PHP fwdays'20 online conference', '...', '#PHPfwdays', new MeetingDuration( new DateTimeImmutable('2020-05-30' . ' ' . '09:50'), new DateTimeImmutable('2020-05-31' . ' ' . '17:50') ), false, 'Online conference for PHP developers', [ [ 'date' => '2020-05-30', 'startTime' => '15:35', 'endTime' => '15:50', 'title' => 'Break', 'room' => 'Pim’s living room', ], [ 'date' => '2020-05-30', 'startTime' => '15:50', 'endTime' => '16:40', 'title' => 'Final Class Aggregate', 'room' => 'Pim’s living room', ], // ..
  • 35. Program slots cannot occur in the same room at the same time
  • 36. [ [ 'date' => '2020-05-30', 'startTime' => '15:35', 'endTime' => '15:50', 'title' => 'Break', 'room' => 'Pim’s living room', ], [ 'date' => '2020-05-30', 'startTime' => '15:50', 'endTime' => '16:40', 'title' => 'Final Class Aggregate', 'room' => 'Pim’s living room', ], ]
  • 37. private function programSlotsCannotOccurInTheSameRoomAtTheSameTime(): void { foreach ($this->program as $index => $slot) { foreach (array_slice($this->program, $index + 1) as $comparison) { if ($slot['room'] !== $comparison['room']) { continue; } if ($slot['date'] !== $comparison['date']) { continue; } if ($slot['startTime'] >= $comparison['endTime']) { continue; } if ($slot['endTime'] <= $comparison['startTime']) { continue; } throw InvalidProgram::becauseProgramSlotsOverlap(); } } }
  • 38. final class Meeting { private $meetingId; private $title; private $description; private $code; private $duration; private $isPublished; private $subTitle; private $program; public function __construct(Uuid $meetingId, string $title, string $description, string $code, MeetingDuration $duration, bool $isPublished, string $subTitle, Program $program){ $this->meetingId = $meetingId; $this->title = $title; $this->description = $description; $this->code = $code; $this->duration = $duration; $this->isPublished = $isPublished; $this->subTitle = $subTitle; $this->program = $program; } }
  • 39. final class Program { // .. private function programSlotsCannotOccurInTheSameRoomAtTheSameTime(): void { foreach ($this->program as $index => $slot) { foreach (array_slice($this->program, $index + 1) as $comparison) { if ($slot['room'] !== $comparison['room']) { continue; } if ($slot['date'] !== $comparison['date']) { continue; } if ($slot['startTime'] >= $comparison['endTime']) { continue; } if ($slot['endTime'] <= $comparison['startTime']) { continue; } throw InvalidProgram::becauseProgramSlotsOverlap(); } } } // ..
  • 40. final class Program { private $program; /** @param Slot[] $program */ public function __construct(array $program) { $this->program = $program; $this->programSlotsCannotOccurInTheSameRoomAtTheSameTime(); } private function programSlotsCannotOccurInTheSameRoomAtTheSameTime(): void { foreach ($this->program as $index => $thisSlot) { foreach (array_slice($this->program, $index + 1) as $thatSlot) { if ($thisSlot->overlapsWith($thatSlot)) { throw InvalidProgram::becauseProgramSlotsOverlap(); } } } } }
  • 41. final class Slot { private $duration; private $title; private $room; public function __construct(MeetingDuration $duration, string $title, string $room) { $this->duration = $duration; $this->title = $title; $this->room = $room; } public function overlapsWith(Slot $that): bool { return $this->room === $that->room && $this->duration->overlapsWith($that->duration); } }
  • 42. final class MeetingDuration { private $start; private $end; public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ } private function meetingCannotEndBeforeStart(): void { /**/ } public function overlapsWith(MeetingDuration $that): bool { return $this->start >= $that->start && $this->start <= $that->end || $this->end >= $that->start && $this->end <= $that->end || $that->start >= $this->start && $that->start <= $this->end || $that->end >= $this->start && $that->end <= $this->end; } }
  • 43. final class MeetingDuration { private $start; private $end; public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ } private function meetingCannotEndBeforeStart(): void { /**/ } public function overlapsWith(MeetingDuration $that): bool { return $that->contains($this->start) || $that->contains($this->end) || $this->contains($that->start) || $this->contains($that->end); } private function contains(DateTimeImmutable $date): bool { return $date >= $this->start && $date <= $this->end; } }
  • 44. 1. 3. 2. 4. This slot That slot This slot That slot This slot That slot This slot That slot Time
  • 45. This slot That slot This slotThat slot Time 1. 2.
  • 46. final class MeetingDuration { private $start; private $end; public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ } private function meetingCannotEndBeforeStart(): void { /**/ } public function overlapsWith(MeetingDuration $that): bool { return !($this->start > $that->end || $that->start > $this->end); } }
  • 47. final class MeetingDuration { private $start; private $end; public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ } private function meetingCannotEndBeforeStart(): void { /**/ } public function overlapsWith(MeetingDuration $that): bool { return !$this->before($that) && !$that->before($this); } private function before(MeetingDuration $that): bool { return $that->start > $this->end; } }
  • 48. [ 'date' => '2020-05-30', 'startTime' => '15:50', 'endTime' => '16:40', 'title' => 'Final Class Aggregate', 'room' => 'Pim’s living room', ], private function before(MeetingDuration $that): bool { return $that->start > $this->end; }
  • 49. final class SlotDuration { private $start; private $end; public function __construct(DateTimeImmutable $start, DateTimeImmutable $end) { /**/ } private function slotCannotEndBeforeStart(): void { /**/ } private function slotMustStartAndEndOnTheSameDay(): void { if ($this->start->diff($this->end)->days >= 1) { throw InvalidSlotDuration::becauseSlotEndsOnDifferentDay(); } } public function overlapsWith(SlotDuration $that): bool { return !$this->before($that) && !$that->before($this); } private function before(SlotDuration $that): bool { return $that->start >= $this->end; } }
  • 50. new Meeting( Uuid::generate(), 'PHP fwdays'20 online conference', '...', '#PHPfwdays', '2020-05-30', '2020-05-31', '09:50', '17:50', false, 'Online conference for PHP developers', [ [ 'date' => '2020-05-30', 'startTime' => '15:35', 'endTime' => '15:50', 'title' => 'Break', 'room' => 'Pim’s living room', ], [ 'date' => '2020-05-30', 'startTime' => '15:50', 'endTime' => '16:40', 'title' => 'Final Class Aggregate', 'room' => 'Pim’s living room', ], ] );
  • 51. new Meeting( Uuid::generate(), 'PHP fwdays'20 online conference', '...', '#PHPfwdays', new MeetingDuration( new DateTimeImmutable('2020-05-30' . ' ' . '09:50'), new DateTimeImmutable('2020-05-31' . ' ' . '17:50') ), false, 'Online conference for PHP developers', new Program([ new Slot( new SlotDuration( new DateTimeImmutable('2020-05-30' . ' ' . '15:35'), new DateTimeImmutable('2020-05-30' . ' ' . '15:50') ), 'Break', 'Pim’s living room' ), new Slot( new SlotDuration( new DateTimeImmutable('2020-05-30' . ' ' . '15:50'), new DateTimeImmutable('2020-05-30' . ' ' . '16:40') ), 'Final Class Aggregate', 'Pim’s living room' ), ]) // ..
  • 52. It must be possible to reschedule a meeting
  • 53.
  • 54. Pim Elshoff Work: developer.procurios.com Follow: @pelshoff Coaching: pelshoff.com Slides: