SlideShare une entreprise Scribd logo
1  sur  76
Télécharger pour lire hors ligne
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

Contenu connexe

Tendances

Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 
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 patternsSamuel 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 196Mahmoud Samir Fayed
 
Modern Android Architecture
Modern Android ArchitectureModern Android Architecture
Modern Android ArchitectureEric Maxwell
 
Modern JavaScript Engine Performance
Modern JavaScript Engine PerformanceModern JavaScript Engine Performance
Modern JavaScript Engine PerformanceCatalin Dumitru
 
Effective Android Data Binding
Effective Android Data BindingEffective Android Data Binding
Effective Android Data BindingEric 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 OdooOdoo
 
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 ApplicationsClinton 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 189Mahmoud Samir Fayed
 
Final tagless and cats mtl
Final tagless and cats mtl Final tagless and cats mtl
Final tagless and cats mtl Alexander Zaidel
 
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 2016Colin O'Dell
 
Data20161007
Data20161007Data20161007
Data20161007capegmail
 
Node meetup feb_20_12
Node meetup feb_20_12Node meetup feb_20_12
Node meetup feb_20_12jafar104
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说Ting Lv
 

Tendances (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 豆瓣说
 

Similaire à 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
 
R57shell
R57shellR57shell
R57shellady36
 
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.docxhopeaustin33688
 
Nko workshop - node js crud & deploy
Nko workshop - node js crud & deployNko workshop - node js crud & deploy
Nko workshop - node js crud & deploySimon Su
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan 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 2015Fernando Daciuk
 
Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptVisual 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.pdfrajkumarm401
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best PracticesYekmer Simsek
 
ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperGary 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 - 4DevelopersKacper 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 silexMichele 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 2018Codemotion
 
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
 

Similaire à 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...
 

Plus de 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ëlCharles 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 çaCharles Desneuf
 
Faire grandir une equipe technique
Faire grandir une equipe techniqueFaire grandir une equipe technique
Faire grandir une equipe techniqueCharles 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 commentCharles Desneuf
 
Dealing with not so exceptional exceptions
Dealing with not so exceptional exceptionsDealing with not so exceptional exceptions
Dealing with not so exceptional exceptionsCharles Desneuf
 

Plus de 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
 

Dernier

Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfCionsystems
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfkalichargn70th171
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️anilsa9823
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 

Dernier (20)

Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdf
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 

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