SlideShare a Scribd company logo
De CRUD à DDD pas à pas
@SelrahcD
Charles Desneuf
Développeur indépendant
@SelrahcD
c.desneuf@gmail.com
4 Architecture
4 Modélisation
4 Tests
4 Agilité
Context
Application de santé qui propose des jeux à des centres
de soins ou des entreprises
pour favoriser une activité physique.
@SelrahcD
Context
Une entreprise qui grandit et recrute.
4 Automatiser les tâches d'administration
chronophages
4 Faciliter la compréhension de son métier par ses
nouveaux arrivants
4 Pouvoir adapter son système rapidement
@SelrahcD
Le backend est développé au dessus d'EasyAdmin 2
@SelrahcD
Player:
class: AppDomainEntityPlayer
controller: AppAdminControllerPlayerController
disabled_actions: [delete,new,show]
templates:
edit: 'admin/player/edit.html.twig'
list:
title: 'player.list.title'
sort: createdAt
actions:
- { name: contact, title: 'player.action.contact'}
fields:
- { property: id, label: UID }
- { property: lastname, label: 'player.label.lastname' }
- { property: firstname, label: 'player.label.firstname' }
edit:
// ...
@SelrahcD
class Player {
/**
* @ORMColumn(type="string")
*/
private $firstname;
public function getFirstname(): string {
return $this->firstname;
}
public function setFirstname(string $firstname): void {
$this->firstname = $firstname;
}
}
@SelrahcD
Préparer le terrain
@SelrahcD
Event storming
@SelrahcD
De nouveaux concepts
4 Registration
4 Assessment
4 Intervention
@SelrahcD
@SelrahcD
Lire le code
@SelrahcD
Annotations
/**
* @AssertNotNull()
*/
private $startDate;
/**
* @AssertExpression(expression="value > this.getStartDate()")
*/
private $endDate;
@SelrahcD
Event handlers doctrine
public function postUpdate(LifecycleEventArgs $event): void
{
if (!self::supportsLifecycleEvent($event)) {
return;
}
/** @var Game $game */
$game = $event->getEntity();
$changeSet = $event->getEntityManager()->getUnitOfWork()->getEntityChangeSet($game);
$changedAttributes = array_keys($changeSet);
$listenedAttributeChanges = ['interventionDuration', 'endDate'];
if (!empty(array_intersect($listenedAttributeChanges, $changedAttributes))) {
// Do something
}
}
@SelrahcD
Player2Game
@SelrahcD
Player2Game
VS
4 Registration
4 Assessment
4 Intervention
@SelrahcD
Player2Game:
class: AppAdminDomainPlayer2Game
controller: AppAdminControllerPlayer2GameController
disabled_actions: [new,edit,delete,show]
list:
sort: createdAt
actions:
- { name: restartIntervention, title: 'p2g.action.restart-intervention'}
- { name: stats, title: 'p2g.action.stats' }
fields:
- { property: game.name, label: 'p2g.label.program' }
- { property: player.firstname, label: 'p2g.label.firstname' }
- { property: player.lastname, label: 'p2g.label.lastname' }
- { property: isValidated, label: 'p2g.label.is-validated', type: boolean }
- { property: hasRunningAssessment, label: 'p2g.label.has-running-assessment', type: boolean }
- { property: startDate, label: 'p2g.label.intervention-start-date', type: date }
- { property: endDate, label: 'p2g.label.intervention-end-date', type: date }
@SelrahcD
Refactoring
@SelrahcD
Poser des tests
qui vont survivre au refactoring
@SelrahcD
Tests fonctionnels Symfony
4 De l'UI
4 Jusqu'à la DB
@SelrahcD
Tests fonctionnels Symfony
4 Charger une page
4 Effectuer une action
4 Aller vérifier que le résultat de l'action a eu lieu via
l'UI
@SelrahcD
Approval testing
4 Passer des paramètres au système
4 Stocker le résultat
4 S'assurer que le résultat reste toujours le même
@SelrahcD
class Player2GameManager
{
public function startIntervention(Player2Game $player2Game): Player2Game
{
$now = new DateTimeImmutable();
$startDate = $now;
$game = $player2Game->getGame();
if($game->getStartDate() > $now) {
$startDate = $game->getStartDate();
}
$endDate = $game->getEndDate();
if($game->duration() !== null) {
$endDate = $startDate->modify(sprintf('+ %d weeks', $game->duration()));
}
$player2Game->setStartDate($startDate);
$player2Game->setEndDate($endDate);
return $player2Game;
}
}
@SelrahcD
class StartInterventionTest extends TestCase {
private function print(Player2Game $player2Game): string
{
return "Start date: {$player2Game->getStartDate()->format(DATE_ISO8601)}"
. PHP_EOL
. "End date: {$player2Game->getEndDate()->format(DATE_ISO8601)}";
}
}
@SelrahcD
public function test_startIntervention(): void
{
$manager = new Player2GameManager();
$game = new Game(
new DateTimeImmutable(),
new DateTimeImmutable()
);
$player2Game = new Player2Game($game, new Player());
$player2GameResult = $manager->startIntervention($player2Game);
Approvals::verifyString($this->print($player2GameResult));
}
@SelrahcD
@SelrahcD
@SelrahcD
public function test_startIntervention(): void
{
$manager = new Player2GameManager();
$game = new Game(
new DateTimeImmutable(),
new DateTimeImmutable()
);
$player2Game = new Player2Game($game, new Player());
$player2GameResult = $manager->startIntervention($player2Game);
Approvals::verifyString($this->print($player2GameResult));
}
@SelrahcD
public function test_startIntervention(): void
{
$manager = new Player2GameManager();
$game = new Game(
new DateTimeImmutable('2017-12-18 22:14:17'),
new DateTimeImmutable('2019-10-09 06:06:16')
);
$player2Game = new Player2Game($game, new Player());
$player2GameResult = $manager->startIntervention($player2Game);
Approvals::verifyString($this->print($player2GameResult));
}
@SelrahcD
@SelrahcD
@SelrahcD
class Player2GameManager
{
public function startIntervention(Player2Game $player2Game): Player2Game
{
$now = new DateTimeImmutable();
$startDate = $now;
$game = $player2Game->getGame();
if($game->getStartDate() > $now) {
$startDate = $game->getStartDate();
}
$endDate = $game->getEndDate();
if($game->duration() !== null) {
$endDate = $startDate->modify(sprintf('+ %d weeks', $game->duration()));
}
$player2Game->setStartDate($startDate);
$player2Game->setEndDate($endDate);
return $player2Game;
}
}
@SelrahcD
class Player2GameManager
{
public function startIntervention(Player2Game $player2Game): Player2Game
{
$now = $this->getNow();
// ...
}
protected function getNow(): DateTimeImmutable
{
return new DateTimeImmutable();
}
}
Extract method
@SelrahcD
class TestablePlayer2GameManager extends Player2GameManager {
public ?DateTimeImmutable $now = null;
protected function getNow(): DateTimeImmutable
{
if($this->now !== null) {
return $this->now;
}
return parent::getNow();
}
}
Override factory method
@SelrahcD
public function test_startIntervention(): void
{
$manager = new TestablePlayer2GameManager();
$manager->now = new DateTimeImmutable('2017-12-22 02:34:18');
$game = new Game(
new DateTimeImmutable('2017-12-18 22:14:17'),
new DateTimeImmutable('2019-10-09 06:06:16')
);
$player2Game = new Player2Game($game, new Player());
$player2GameResult = $manager->startIntervention($player2Game);
Approvals::verifyString($this->print($player2GameResult));
}
@SelrahcD
@SelrahcD
@SelrahcD
public function test_startIntervention_start_date_after_now(): void
{
$manager = new TestablePlayer2GameManager();
$manager->now = new DateTimeImmutable('2017-12-15 02:34:18');
$game = new Game(
new DateTimeImmutable('2017-12-18 22:14:17'),
new DateTimeImmutable('2019-10-09 06:06:16')
);
$player2Game = new Player2Game($game, new Player());
$player2GameResult = $manager->startIntervention($player2Game);
Approvals::verifyString($this->print($player2GameResult));
}
@SelrahcD
@SelrahcD
@SelrahcD
public function test_startIntervention_start_date_after_now(): void
{
$manager = new TestablePlayer2GameManager();
$manager->now = new DateTimeImmutable('2017-12-15 02:34:18');
$duration = 2;
$game = new Game(
new DateTimeImmutable('2017-12-18 22:14:17'),
new DateTimeImmutable('2019-10-09 06:06:16'),
$duration
);
$player2Game = new Player2Game($game, new Player());
$player2GameResult = $manager->startIntervention($player2Game);
Approvals::verifyString($this->print($player2GameResult));
}
@SelrahcD
@SelrahcD
@SelrahcD
$exec = function($startDate, $endDate, $now, $duration) {
$manager = new TestablePlayer2GameManager();
$manager->now = new DateTimeImmutable($now);
$game = new Game(
new DateTimeImmutable($startDate),
new DateTimeImmutable($endDate),
$duration);
$player2Game = new Player2Game($game, new Player());
$player2GameResult = $manager->startIntervention($player2Game);
return $this->print($player2GameResult);
};
@SelrahcD
public function test_startIntervention_combinations(): void {
$exec = //...
$startDates = ['2017-12-18 22:14:17', ...];
$endDates = ['2019-10-09 06:06:16', ...];
$nows = ['2017-12-15 02:34:18', ...];
$durations = [null, 0, 2, 10];
CombinationApprovals::verifyAllCombinations4(
$exec,
$startDates,
$endDates,
$nows,
$durations
);
}
@SelrahcD
[2017-12-18 22:14:17, 2019-10-09 06:06:16, 2017-12-15 02:34:18, ] =>
Start date: 2017-12-18T22:14:17+00:00
End date: 2019-10-09T06:06:16+00:00
[2017-12-18 22:14:17, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 0] =>
Start date: 2017-12-18T22:14:17+00:00
End date: 2017-12-18T22:14:17+00:00
[2017-12-18 22:14:17, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 2] =>
Start date: 2017-12-18T22:14:17+00:00
End date: 2018-01-01T22:14:17+00:00
[2017-12-18 22:14:17, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 10] =>
Start date: 2017-12-18T22:14:17+00:00
End date: 2018-02-26T22:14:17+00:00
[2017-12-18 22:14:17, 2022-08-16 08:56:18, 2017-12-15 02:34:18, ] =>
Start date: 2017-12-18T22:14:17+00:00
End date: 2022-08-16T08:56:18+00:00
[2017-12-18 22:14:17, 2022-08-16 08:56:18, 2017-12-15 02:34:18, 0] =>
Start date: 2017-12-18T22:14:17+00:00
End date: 2017-12-18T22:14:17+00:00
[2017-12-18 22:14:17, 2022-08-16 08:56:18, 2017-12-15 02:34:18, 2] =>
Start date: 2017-12-18T22:14:17+00:00
End date: 2018-01-01T22:14:17+00:00
[2017-12-18 22:14:17, 2022-08-16 08:56:18, 2017-12-15 02:34:18, 10] =>
Start date: 2017-12-18T22:14:17+00:00
End date: 2018-02-26T22:14:17+00:00
[2017-12-10 13:30:15, 2019-10-09 06:06:16, 2017-12-15 02:34:18, ] =>
Start date: 2017-12-15T02:34:18+00:00
End date: 2019-10-09T06:06:16+00:00
[2017-12-10 13:30:15, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 0] =>
Start date: 2017-12-15T02:34:18+00:00
End date: 2017-12-15T02:34:18+00:00
[2017-12-10 13:30:15, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 2] =>
Start date: 2017-12-15T02:34:18+00:00
End date: 2017-12-29T02:34:18+00:00
...
@SelrahcD
class Player2GameManager {
public function startIntervention(Player2Game $player2Game): Player2Game
{
$now = $this->getNow();
$startDate = $now;
$game = $player2Game->getGame();
if($game->getStartDate() > $now) {
$startDate = $game->getStartDate();
}
$endDate = $game->getEndDate();
if($game->duration() !== null) {
$endDate = $startDate->modify(sprintf('+ %d weeks', $game->duration()));
}
$player2Game->setStartDate($startDate);
$player2Game->setEndDate($endDate);
return $player2Game;
}
}
@SelrahcD
class Player2Game {
public function startIntervention(DateTimeImmutable $now): Player2Game
{
$startDate = $now;
if($this->game->getStartDate() > $now) {
$startDate = $this->game->getStartDate();
}
$endDate = $this->game->getEndDate();
if($this->game->duration() !== null) {
$endDate = $startDate->modify(sprintf('+ %d weeks', $this->game->duration()));
}
$this->startDate = $startDate;
$this->endDate = $endDate;
return $this;
}
}
@SelrahcD
class Player2GameManager {
public function startIntervention(Player2Game $player2Game): Player2Game
{
return $player2Game->startIntervention($this->getNow());
}
}
@SelrahcD
class Player2Game {
public function startIntervention(DateTimeImmutable $now): Player2Game
{
$startDate = $now;
if($this->game->getStartDate() > $now) {
$startDate = $this->game->getStartDate();
}
$endDate = $this->game->getEndDate();
if($this->game->duration() !== null) {
$endDate = $startDate->modify(sprintf('+ %d weeks', $this->game->duration()));
}
$this->startDate = $startDate;
$this->endDate = $endDate;
return $this;
}
}
@SelrahcD
final class InterventionDates
{
public readonly DateTimeImmutable $startDate;
public readonly DateTimeImmutable $endDate;
public function __construct(
DateTimeImmutable $startDate,
DateTimeImmutable $endDate)
{
if($startDate > $endDate) {
throw new Exception('Start date must be before end date');
}
$this->startDate = $startDate;
$this->endDate = $endDate;
}
}
@SelrahcD
class Game {
public function computeInterventionDates(DateTimeImmutable $now): InterventionDates
{
$startDate = $this->startDate > $now ?
$this->startDate : $now;
$endDate = $this->duration === null ?
$this->endDate : $startDate->modify(sprintf('+ %d weeks', $this->duration));
return new InterventionDates($startDate, $endDate);
}
}
@SelrahcD
class Player2Game {
public function startIntervention(DateTimeImmutable $now): Player2Game
{
$interventionDates = $this->game->computeInterventionDates($now);
$this->startDate = $interventionDates->startDate;
$this->endDate = $interventionDates->endDate;
return $this;
}
}
@SelrahcD
After a while...
Photo by Jon Tyson on Unsplash
@SelrahcD
class Player2Game {
public function startIntervention(DateTimeImmutable $now): Player2Game {}
public function startInitialAssessment(): Player2Game {}
public function restartOn(DateTimeImmutable $restartDate): void {}
public function validate(): void {}
public function accept(): self {}
}
@SelrahcD
Utilisation des interfaces
interface Registration {
public function accept(): self;
public function startIntervention(): Intervention;
public function startInitialAssessment(): Assessment;
}
interface Intervention {
public function restartOn(DateTimeImmutable $restartDate): void;
}
interface Assessment {
public function validate(): void;
}
class Player2Game implements Registration, Assessment, Intervention {}
@SelrahcD
class Player2GameManager {
public function startIntervention(Player2Game $player2Game): Player2Game
{
return $player2Game->startIntervention($this->getNow());
}
}
@SelrahcD
class Player2GameManager {
public function startIntervention(Registration $registration): Intervention
{
return $registration->startIntervention($this->getClock());
}
}
@SelrahcD
Modification de la base de données
@SelrahcD
Player2Game
Pouvoir afficher des informations sur la relation entre
un joueur et un jeu venant de plusieurs entitées.
@SelrahcD
Player2Game:
class: AppAdminDomainPlayer2Game
controller: AppAdminControllerPlayer2GameController
disabled_actions: [new,edit,delete,show]
list:
sort: createdAt
actions:
- { name: restartIntervention, title: 'p2g.action.restart-intervention'}
- { name: stats, title: 'p2g.action.stats' }
fields:
- { property: game.name, label: 'p2g.label.program' }
- { property: player.firstname, label: 'p2g.label.firstname' }
- { property: player.lastname, label: 'p2g.label.lastname' }
- { property: isValidated, label: 'p2g.label.is-validated', type: boolean }
- { property: hasRunningAssessment, label: 'p2g.label.has-running-assessment', type: boolean }
- { property: startDate, label: 'p2g.label.intervention-start-date', type: date }
- { property: endDate, label: 'p2g.label.intervention-end-date', type: date }
@SelrahcD
Fonctionnement actuel
@SelrahcD
CQRS
@SelrahcD
Tentative 1 : 3 entitées d'écriture, une table, 1 entitée de
lecture
@SelrahcD
Tentative 2 : 3 tables, 3 entités d'écriture, 1 entitée de
lecture
@SelrahcD
Tentative 3 : 3 tables, 3 entités d'écriture, 1 entitée de
lecture et 1 vue
@SelrahcD
Création d'une vue MySQL
CREATE VIEW view_player_2_game AS
SELECT
r.id,
r.player_id,
r.game_id,
r.is_validated,
CASE WHEN a.id IS NULL THEN 0
ELSE 1 END AS has_running_assessment,
i.start_date,
i.end_date
FROM registration r
LEFT JOIN assessement a ON a.player_id = r.player_id AND a.game_id = r.game_id AND a.validation_date IS NULL
LEFT JOIN intervention i ON i.player_id = r.player_id AND i.game_id = r.game_id
@SelrahcD
Création d'un entité Doctrine
/**
* @ORMEntity(readOnly=true)
* @ORMTable (name="view_player_2_game")
*/
class Player2Game {
/**
* @ORMId
* @ORMColumn(type="guid")
*/
public $id;
/**
* @ORMManyToOne(targetEntity="AppDomainEntityPlayer")
*/
public $player;
/**
* @ORMColumn(type="datetime_immutable")
*/
public $validationDate;
}
@SelrahcD
Utilisation d'entités uniquement pour
la vue
/**
* @ORMEntity(readOnly=true)
* @ORMTable (name="view_player_2_game")
*/
class Player2Game {
/**
* @ORMId
* @ORMColumn(type="guid")
*/
public $id;
/**
* @ORMManyToOne(targetEntity="AppUIViewModelPlayer")
*/
public $player;
/**
* @ORMColumn(type="datetime_immutable")
*/
public $validationDate;
}
@SelrahcD
Faites le à plusieurs !
Photo by Helena Lopes on Unsplash
@SelrahcD
Merci !
@SelrahcD
Ressources
@SelrahcD
Générale
4 Design patterns for modernizing legacy code bases -
Matthias Noback
@SelrahcD
Rollup
4 Why & How to use Doctrine Migrations Rollup? -
Olivier Dolbeau
4 Rolling up database migrations with Doctrine -
Andreas Möller
@SelrahcD
Doctrine et vues
4 Aggregate your data by using SQL views and
Doctrine. - Robbert Stevens
4 Separating Concerns using Embeddables - Doctrine
documentation
@SelrahcD
Approval testing
4 approvals/approval-tests
4 Approval testing - Emily Bach
4 Live-Refactoring de Legacy Code avec la technique
du Golden Master - Philippe Bourgau
@SelrahcD

More Related Content

What's hot

Jasmine frontinrio
Jasmine frontinrioJasmine frontinrio
Jasmine frontinrio
Andre Fonseca
 
Practical
PracticalPractical
Practical
rajesh samata
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
Michelangelo van Dam
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
Samuel ROZE
 
The Ring programming language version 1.7 book - Part 63 of 196
The Ring programming language version 1.7 book - Part 63 of 196The Ring programming language version 1.7 book - Part 63 of 196
The Ring programming language version 1.7 book - Part 63 of 196
Mahmoud Samir Fayed
 
Modern Android Architecture
Modern Android ArchitectureModern Android Architecture
Modern Android Architecture
Eric Maxwell
 
Daily notes
Daily notesDaily notes
Daily notes
meghendra168
 
Modern JavaScript Engine Performance
Modern JavaScript Engine PerformanceModern JavaScript Engine Performance
Modern JavaScript Engine Performance
Catalin Dumitru
 
Effective Android Data Binding
Effective Android Data BindingEffective Android Data Binding
Effective Android Data Binding
Eric Maxwell
 
What's up with Prototype and script.aculo.us?
What's up with Prototype and script.aculo.us?What's up with Prototype and script.aculo.us?
What's up with Prototype and script.aculo.us?
Christophe Porteneuve
 
Asynchronous JS in Odoo
Asynchronous JS in OdooAsynchronous JS in Odoo
Asynchronous JS in Odoo
Odoo
 
code for quiz in my sql
code for quiz  in my sql code for quiz  in my sql
code for quiz in my sql
JOYITAKUNDU1
 
Dealing with Legacy PHP Applications
Dealing with Legacy PHP ApplicationsDealing with Legacy PHP Applications
Dealing with Legacy PHP Applications
Clinton Dreisbach
 
The Ring programming language version 1.6 book - Part 55 of 189
The Ring programming language version 1.6 book - Part 55 of 189The Ring programming language version 1.6 book - Part 55 of 189
The Ring programming language version 1.6 book - Part 55 of 189
Mahmoud Samir Fayed
 
Final tagless and cats mtl
Final tagless and cats mtl Final tagless and cats mtl
Final tagless and cats mtl
Alexander Zaidel
 
Mpg Dec07 Gian Lorenzetto
Mpg Dec07 Gian Lorenzetto Mpg Dec07 Gian Lorenzetto
Mpg Dec07 Gian Lorenzetto
melbournepatterns
 
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Colin O'Dell
 
Data20161007
Data20161007Data20161007
Data20161007
capegmail
 
Node meetup feb_20_12
Node meetup feb_20_12Node meetup feb_20_12
Node meetup feb_20_12
jafar104
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
Ting Lv
 

What's hot (20)

Jasmine frontinrio
Jasmine frontinrioJasmine frontinrio
Jasmine frontinrio
 
Practical
PracticalPractical
Practical
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
The Ring programming language version 1.7 book - Part 63 of 196
The Ring programming language version 1.7 book - Part 63 of 196The Ring programming language version 1.7 book - Part 63 of 196
The Ring programming language version 1.7 book - Part 63 of 196
 
Modern Android Architecture
Modern Android ArchitectureModern Android Architecture
Modern Android Architecture
 
Daily notes
Daily notesDaily notes
Daily notes
 
Modern JavaScript Engine Performance
Modern JavaScript Engine PerformanceModern JavaScript Engine Performance
Modern JavaScript Engine Performance
 
Effective Android Data Binding
Effective Android Data BindingEffective Android Data Binding
Effective Android Data Binding
 
What's up with Prototype and script.aculo.us?
What's up with Prototype and script.aculo.us?What's up with Prototype and script.aculo.us?
What's up with Prototype and script.aculo.us?
 
Asynchronous JS in Odoo
Asynchronous JS in OdooAsynchronous JS in Odoo
Asynchronous JS in Odoo
 
code for quiz in my sql
code for quiz  in my sql code for quiz  in my sql
code for quiz in my sql
 
Dealing with Legacy PHP Applications
Dealing with Legacy PHP ApplicationsDealing with Legacy PHP Applications
Dealing with Legacy PHP Applications
 
The Ring programming language version 1.6 book - Part 55 of 189
The Ring programming language version 1.6 book - Part 55 of 189The Ring programming language version 1.6 book - Part 55 of 189
The Ring programming language version 1.6 book - Part 55 of 189
 
Final tagless and cats mtl
Final tagless and cats mtl Final tagless and cats mtl
Final tagless and cats mtl
 
Mpg Dec07 Gian Lorenzetto
Mpg Dec07 Gian Lorenzetto Mpg Dec07 Gian Lorenzetto
Mpg Dec07 Gian Lorenzetto
 
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016
 
Data20161007
Data20161007Data20161007
Data20161007
 
Node meetup feb_20_12
Node meetup feb_20_12Node meetup feb_20_12
Node meetup feb_20_12
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
 

Similar to De CRUD à DDD pas à pas

Games, AI, and Research - Part 2 Training (FightingICE AI Programming)
Games, AI, and Research - Part 2 Training (FightingICE AI Programming)Games, AI, and Research - Part 2 Training (FightingICE AI Programming)
Games, AI, and Research - Part 2 Training (FightingICE AI Programming)
Pujana Paliyawan
 
Fact, Fiction, and FP
Fact, Fiction, and FPFact, Fiction, and FP
Fact, Fiction, and FP
Brian Lonsdorf
 
R57shell
R57shellR57shell
R57shell
ady36
 
Ordering System IP2buildclasses.netbeans_automatic_buildO.docx
Ordering System IP2buildclasses.netbeans_automatic_buildO.docxOrdering System IP2buildclasses.netbeans_automatic_buildO.docx
Ordering System IP2buildclasses.netbeans_automatic_buildO.docx
hopeaustin33688
 
Nko workshop - node js crud & deploy
Nko workshop - node js crud & deployNko workshop - node js crud & deploy
Nko workshop - node js crud & deploy
Simon Su
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
Nishan Subedi
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
Fernando Daciuk
 
Videogiochi in PHP 👾
Videogiochi in PHP 👾Videogiochi in PHP 👾
Videogiochi in PHP 👾
Manuel Baldassarri
 
Drupal 8 migrate!
Drupal 8 migrate!Drupal 8 migrate!
Drupal 8 migrate!
Pavel Makhrinsky
 
Introduction to angular js
Introduction to angular jsIntroduction to angular js
Introduction to angular js
Marco Vito Moscaritolo
 
Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScript
Visual Engineering
 
Having issues with passing my values through different functions aft.pdf
Having issues with passing my values through different functions aft.pdfHaving issues with passing my values through different functions aft.pdf
Having issues with passing my values through different functions aft.pdf
rajkumarm401
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
Yekmer Simsek
 
Celery
CeleryCelery
Celery
Fatih Erikli
 
ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 Developer
Gary Hockin
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
Kacper Gunia
 
Server side data sync for mobile apps with silex
Server side data sync for mobile apps with silexServer side data sync for mobile apps with silex
Server side data sync for mobile apps with silex
Michele Orselli
 
David Kopal - Write better React with ReasonML - Codemotion Milan 2018
David Kopal - Write better React with ReasonML - Codemotion Milan 2018David Kopal - Write better React with ReasonML - Codemotion Milan 2018
David Kopal - Write better React with ReasonML - Codemotion Milan 2018
Codemotion
 
Strategies for refactoring and migrating a big old project to be multilingual...
Strategies for refactoring and migrating a big old project to be multilingual...Strategies for refactoring and migrating a big old project to be multilingual...
Strategies for refactoring and migrating a big old project to be multilingual...
benjaoming
 
Visual Component Testing -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...
Visual Component Testing  -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...Visual Component Testing  -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...
Visual Component Testing -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...
Applitools
 

Similar to De CRUD à DDD pas à pas (20)

Games, AI, and Research - Part 2 Training (FightingICE AI Programming)
Games, AI, and Research - Part 2 Training (FightingICE AI Programming)Games, AI, and Research - Part 2 Training (FightingICE AI Programming)
Games, AI, and Research - Part 2 Training (FightingICE AI Programming)
 
Fact, Fiction, and FP
Fact, Fiction, and FPFact, Fiction, and FP
Fact, Fiction, and FP
 
R57shell
R57shellR57shell
R57shell
 
Ordering System IP2buildclasses.netbeans_automatic_buildO.docx
Ordering System IP2buildclasses.netbeans_automatic_buildO.docxOrdering System IP2buildclasses.netbeans_automatic_buildO.docx
Ordering System IP2buildclasses.netbeans_automatic_buildO.docx
 
Nko workshop - node js crud & deploy
Nko workshop - node js crud & deployNko workshop - node js crud & deploy
Nko workshop - node js crud & deploy
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
 
Videogiochi in PHP 👾
Videogiochi in PHP 👾Videogiochi in PHP 👾
Videogiochi in PHP 👾
 
Drupal 8 migrate!
Drupal 8 migrate!Drupal 8 migrate!
Drupal 8 migrate!
 
Introduction to angular js
Introduction to angular jsIntroduction to angular js
Introduction to angular js
 
Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScript
 
Having issues with passing my values through different functions aft.pdf
Having issues with passing my values through different functions aft.pdfHaving issues with passing my values through different functions aft.pdf
Having issues with passing my values through different functions aft.pdf
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 
Celery
CeleryCelery
Celery
 
ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 Developer
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
 
Server side data sync for mobile apps with silex
Server side data sync for mobile apps with silexServer side data sync for mobile apps with silex
Server side data sync for mobile apps with silex
 
David Kopal - Write better React with ReasonML - Codemotion Milan 2018
David Kopal - Write better React with ReasonML - Codemotion Milan 2018David Kopal - Write better React with ReasonML - Codemotion Milan 2018
David Kopal - Write better React with ReasonML - Codemotion Milan 2018
 
Strategies for refactoring and migrating a big old project to be multilingual...
Strategies for refactoring and migrating a big old project to be multilingual...Strategies for refactoring and migrating a big old project to be multilingual...
Strategies for refactoring and migrating a big old project to be multilingual...
 
Visual Component Testing -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...
Visual Component Testing  -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...Visual Component Testing  -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...
Visual Component Testing -- w/ Gil Tayar (Applitools) and Gleb Bahmutov (Cyp...
 

More from Charles Desneuf

Process Behavior Charts - Le Conte de Noël
Process Behavior Charts - Le Conte de NoëlProcess Behavior Charts - Le Conte de Noël
Process Behavior Charts - Le Conte de Noël
Charles Desneuf
 
Vous n'avez pas besoin de ça
Vous n'avez pas besoin de çaVous n'avez pas besoin de ça
Vous n'avez pas besoin de ça
Charles Desneuf
 
Faire grandir une equipe technique
Faire grandir une equipe techniqueFaire grandir une equipe technique
Faire grandir une equipe technique
Charles Desneuf
 
Les exceptions, oui, mais pas n'importe comment
Les exceptions, oui, mais pas n'importe commentLes exceptions, oui, mais pas n'importe comment
Les exceptions, oui, mais pas n'importe comment
Charles Desneuf
 
Recettes de tests
Recettes de testsRecettes de tests
Recettes de tests
Charles Desneuf
 
Dealing with not so exceptional exceptions
Dealing with not so exceptional exceptionsDealing with not so exceptional exceptions
Dealing with not so exceptional exceptions
Charles Desneuf
 

More from Charles Desneuf (6)

Process Behavior Charts - Le Conte de Noël
Process Behavior Charts - Le Conte de NoëlProcess Behavior Charts - Le Conte de Noël
Process Behavior Charts - Le Conte de Noël
 
Vous n'avez pas besoin de ça
Vous n'avez pas besoin de çaVous n'avez pas besoin de ça
Vous n'avez pas besoin de ça
 
Faire grandir une equipe technique
Faire grandir une equipe techniqueFaire grandir une equipe technique
Faire grandir une equipe technique
 
Les exceptions, oui, mais pas n'importe comment
Les exceptions, oui, mais pas n'importe commentLes exceptions, oui, mais pas n'importe comment
Les exceptions, oui, mais pas n'importe comment
 
Recettes de tests
Recettes de testsRecettes de tests
Recettes de tests
 
Dealing with not so exceptional exceptions
Dealing with not so exceptional exceptionsDealing with not so exceptional exceptions
Dealing with not so exceptional exceptions
 

Recently uploaded

How to write a program in any programming language
How to write a program in any programming languageHow to write a program in any programming language
How to write a program in any programming language
Rakesh Kumar R
 
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
Bert Jan Schrijver
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
Philip Schwarz
 
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdfTop Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
VALiNTRY360
 
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
mz5nrf0n
 
Fundamentals of Programming and Language Processors
Fundamentals of Programming and Language ProcessorsFundamentals of Programming and Language Processors
Fundamentals of Programming and Language Processors
Rakesh Kumar R
 
Top 9 Trends in Cybersecurity for 2024.pptx
Top 9 Trends in Cybersecurity for 2024.pptxTop 9 Trends in Cybersecurity for 2024.pptx
Top 9 Trends in Cybersecurity for 2024.pptx
devvsandy
 
Mobile app Development Services | Drona Infotech
Mobile app Development Services  | Drona InfotechMobile app Development Services  | Drona Infotech
Mobile app Development Services | Drona Infotech
Drona Infotech
 
Odoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Odoo ERP Vs. Traditional ERP Systems – A Comparative AnalysisOdoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Odoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Envertis Software Solutions
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
dakas1
 
Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
Ayan Halder
 
socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
SOCRadar
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
Remote DBA Services
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
Yara Milbes
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
brainerhub1
 
zOS Mainframe JES2-JES3 JCL-JECL Differences
zOS Mainframe JES2-JES3 JCL-JECL DifferenceszOS Mainframe JES2-JES3 JCL-JECL Differences
zOS Mainframe JES2-JES3 JCL-JECL Differences
YousufSait3
 
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
Peter Muessig
 
Energy consumption of Database Management - Florina Jonuzi
Energy consumption of Database Management - Florina JonuziEnergy consumption of Database Management - Florina Jonuzi
Energy consumption of Database Management - Florina Jonuzi
Green Software Development
 
Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !
Marcin Chrost
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
TheSMSPoint
 

Recently uploaded (20)

How to write a program in any programming language
How to write a program in any programming languageHow to write a program in any programming language
How to write a program in any programming language
 
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
 
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdfTop Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
 
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
 
Fundamentals of Programming and Language Processors
Fundamentals of Programming and Language ProcessorsFundamentals of Programming and Language Processors
Fundamentals of Programming and Language Processors
 
Top 9 Trends in Cybersecurity for 2024.pptx
Top 9 Trends in Cybersecurity for 2024.pptxTop 9 Trends in Cybersecurity for 2024.pptx
Top 9 Trends in Cybersecurity for 2024.pptx
 
Mobile app Development Services | Drona Infotech
Mobile app Development Services  | Drona InfotechMobile app Development Services  | Drona Infotech
Mobile app Development Services | Drona Infotech
 
Odoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Odoo ERP Vs. Traditional ERP Systems – A Comparative AnalysisOdoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
Odoo ERP Vs. Traditional ERP Systems – A Comparative Analysis
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
 
Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
 
socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
 
zOS Mainframe JES2-JES3 JCL-JECL Differences
zOS Mainframe JES2-JES3 JCL-JECL DifferenceszOS Mainframe JES2-JES3 JCL-JECL Differences
zOS Mainframe JES2-JES3 JCL-JECL Differences
 
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
 
Energy consumption of Database Management - Florina Jonuzi
Energy consumption of Database Management - Florina JonuziEnergy consumption of Database Management - Florina Jonuzi
Energy consumption of Database Management - Florina Jonuzi
 
Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
 

De CRUD à DDD pas à pas

  • 1. De CRUD à DDD pas à pas @SelrahcD
  • 2. Charles Desneuf Développeur indépendant @SelrahcD c.desneuf@gmail.com 4 Architecture 4 Modélisation 4 Tests 4 Agilité
  • 3. Context Application de santé qui propose des jeux à des centres de soins ou des entreprises pour favoriser une activité physique. @SelrahcD
  • 4. Context Une entreprise qui grandit et recrute. 4 Automatiser les tâches d'administration chronophages 4 Faciliter la compréhension de son métier par ses nouveaux arrivants 4 Pouvoir adapter son système rapidement @SelrahcD
  • 5. Le backend est développé au dessus d'EasyAdmin 2 @SelrahcD
  • 6. Player: class: AppDomainEntityPlayer controller: AppAdminControllerPlayerController disabled_actions: [delete,new,show] templates: edit: 'admin/player/edit.html.twig' list: title: 'player.list.title' sort: createdAt actions: - { name: contact, title: 'player.action.contact'} fields: - { property: id, label: UID } - { property: lastname, label: 'player.label.lastname' } - { property: firstname, label: 'player.label.firstname' } edit: // ... @SelrahcD
  • 7. class Player { /** * @ORMColumn(type="string") */ private $firstname; public function getFirstname(): string { return $this->firstname; } public function setFirstname(string $firstname): void { $this->firstname = $firstname; } } @SelrahcD
  • 10. De nouveaux concepts 4 Registration 4 Assessment 4 Intervention @SelrahcD
  • 13. Annotations /** * @AssertNotNull() */ private $startDate; /** * @AssertExpression(expression="value > this.getStartDate()") */ private $endDate; @SelrahcD
  • 14. Event handlers doctrine public function postUpdate(LifecycleEventArgs $event): void { if (!self::supportsLifecycleEvent($event)) { return; } /** @var Game $game */ $game = $event->getEntity(); $changeSet = $event->getEntityManager()->getUnitOfWork()->getEntityChangeSet($game); $changedAttributes = array_keys($changeSet); $listenedAttributeChanges = ['interventionDuration', 'endDate']; if (!empty(array_intersect($listenedAttributeChanges, $changedAttributes))) { // Do something } } @SelrahcD
  • 17. Player2Game: class: AppAdminDomainPlayer2Game controller: AppAdminControllerPlayer2GameController disabled_actions: [new,edit,delete,show] list: sort: createdAt actions: - { name: restartIntervention, title: 'p2g.action.restart-intervention'} - { name: stats, title: 'p2g.action.stats' } fields: - { property: game.name, label: 'p2g.label.program' } - { property: player.firstname, label: 'p2g.label.firstname' } - { property: player.lastname, label: 'p2g.label.lastname' } - { property: isValidated, label: 'p2g.label.is-validated', type: boolean } - { property: hasRunningAssessment, label: 'p2g.label.has-running-assessment', type: boolean } - { property: startDate, label: 'p2g.label.intervention-start-date', type: date } - { property: endDate, label: 'p2g.label.intervention-end-date', type: date } @SelrahcD
  • 19. Poser des tests qui vont survivre au refactoring @SelrahcD
  • 20. Tests fonctionnels Symfony 4 De l'UI 4 Jusqu'à la DB @SelrahcD
  • 21. Tests fonctionnels Symfony 4 Charger une page 4 Effectuer une action 4 Aller vérifier que le résultat de l'action a eu lieu via l'UI @SelrahcD
  • 22. Approval testing 4 Passer des paramètres au système 4 Stocker le résultat 4 S'assurer que le résultat reste toujours le même @SelrahcD
  • 23. class Player2GameManager { public function startIntervention(Player2Game $player2Game): Player2Game { $now = new DateTimeImmutable(); $startDate = $now; $game = $player2Game->getGame(); if($game->getStartDate() > $now) { $startDate = $game->getStartDate(); } $endDate = $game->getEndDate(); if($game->duration() !== null) { $endDate = $startDate->modify(sprintf('+ %d weeks', $game->duration())); } $player2Game->setStartDate($startDate); $player2Game->setEndDate($endDate); return $player2Game; } } @SelrahcD
  • 24. class StartInterventionTest extends TestCase { private function print(Player2Game $player2Game): string { return "Start date: {$player2Game->getStartDate()->format(DATE_ISO8601)}" . PHP_EOL . "End date: {$player2Game->getEndDate()->format(DATE_ISO8601)}"; } } @SelrahcD
  • 25. public function test_startIntervention(): void { $manager = new Player2GameManager(); $game = new Game( new DateTimeImmutable(), new DateTimeImmutable() ); $player2Game = new Player2Game($game, new Player()); $player2GameResult = $manager->startIntervention($player2Game); Approvals::verifyString($this->print($player2GameResult)); } @SelrahcD
  • 28. public function test_startIntervention(): void { $manager = new Player2GameManager(); $game = new Game( new DateTimeImmutable(), new DateTimeImmutable() ); $player2Game = new Player2Game($game, new Player()); $player2GameResult = $manager->startIntervention($player2Game); Approvals::verifyString($this->print($player2GameResult)); } @SelrahcD
  • 29. public function test_startIntervention(): void { $manager = new Player2GameManager(); $game = new Game( new DateTimeImmutable('2017-12-18 22:14:17'), new DateTimeImmutable('2019-10-09 06:06:16') ); $player2Game = new Player2Game($game, new Player()); $player2GameResult = $manager->startIntervention($player2Game); Approvals::verifyString($this->print($player2GameResult)); } @SelrahcD
  • 32. class Player2GameManager { public function startIntervention(Player2Game $player2Game): Player2Game { $now = new DateTimeImmutable(); $startDate = $now; $game = $player2Game->getGame(); if($game->getStartDate() > $now) { $startDate = $game->getStartDate(); } $endDate = $game->getEndDate(); if($game->duration() !== null) { $endDate = $startDate->modify(sprintf('+ %d weeks', $game->duration())); } $player2Game->setStartDate($startDate); $player2Game->setEndDate($endDate); return $player2Game; } } @SelrahcD
  • 33. class Player2GameManager { public function startIntervention(Player2Game $player2Game): Player2Game { $now = $this->getNow(); // ... } protected function getNow(): DateTimeImmutable { return new DateTimeImmutable(); } } Extract method @SelrahcD
  • 34. class TestablePlayer2GameManager extends Player2GameManager { public ?DateTimeImmutable $now = null; protected function getNow(): DateTimeImmutable { if($this->now !== null) { return $this->now; } return parent::getNow(); } } Override factory method @SelrahcD
  • 35. public function test_startIntervention(): void { $manager = new TestablePlayer2GameManager(); $manager->now = new DateTimeImmutable('2017-12-22 02:34:18'); $game = new Game( new DateTimeImmutable('2017-12-18 22:14:17'), new DateTimeImmutable('2019-10-09 06:06:16') ); $player2Game = new Player2Game($game, new Player()); $player2GameResult = $manager->startIntervention($player2Game); Approvals::verifyString($this->print($player2GameResult)); } @SelrahcD
  • 38. public function test_startIntervention_start_date_after_now(): void { $manager = new TestablePlayer2GameManager(); $manager->now = new DateTimeImmutable('2017-12-15 02:34:18'); $game = new Game( new DateTimeImmutable('2017-12-18 22:14:17'), new DateTimeImmutable('2019-10-09 06:06:16') ); $player2Game = new Player2Game($game, new Player()); $player2GameResult = $manager->startIntervention($player2Game); Approvals::verifyString($this->print($player2GameResult)); } @SelrahcD
  • 41. public function test_startIntervention_start_date_after_now(): void { $manager = new TestablePlayer2GameManager(); $manager->now = new DateTimeImmutable('2017-12-15 02:34:18'); $duration = 2; $game = new Game( new DateTimeImmutable('2017-12-18 22:14:17'), new DateTimeImmutable('2019-10-09 06:06:16'), $duration ); $player2Game = new Player2Game($game, new Player()); $player2GameResult = $manager->startIntervention($player2Game); Approvals::verifyString($this->print($player2GameResult)); } @SelrahcD
  • 44. $exec = function($startDate, $endDate, $now, $duration) { $manager = new TestablePlayer2GameManager(); $manager->now = new DateTimeImmutable($now); $game = new Game( new DateTimeImmutable($startDate), new DateTimeImmutable($endDate), $duration); $player2Game = new Player2Game($game, new Player()); $player2GameResult = $manager->startIntervention($player2Game); return $this->print($player2GameResult); }; @SelrahcD
  • 45. public function test_startIntervention_combinations(): void { $exec = //... $startDates = ['2017-12-18 22:14:17', ...]; $endDates = ['2019-10-09 06:06:16', ...]; $nows = ['2017-12-15 02:34:18', ...]; $durations = [null, 0, 2, 10]; CombinationApprovals::verifyAllCombinations4( $exec, $startDates, $endDates, $nows, $durations ); } @SelrahcD
  • 46. [2017-12-18 22:14:17, 2019-10-09 06:06:16, 2017-12-15 02:34:18, ] => Start date: 2017-12-18T22:14:17+00:00 End date: 2019-10-09T06:06:16+00:00 [2017-12-18 22:14:17, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 0] => Start date: 2017-12-18T22:14:17+00:00 End date: 2017-12-18T22:14:17+00:00 [2017-12-18 22:14:17, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 2] => Start date: 2017-12-18T22:14:17+00:00 End date: 2018-01-01T22:14:17+00:00 [2017-12-18 22:14:17, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 10] => Start date: 2017-12-18T22:14:17+00:00 End date: 2018-02-26T22:14:17+00:00 [2017-12-18 22:14:17, 2022-08-16 08:56:18, 2017-12-15 02:34:18, ] => Start date: 2017-12-18T22:14:17+00:00 End date: 2022-08-16T08:56:18+00:00 [2017-12-18 22:14:17, 2022-08-16 08:56:18, 2017-12-15 02:34:18, 0] => Start date: 2017-12-18T22:14:17+00:00 End date: 2017-12-18T22:14:17+00:00 [2017-12-18 22:14:17, 2022-08-16 08:56:18, 2017-12-15 02:34:18, 2] => Start date: 2017-12-18T22:14:17+00:00 End date: 2018-01-01T22:14:17+00:00 [2017-12-18 22:14:17, 2022-08-16 08:56:18, 2017-12-15 02:34:18, 10] => Start date: 2017-12-18T22:14:17+00:00 End date: 2018-02-26T22:14:17+00:00 [2017-12-10 13:30:15, 2019-10-09 06:06:16, 2017-12-15 02:34:18, ] => Start date: 2017-12-15T02:34:18+00:00 End date: 2019-10-09T06:06:16+00:00 [2017-12-10 13:30:15, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 0] => Start date: 2017-12-15T02:34:18+00:00 End date: 2017-12-15T02:34:18+00:00 [2017-12-10 13:30:15, 2019-10-09 06:06:16, 2017-12-15 02:34:18, 2] => Start date: 2017-12-15T02:34:18+00:00 End date: 2017-12-29T02:34:18+00:00 ... @SelrahcD
  • 47. class Player2GameManager { public function startIntervention(Player2Game $player2Game): Player2Game { $now = $this->getNow(); $startDate = $now; $game = $player2Game->getGame(); if($game->getStartDate() > $now) { $startDate = $game->getStartDate(); } $endDate = $game->getEndDate(); if($game->duration() !== null) { $endDate = $startDate->modify(sprintf('+ %d weeks', $game->duration())); } $player2Game->setStartDate($startDate); $player2Game->setEndDate($endDate); return $player2Game; } } @SelrahcD
  • 48. class Player2Game { public function startIntervention(DateTimeImmutable $now): Player2Game { $startDate = $now; if($this->game->getStartDate() > $now) { $startDate = $this->game->getStartDate(); } $endDate = $this->game->getEndDate(); if($this->game->duration() !== null) { $endDate = $startDate->modify(sprintf('+ %d weeks', $this->game->duration())); } $this->startDate = $startDate; $this->endDate = $endDate; return $this; } } @SelrahcD
  • 49. class Player2GameManager { public function startIntervention(Player2Game $player2Game): Player2Game { return $player2Game->startIntervention($this->getNow()); } } @SelrahcD
  • 50. class Player2Game { public function startIntervention(DateTimeImmutable $now): Player2Game { $startDate = $now; if($this->game->getStartDate() > $now) { $startDate = $this->game->getStartDate(); } $endDate = $this->game->getEndDate(); if($this->game->duration() !== null) { $endDate = $startDate->modify(sprintf('+ %d weeks', $this->game->duration())); } $this->startDate = $startDate; $this->endDate = $endDate; return $this; } } @SelrahcD
  • 51. final class InterventionDates { public readonly DateTimeImmutable $startDate; public readonly DateTimeImmutable $endDate; public function __construct( DateTimeImmutable $startDate, DateTimeImmutable $endDate) { if($startDate > $endDate) { throw new Exception('Start date must be before end date'); } $this->startDate = $startDate; $this->endDate = $endDate; } } @SelrahcD
  • 52. class Game { public function computeInterventionDates(DateTimeImmutable $now): InterventionDates { $startDate = $this->startDate > $now ? $this->startDate : $now; $endDate = $this->duration === null ? $this->endDate : $startDate->modify(sprintf('+ %d weeks', $this->duration)); return new InterventionDates($startDate, $endDate); } } @SelrahcD
  • 53. class Player2Game { public function startIntervention(DateTimeImmutable $now): Player2Game { $interventionDates = $this->game->computeInterventionDates($now); $this->startDate = $interventionDates->startDate; $this->endDate = $interventionDates->endDate; return $this; } } @SelrahcD
  • 54. After a while... Photo by Jon Tyson on Unsplash @SelrahcD
  • 55. class Player2Game { public function startIntervention(DateTimeImmutable $now): Player2Game {} public function startInitialAssessment(): Player2Game {} public function restartOn(DateTimeImmutable $restartDate): void {} public function validate(): void {} public function accept(): self {} } @SelrahcD
  • 56. Utilisation des interfaces interface Registration { public function accept(): self; public function startIntervention(): Intervention; public function startInitialAssessment(): Assessment; } interface Intervention { public function restartOn(DateTimeImmutable $restartDate): void; } interface Assessment { public function validate(): void; } class Player2Game implements Registration, Assessment, Intervention {} @SelrahcD
  • 57. class Player2GameManager { public function startIntervention(Player2Game $player2Game): Player2Game { return $player2Game->startIntervention($this->getNow()); } } @SelrahcD
  • 58. class Player2GameManager { public function startIntervention(Registration $registration): Intervention { return $registration->startIntervention($this->getClock()); } } @SelrahcD
  • 59. Modification de la base de données @SelrahcD
  • 60. Player2Game Pouvoir afficher des informations sur la relation entre un joueur et un jeu venant de plusieurs entitées. @SelrahcD
  • 61. Player2Game: class: AppAdminDomainPlayer2Game controller: AppAdminControllerPlayer2GameController disabled_actions: [new,edit,delete,show] list: sort: createdAt actions: - { name: restartIntervention, title: 'p2g.action.restart-intervention'} - { name: stats, title: 'p2g.action.stats' } fields: - { property: game.name, label: 'p2g.label.program' } - { property: player.firstname, label: 'p2g.label.firstname' } - { property: player.lastname, label: 'p2g.label.lastname' } - { property: isValidated, label: 'p2g.label.is-validated', type: boolean } - { property: hasRunningAssessment, label: 'p2g.label.has-running-assessment', type: boolean } - { property: startDate, label: 'p2g.label.intervention-start-date', type: date } - { property: endDate, label: 'p2g.label.intervention-end-date', type: date } @SelrahcD
  • 64. Tentative 1 : 3 entitées d'écriture, une table, 1 entitée de lecture @SelrahcD
  • 65. Tentative 2 : 3 tables, 3 entités d'écriture, 1 entitée de lecture @SelrahcD
  • 66. Tentative 3 : 3 tables, 3 entités d'écriture, 1 entitée de lecture et 1 vue @SelrahcD
  • 67. Création d'une vue MySQL CREATE VIEW view_player_2_game AS SELECT r.id, r.player_id, r.game_id, r.is_validated, CASE WHEN a.id IS NULL THEN 0 ELSE 1 END AS has_running_assessment, i.start_date, i.end_date FROM registration r LEFT JOIN assessement a ON a.player_id = r.player_id AND a.game_id = r.game_id AND a.validation_date IS NULL LEFT JOIN intervention i ON i.player_id = r.player_id AND i.game_id = r.game_id @SelrahcD
  • 68. Création d'un entité Doctrine /** * @ORMEntity(readOnly=true) * @ORMTable (name="view_player_2_game") */ class Player2Game { /** * @ORMId * @ORMColumn(type="guid") */ public $id; /** * @ORMManyToOne(targetEntity="AppDomainEntityPlayer") */ public $player; /** * @ORMColumn(type="datetime_immutable") */ public $validationDate; } @SelrahcD
  • 69. Utilisation d'entités uniquement pour la vue /** * @ORMEntity(readOnly=true) * @ORMTable (name="view_player_2_game") */ class Player2Game { /** * @ORMId * @ORMColumn(type="guid") */ public $id; /** * @ORMManyToOne(targetEntity="AppUIViewModelPlayer") */ public $player; /** * @ORMColumn(type="datetime_immutable") */ public $validationDate; } @SelrahcD
  • 70. Faites le à plusieurs ! Photo by Helena Lopes on Unsplash @SelrahcD
  • 73. Générale 4 Design patterns for modernizing legacy code bases - Matthias Noback @SelrahcD
  • 74. Rollup 4 Why & How to use Doctrine Migrations Rollup? - Olivier Dolbeau 4 Rolling up database migrations with Doctrine - Andreas Möller @SelrahcD
  • 75. Doctrine et vues 4 Aggregate your data by using SQL views and Doctrine. - Robbert Stevens 4 Separating Concerns using Embeddables - Doctrine documentation @SelrahcD
  • 76. Approval testing 4 approvals/approval-tests 4 Approval testing - Emily Bach 4 Live-Refactoring de Legacy Code avec la technique du Golden Master - Philippe Bourgau @SelrahcD