SlideShare ist ein Scribd-Unternehmen logo
1 von 90
Rich Domain Model with 
Symfony2 and Doctrine2 
Leonardo Proietti 
@_leopro_ 
Symfony Day Italy 
Milan, 10 October 2014
twitter.com/_leopro_ 
github.com/leopro 
linkedin.com/in/leonardoproietti
1. Domain Modeling
domain = problem space 
domain model = solution space
Domain 
“A Domain [...] is what an organization 
does and the world it does in.” 
(Vaughn Vernon, “Implementing Domain-Driven Design”)
Domain Model 
“A model is a simplification. It is an 
interpretation of reality that abstracts the 
aspects relevant to solving problem at hand 
and ignores extraneous detail.” 
(Eric Evans, "Domain Driven Design")
A model
Domain Model 
“In DDD, domain model refers to a 
class.” 
(Julie Lerman, http://msdn.microsoft.com/en-us/magazine/dn385704.aspx)
Domain Model 
“An object model of the domain that 
incorporates both behavior and data.” 
(Martin Fowler, http://martinfowler.com/eaaCatalog/domainModel.html)
Domain Model 
“An object model of the domain that 
incorporates both behavior and data.” 
(Martin Fowler, http://martinfowler.com/eaaCatalog/domainModel.html)
Why Rich?
First, because it’s not anemic 
Martin Fowler 
http://martinfowler.com/bliki/AnemicDomainModel.html
<?php 
class Anemic 
{ 
private $shouldNotChangeAfterCreation; 
private $shouldBeChangedOnlyOnEdit; 
private $couldBeChangedAnytime; 
public function setCouldBeChangedAnytime($couldBeChangedAnytime) {} 
public function setShouldBeChangedOnlyOnEdit($shouldBeChangedOnlyOnEdit) {} 
public function setShouldNotChangeAfterCreation($shouldNotChangeAfterCreation {} 
}
<?php 
class SomeService 
{ 
public function doStuffOnCreation() 
{ 
$anemic = new Anemic(); 
$anemic->setCouldBeChangedAnytime('abc'); 
$anemic->setShouldNotChangeAfterCreation('def'); 
$unitOfWork->persist($anemic); 
$unitOfWork->flush(); 
} 
}
<?php 
class SomeService 
{ 
public function doStuffOnEdit() 
{ 
$anemic = new Anemic(); 
$anemic->setCouldBeChangedAnytime('abc'); 
$anemic->setShouldBeChangedOnlyOnEdit(‘123’); 
$unitOfWork->persist($anemic); 
$unitOfWork->flush(); 
} 
}
<?php 
class SomeService 
{ 
public function doStuffOnEdit() 
{ 
Loss of memory 
$anemic = new Anemic(); 
$anemic->setCouldBeChangedAnytime('abc'); 
$anemic->setShouldBeChangedOnlyOnEdit(‘123’); 
$unitOfWork->persist($anemic); 
$unitOfWork->flush(); 
} 
}
<?php 
class SomeService 
{ 
public function doStuffOnEdit() 
{ 
$anemic = new Anemic(); 
$anemic->setCouldBeChangedAnytime('abc'); 
$anemic->setShouldBeChangedOnlyOnEdit(‘123’); 
$anemic->setShouldNotChangeAfterCreation('def'); 
$unitOfWork->persist($anemic); 
$unitOfWork->flush(); 
} 
}
<?php 
class BehaviouralClass 
{ 
private $shouldNotChangeAfterCreation; 
private $shouldBeChangedOnlyOnEdit; 
private $couldBeChangedAnytime; 
public function __construct($shouldNotChangeAfterCreation, $couldBeChangedAnytime) 
{ 
$this->shouldNotChangeAfterCreation = $shouldNotChangeAfterCreation; 
$this->couldBeChangedAnytime = $couldBeChangedAnytime; 
} 
public function modify($shouldBeChangedOnlyOnEdit, $couldBeChangedAnytime = null) 
{ 
$this->shouldBeChangedOnlyOnEdit = $shouldBeChangedOnlyOnEdit; 
$this->couldBeChangedAnytime = $couldBeChangedAnytime; 
} 
}
<?php 
class BehaviouralClass 
{ 
private $shouldNotChangeAfterCreation; 
private $shouldBeChangedOnlyOnEdit; 
private $couldBeChangedAnytime; 
It's not still rich, lacks of ... 
public function __construct($shouldNotChangeAfterCreation, $couldBeChangedAnytime) 
{ 
$this->shouldNotChangeAfterCreation = $shouldNotChangeAfterCreation; 
$this->couldBeChangedAnytime = $couldBeChangedAnytime; 
} 
public function modify($shouldBeChangedOnlyOnEdit, $couldBeChangedAnytime = null) 
{ 
$this->shouldBeChangedOnlyOnEdit = $shouldBeChangedOnlyOnEdit; 
$this->couldBeChangedAnytime = $couldBeChangedAnytime; 
} 
}
Ubiquitous Language 
“the domain model can provide the backbone 
for that common language [...]. The vocabulary 
of that ubiquitius language includes the names 
of classes and prominent operations” 
(Eric Evans, "Domain Driven Design")
What does “coffee” mean? 
Alberto Brandolini AKA @ziobrando
A bit of strategy
Our domain is an online game that simulate the soccer’s world.
Bounded Contexts
What does “player” mean 
in our domain?
The meaning of “player” 
Within game engine context 
a model of a real soccer player, 
modelled with behaviours to fit the 
requirements of the game engine.
The meaning of “player” 
Within data import context 
a model of a real soccer player, but 
modelled for a simple CRUD.
The meaning of “player” 
Within user profile context 
a model of the user of the website, 
who plays the game.
League Team 
Calendar Player 
Lineup 
Coach 
Core context
League Team 
Calendar Player 
Lineup 
Coach 
(the player in “user 
context”) 
Core context
Player 
Game context User context 
Uuid 
Name 
Roles 
Uuid 
Email 
Password
Data and Behaviours
<?php 
class League 
{ 
private $id; 
private $name; 
private $teams; 
public function __construct(Uuid $uuid, $name) 
{ 
$this->id = $uuid; 
$this->name = $name; 
$this->teams = new ArrayCollection(); 
} 
public function registerTeam(Team $team) 
{ 
$this->teams->add($team); 
} 
}
<?php 
class League 
{ 
private $id; 
private $name; 
private $teams; 
A team must do a registration 
public function __construct({ 
to the league 
Uuid $uuid, $name) 
$this->id = $uuid; 
$this->name = $name; 
$this->teams = new ArrayCollection(); 
} 
public function registerTeam(Team $team) 
{ 
$this->teams->add($team); 
} 
}
<?php 
class League 
{ 
private $id; 
private $genericInfo; 
private $teams; 
public function __construct(Uuid $uuid, 
LeagueGenericInfo $leagueGenericInfo) 
{ 
$this->id = $uuid; 
$this->genericInfo = $leagueGenericInfo; 
$this->teams = new ArrayCollection(); 
} 
// ...}
<?php 
class LeagueGenericInfo 
{ 
private $name; 
private $description; 
private $country; 
public function __construct($country, $description, $name) 
{ 
$this->country = $country; 
$this->description = $description; 
$this->name = $name; 
} 
// … getters and behaviours}
<?php 
class LeagueGenericInfo 
{ 
private $name; 
private $description; 
private $country; 
Value object 
public function __construct($country, $description, $name) 
{ 
$this->country = $country; 
$this->description = $description; 
$this->name = $name; 
} 
// … getters and behaviours}
Validation: invariants and input
<?php 
class League 
{ 
// ... 
public function registerTeam(Team $team) 
{ 
if (!$this->canLeagueAcceptAnotherRegistration()) { 
throw new DomainException('Not more places available'); 
} 
$this->teams->add($team); 
} 
private function canLeagueAcceptAnotherRegistration() 
{ 
if ($this->teams->count() == 8) { 
return false; 
} 
return true; 
} 
}
<?php 
class League 
{ 
// ... 
public function registerTeam(Team $team) 
{ 
if (!$this->canLeagueAcceptAnotherRegistration()) { 
League protects its invariants 
throw new DomainException('Not more places available'); 
} 
$this->teams->add($team); 
} 
private function canLeagueAcceptAnotherRegistration() 
{ 
if ($this->teams->count() == 8) { 
return false; 
} 
return true; 
} 
}
<?php 
class League 
{ 
// ... 
public function getTeams() 
{ 
return $this->teams; 
} 
}
<?php 
class League 
{ 
// ... 
private function getTeams() 
{ 
return $this->teams; 
} 
}
<?php 
class LeagueGenericInfo 
{ 
private $name; 
private $description; 
private $country; 
private static $countries; 
public function __construct($country, $description, $name) 
{ 
if(!isset(static::$countries)) { 
static::$countries = require __DIR__.'/countries.php'; 
} 
if (!array_key_exists($name, static::$countries)) { 
throw new UnknownCountryException($country); 
} 
$this->country = $country; 
// .. thanks to Mathias Verraes for “Money” ;-) 
}}
<?php 
class LeagueGenericInfo 
{ 
private $name; 
private $description; 
private $country; 
Input validation 
private static $countries; 
public function __construct($country, $description, $name) 
{ 
if(!isset(static::$countries)) { 
static::$countries = require __DIR__.'/countries.php'; 
} 
if (!array_key_exists($name, static::$countries)) { 
throw new UnknownCountryException($country); 
} 
$this->country = $country; 
// .. thanks to Mathias Verraes for “Money” ;-) 
}}
<?php 
class LeagueGenericInfo 
{ 
private $name; 
private $description; 
private $country; 
Could be private static also $countries; 
placed in commands 
public function __construct($country, $description, $name) 
{ 
if(!isset(static::$countries)) { 
static::$countries = require __DIR__.'/countries.php'; 
} 
if (!array_key_exists($name, static::$countries)) { 
throw new UnknownCountryException($country); 
} 
$this->country = $country; 
// .. thanks to Mathias Verraes for “Money” ;-) 
}}
<?php 
class League 
{ 
public function render() 
{ 
$properties = [ 
'id' => $this->id, 
'name' => $this->genericInfo->getName(), 
'description' => $this->genericInfo->getDescription(), 
'country' => $this->genericInfo->getCountry(), 
]; 
$teams = new ArrayCollection(); 
foreach ($this->teams as $team) { 
$teams->add($team->render()); 
} 
$properties['teams'] = $teams; 
return new ArrayCollection($properties); 
} 
}
<?php 
class League 
{ 
public function render() 
{ 
$properties = [ 
'id' => $this->id, 
'name' => $this->genericInfo->getName(), 
'description' => $this->genericInfo->getDescription(), 
'country' => $this->genericInfo->getCountry(), 
]; 
Return a read-only object 
$teams = new ArrayCollection(); 
foreach ($this->teams as $team) { 
$teams->add($team->render()); 
} 
$properties['teams'] = $teams; 
return new ArrayCollection($properties); 
} 
}
<?php 
class Team 
{ 
private $id; 
private $players; 
public function __construct(Uuid $uuid) 
{ 
$this->id = $uuid; 
$this->players = new ArrayCollection(); 
} 
public function firePlayer($id) 
{ 
foreach ($this->players as $key => $player) { 
if ($player->getId() == $id) { 
$this->players->remove($key); 
} 
} 
} 
}
<?php 
class Team 
{ 
private $id; 
private $players; 
Traverse the collections 
public function __construct(Uuid $uuid) 
{ 
$this->id = $uuid; 
$this->players = new ArrayCollection(); 
} 
public function firePlayer(Player $playerToFire) 
{ 
foreach ($this->players as $key => $player) { 
if ($player->getId() == $playerToFire->getId()) { 
$this->players->remove($key); 
} 
} 
} 
}
The Player should have a 
relation towards the Team?
Be iterative using TDD/BDD
<?php 
class LeagueTest extends PHPUnit_Framework_TestCase 
{ 
/** 
* @test 
* @expectedException 
*/ 
public function leagueMustHaveMaximumEightTeams() 
{ 
// … 
$genericInfo = new LeagueGenericInfo('it', 'my league', 'awesome league'); 
$league = new League($uuid, $genericInfo); 
$team = $this->getMockBuilder('Team') // … 
for ($x=0; $x<=8; $x++) { 
$league->registerTeam($team); 
} 
} 
}
<?php 
class LeagueTest extends PHPUnit_Framework_TestCase 
{ 
/** 
* @test 
* @expectedException 
*/ 
public function leagueMustHaveMaximumEightTeams() 
{ 
// … 
$genericInfo = new LeagueGenericInfo('it', 'my league', 'awesome league'); 
$league = new League($uuid, $genericInfo); 
$team = $this->getMockBuilder('Team') // … 
for ($x=0; $x<=8; $x++) { 
$league->registerTeam($team); 
} 
} 
}
<?php 
class LeagueTest extends PHPUnit_Framework_TestCase 
{ 
/** 
* @test 
* @expectedException 
*/ 
public function leagueMustHaveMaximumEightTeams() 
{ 
// … 
The same Team 
can do more than one registration 
to the League?!? 
$genericInfo = new LeagueGenericInfo('it', 'my league', 'awesome league'); 
$league = new League($uuid, $genericInfo); 
$team = $this->getMockBuilder('Team') // … 
for ($x=0; $x<=8; $x++) { 
$league->registerTeam($team); 
} 
} 
}
<?php 
class League 
{ 
//.. 
public function registerTeam(Team $team) 
{ 
$this->canLeagueAcceptRegistrationOf($team); 
$this->teams->add($team); 
} 
private function canLeagueAcceptRegistrationOf(Team $applicantTeam) 
{ 
if (!$this->canLeagueAcceptAnotherRegistration()) { 
throw new DomainException('Not more places available'); 
} 
foreach ($this->teams as $key => $team) { 
if ($team->getId() == $applicantTeam->getId()) { 
throw new DomainException('Team already registered'); 
} 
} 
} 
}
<?php 
class League 
{ 
//.. 
public function registerTeam(Team $team) 
{ 
$this->canLeagueAcceptRegistrationOf($team); 
$this->teams->add($team); 
} 
And so on ... 
private function canLeagueAcceptRegistrationOf(Team $applicantTeam) 
{ 
if (!$this->canLeagueAcceptAnotherRegistration()) { 
throw new DomainException('Not more places available'); 
} 
foreach ($this->teams as $key => $team) { 
if ($team->getId() == $applicantTeam->getId()) { 
throw new DomainException('Team already registered'); 
} 
} 
} 
}
2. Doctrine (v2.5)
Awareness
We are using the entities of 
the Persistence Model as 
entities of our Domain 
Awareness 
Model
League.orm.yml 
League: 
type: entity 
table: league 
embedded: 
id: 
class: ValueObjectUuid 
genericInfo: 
class: ValueObjectLeagueGenericInfo 
oneToMany: 
contratti: 
targetEntity: Team 
mappedBy: league 
fetch: EXTRA_LAZY
League.orm.yml 
League: 
type: entity 
table: league 
embedded: 
id: 
class: ValueObjectUuid 
genericInfo: 
class: ValueObjectLeagueGenericInfo 
oneToMany: 
contratti: 
targetEntity: Team 
mappedBy: league 
fetch: EXTRA_LAZY
League.orm.yml 
League: 
type: entity 
table: league 
embedded: 
id: 
class: ValueObjectUuid 
genericInfo: 
class: ValueObjectLeagueGenericInfo 
oneToMany: 
contratti: 
targetEntity: Team 
mappedBy: league 
fetch: EXTRA_LAZY 
Uuid.orm.yml 
Uuid: 
type: embeddable 
id: 
uuid: 
type: string 
length: 36
League.orm.yml 
League: 
type: entity 
table: league 
embedded: 
id: 
class: ValueObjectUuid 
genericInfo: 
class: ValueObjectLeagueGenericInfo 
oneToMany: 
contratti: 
targetEntity: Team 
mappedBy: league 
fetch: EXTRA_LAZY 
Uuid.orm.yml 
Uuid: 
type: embeddable 
id: 
uuid: 
type: string 
length: 36
<?php 
class TeamRepository implements TeamRepositoryInterface 
{ 
private $em; 
public function __construct(EntityManager $em) 
{ 
$this->em = $em; 
} 
}
Persisting entities 
<?php 
class TeamRepository implements TeamRepositoryInterface 
{ 
public function add(Team $team) 
{ 
$this->em->persist($team); 
$this->em->flush(); 
} 
}
Avoid collection hydration 
(foreach, toArray) 
<?php 
class TeamRepository implements TeamRepositoryInterface 
{ 
public function getWithoutPlayers($id) 
{ 
$qb = $this->em->createQueryBuilder(); 
$qb 
->select('t', 'p') 
->from("Team", 't') 
->leftJoin('t.players', 'p', Join::WITH, $qb->expr()->andX( 
$qb->expr()->eq('p.id.uuid', ':pid') 
)) 
->where('c.id.uuid = :id') 
->setMaxResults(1); 
$qb->setParameter('id', $id); 
$qb->setParameter('pid', null); 
return $qb->getQuery()->getOneOrNullResult(); 
} 
}
Retrieve an object joined with empty collection 
<?php 
class TeamRepository implements TeamRepositoryInterface 
{ 
public function getWithPlayers($id) 
{ 
$qb = $this->em->createQueryBuilder(); 
$qb 
->select('t', 'p') 
->from("Team", 't') 
->leftJoin(t.players', 'p', Join::WITH, $qb->expr()->andX( 
$qb->expr()->eq('p.status', ':status') 
)) 
->where('t.id.uuid = :id'); 
$qb->setParameter('status', 'on_the_market'); 
$qb->setParameter('id', $id); 
return $qb->getQuery()->getOneOrNullResult(); 
} 
}
Get paginated list of Teams with Player joined 
<?php 
use DoctrineORMToolsPaginationPaginator; 
class TeamRepository implements TeamRepositoryInterface 
{ 
public function paginate($first, $max) 
{ 
$qb = $this->em->createQueryBuilder(); 
$qb 
->select('t', 'p') 
->from("Team", 't') 
->leftJoin('t.players', 'p') 
->setFirstResult($first) 
->setMaxResults($max); 
$paginator = new Paginator($qb->getQuery()); 
return $paginator->getIterator(); 
} 
}
3. Symfony (v2.5)
<?php 
class FirePlayerCommand implements Command 
{ 
public $teamId; 
public $playerId; 
public function getRequest() 
{ 
return new Request( 
[ 
'teamId' => $this->teamId, 
'playerId' => $this->playerId 
] 
); 
} 
}
<?php 
class Request extends ArrayCollection implements RequestInterface 
{ 
public function __construct(array $values) 
{ 
parent::__construct($values); 
} 
public function get($key, $default = null) 
{ 
if (!parent::containsKey($key)) { 
throw new DomainException(); 
} 
$value = parent::get($key); 
if (!$value && $default) { 
return $default; 
} 
return $value; 
} 
}
<?php 
class CommandHandler 
{ 
private $dispatcher; 
private $useCases; 
public function __construct(EventDispatcherInterface $dispatcher) 
{ 
$this->dispatcher = $dispatcher; 
} 
public function registerUseCases($useCases) 
{ 
foreach ($useCases as $useCase) { 
if ($useCase instanceof UseCase) { 
$this->useCases[$useCase->getManagedCommand()] = $useCase; 
} else { 
throw new LogicException(''); 
} 
} 
} 
// ...}
<?php 
class CommandHandler 
{ 
// ... 
public function execute(Command $command) 
{ 
try { 
$this->dispatcher 
->dispatch(Events::PRE_COMMAND, new CommandEvent($command)); 
$this->useCases[get_class($command)]->run($command); 
$response = new Response(); 
$this->dispatcher 
->dispatch(Events::POST_COMMAND, new PostCommandEvent($command, $response)); 
return $response; 
} catch (DomainException $e) { 
$this->dispatcher 
->dispatch(Events::EXCEPTION, new ExceptionEvent($command, $e)); 
return new Response($e->getMessage(), Response::STATUS_KO); 
} 
} 
}
<?php 
class CommandHandler 
{ 
// ... 
public function execute(Command $command) 
{ 
try { 
$this->dispatcher 
->dispatch(Events::PRE_COMMAND, new CommandEvent($command)); 
$this->useCases[get_class($command)]->run($command); 
$response = new Response(); 
$this->dispatcher 
->dispatch(Events::POST_COMMAND, new PostCommandEvent($command, $response)); 
return $response; 
} catch (DomainException $e) { 
$this->dispatcher 
->dispatch(Events::EXCEPTION, new ExceptionEvent($command, $e)); 
return new Response($e->getMessage(), Response::STATUS_KO); 
} 
} 
}
<?php 
class FirePlayerUseCase implements UseCase 
{ 
private $repository; 
public function __construct(TeamRepositoryInterface $repository) 
{ 
$this->repository = $repository; 
} 
public function run(Command $command) 
{ 
$request = $command->getRequest(); 
$team = $this->repository->get( 
$request->get('teamId') 
); 
$team->firePlayer( 
$request->get('playerId') 
); 
$this->repository->add($team); 
} 
}
Commands and Use Cases could be used standalone
<?php 
class CommandHandlerCompilerPass implements CompilerPassInterface 
{ 
public function process(ContainerBuilder $container) 
{ 
if (!$container->hasDefinition('command_handler')) { 
return; 
} 
$definition = $container->getDefinition('command_handler'); 
$taggedServices = $container->findTaggedServiceIds('use_case'); 
$useCases = array(); 
foreach ($taggedServices as $id => $attributes) { 
$useCases[] = new Reference($id); 
} 
$definition->addMethodCall( 
'registerUseCases', 
array($useCases) 
); 
} 
}
<service id="use_case.fire_player" public="false" class="UseCaseFirePlayerUseCase"> 
<argument type="service" id="repository_team"/> 
<tag name="use_case" /> 
</service>
<?php 
class MyController extends Controller 
{ 
public function modifyLeagueAction(Request $request, $id) 
{ 
$reader = $this->get('reader'); 
$league = $reader->getLeague($id); 
$command = ModifyLeagueCommand::fromArray($league); 
$form = $this->createForm(new ModifyLeagueType(), $command); 
$form->handleRequest($request); 
if ($form->isValid()) { 
$commandHandler = $this->get('command_handler'); 
$response = $commandHandler->execute($command); 
if ($response->isOk()) { 
//... 
} 
} 
return array( 
'form' => $form->createView() 
); 
} 
}
<?php 
class MyController extends Controller 
{ 
public function modifyLeagueAction(Request $request, $id) 
{ 
$reader = $this->get('reader'); 
$league = $reader->getLeague($id); 
$command = ModifyLeagueCommand::fromArray($league); 
$form = $this->createForm(new ModifyLeagueType(), $command); 
$form->handleRequest($request); 
if ($form->isValid()) { 
$commandHandler = $this->get('command_handler'); 
$response = $commandHandler->execute($command); 
if ($response->isOk()) { 
//... 
} 
} 
return array( 
'form' => $form->createView() 
); 
} 
}
<?php 
class MyController extends Controller 
{ 
public function modifyLeagueAction(Request $request, $id) 
{ 
$reader = $this->get('reader'); 
Consider using a service for 
$league = $reader->getLeague($id); 
reading $command = ModifyLeagueCommand::operations, fromArray($league); 
instead 
$form = $this->createForm(new ModifyLeagueType(), $command); 
use $form->the handleRequest($repository request); 
directly 
if ($form->isValid()) { 
$commandHandler = $this->get('command_handler'); 
$response = $commandHandler->execute($command); 
if ($response->isOk()) { 
//... 
} 
} 
return array( 
'form' => $form->createView() 
); 
} 
}
<?php 
class ModifyLeagueType extends CreateNewsType 
{ 
public function buildForm(FormBuilderInterface $builder, array $options) 
{ 
$builder 
->add('leagueId’, ‘’hidden') 
->add('name') 
->add('save', 'submit') 
; 
} 
public function setDefaultOptions(OptionsResolverInterface $resolver) 
{ 
$resolver->setDefaults(array( 
'data_class' => ModifyLeagueCommand::CLASS, 
)); 
} 
}
<?php 
class ModifyLeagueCommand implements Command 
{ 
public $leagueId; 
public $name; 
public function getRequest() 
{ 
return new Request( 
[ 
leagueId => $this->leagueId, 
name => $this->name 
] 
); 
} 
}
Command validation 
ModifyLeagueCommand: 
properties: 
leagueId: 
- NotBlank: ~ 
name: 
- NotBlank: ~
Using CQRS and Event Sourcing things change deeply
?
https://joind.in/talk/view/12214
Credits 
A special thanks to @_orso_ the first who told me about rich models 
- Eric Evans - "Domain Driven Design" 
- Vaughn Vernon - “Implementing Domain-Driven Design” 
- http://www.slideshare.net/ziobrando/gestire-la-complessit-con-domain-driven-design 
- http://verraes.net/2013/12/related-entities-vs-child-entities/ 
- http://www.whitewashing.de/2012/08/22/building_an_object_model__no_setters_allowed.html 
- http://www.infoq.com/articles/ddd-contextmapping 
- http://nicolopignatelli.me/valueobjects-a-php-immutable-class-library/ 
- http://welcometothebundle.com/domain-driven-design-and-symfony-for-simple-app/ 
- http://www.slideshare.net/perprogramming/application-layer-33335917 
- http://lostechies.com/jimmybogard/2008/08/21/services-in-domain-driven-design/ 
- http://www.slideshare.net/thinkddd/practical-domain-driven-design-cqrs-and-messaging-architectures 
- http://lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world/ 
- http://gojko.net/2009/09/30/ddd-and-relational-databases-the-value-object-dilemma/
Credits 
- http://verraes.net/2013/06/unbreakable-domain-models/ 
- http://www.mehdi-khalili.com/orm-anti-patterns-part-4-persistence-domain-model 
- http://martinfowler.com/bliki/BoundedContext.html 
- http://www.substanceofcode.com/2007/01/17/from-anemic-to-rich-domain-model/ 
- http://gorodinski.com/blog/2012/04/25/read-models-as-a-tactical-pattern-in-domain-driven-design-ddd/ 
- http://www.sapiensworks.com/blog/post/2013/05/01/DDD-Persisting-Aggregate-Roots-In-A-Unit-Of-Work.aspx 
- http://simon-says-architecture.com/2011/09/06/ddd-by-the-book/ 
- http://scaledagileframework.com/domain-modeling/ 
- http://www.codeproject.com/Articles/555855/Introduction-to-CQRS

Weitere ähnliche Inhalte

Was ist angesagt?

Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDAleix Vergés
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo Hamon
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of LithiumNate Abele
 
PHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkPHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkG Woo
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsRoss Tuck
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksNate Abele
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationSamuel ROZE
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationKirill Chebunin
 
The Origin of Lithium
The Origin of LithiumThe Origin of Lithium
The Origin of LithiumNate Abele
 
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
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Konstantin Kudryashov
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2Hugo Hamon
 
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionLithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionNate Abele
 
When cqrs meets event sourcing
When cqrs meets event sourcingWhen cqrs meets event sourcing
When cqrs meets event sourcingManel Sellés
 

Was ist angesagt? (20)

Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of Lithium
 
PHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkPHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php framework
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and Hobgoblins
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate Frameworks
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony application
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 Application
 
The Origin of Lithium
The Origin of LithiumThe Origin of Lithium
The Origin of Lithium
 
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
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2
 
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionLithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
 
When cqrs meets event sourcing
When cqrs meets event sourcingWhen cqrs meets event sourcing
When cqrs meets event sourcing
 

Andere mochten auch

Clean architecture with ddd layering in php
Clean architecture with ddd layering in phpClean architecture with ddd layering in php
Clean architecture with ddd layering in phpLeonardo Proietti
 
Effective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 DevelopersEffective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 DevelopersMarcin Chwedziak
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricksJavier Eguiluz
 
Enterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and servicesEnterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and servicesAaron Saray
 
Symfony in microservice architecture
Symfony in microservice architectureSymfony in microservice architecture
Symfony in microservice architectureDaniele D'Angeli
 
Living documentation mini-workshop
Living documentation mini-workshopLiving documentation mini-workshop
Living documentation mini-workshopMatthias Noback
 
Performance tips for Symfony2 & PHP
Performance tips for Symfony2 & PHPPerformance tips for Symfony2 & PHP
Performance tips for Symfony2 & PHPMax Romanovsky
 
Increase your performance and code quality
Increase your performance and code qualityIncrease your performance and code quality
Increase your performance and code qualityDusko Vesin
 
Держим руку на пульсе проекта. Мониторинг PHP-приложений
Держим руку на пульсе проекта. Мониторинг PHP-приложенийДержим руку на пульсе проекта. Мониторинг PHP-приложений
Держим руку на пульсе проекта. Мониторинг PHP-приложенийMax Romanovsky
 
Things I Believe Now That I'm Old
Things I Believe Now That I'm OldThings I Believe Now That I'm Old
Things I Believe Now That I'm OldRoss Tuck
 
An Architectural Model for Adapting Domain-Specific AOM Applications
An Architectural Model for Adapting Domain-Specific AOM ApplicationsAn Architectural Model for Adapting Domain-Specific AOM Applications
An Architectural Model for Adapting Domain-Specific AOM Applicationseduardomg23
 
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)Matthias Noback
 
What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012D
 
Autenticazione delle api con jwt e symfony (Italian)
Autenticazione delle api con jwt e symfony (Italian)Autenticazione delle api con jwt e symfony (Italian)
Autenticazione delle api con jwt e symfony (Italian)Marco Albarelli
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Fabien Potencier
 
Building a documented RESTful API in just a few hours with Symfony
Building a documented RESTful API in just a few hours with SymfonyBuilding a documented RESTful API in just a few hours with Symfony
Building a documented RESTful API in just a few hours with Symfonyolrandir
 
Service approach for development REST API in Symfony2
Service approach for development REST API in Symfony2Service approach for development REST API in Symfony2
Service approach for development REST API in Symfony2Sumy PHP User Grpoup
 
Domain Model
Domain ModelDomain Model
Domain Modeldzenanr
 

Andere mochten auch (20)

Clean architecture with ddd layering in php
Clean architecture with ddd layering in phpClean architecture with ddd layering in php
Clean architecture with ddd layering in php
 
Effective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 DevelopersEffective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 Developers
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Enterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and servicesEnterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and services
 
Symfony in microservice architecture
Symfony in microservice architectureSymfony in microservice architecture
Symfony in microservice architecture
 
Living documentation mini-workshop
Living documentation mini-workshopLiving documentation mini-workshop
Living documentation mini-workshop
 
Performance tips for Symfony2 & PHP
Performance tips for Symfony2 & PHPPerformance tips for Symfony2 & PHP
Performance tips for Symfony2 & PHP
 
Increase your performance and code quality
Increase your performance and code qualityIncrease your performance and code quality
Increase your performance and code quality
 
Держим руку на пульсе проекта. Мониторинг PHP-приложений
Держим руку на пульсе проекта. Мониторинг PHP-приложенийДержим руку на пульсе проекта. Мониторинг PHP-приложений
Держим руку на пульсе проекта. Мониторинг PHP-приложений
 
Things I Believe Now That I'm Old
Things I Believe Now That I'm OldThings I Believe Now That I'm Old
Things I Believe Now That I'm Old
 
An Architectural Model for Adapting Domain-Specific AOM Applications
An Architectural Model for Adapting Domain-Specific AOM ApplicationsAn Architectural Model for Adapting Domain-Specific AOM Applications
An Architectural Model for Adapting Domain-Specific AOM Applications
 
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
 
What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012
 
Autenticazione delle api con jwt e symfony (Italian)
Autenticazione delle api con jwt e symfony (Italian)Autenticazione delle api con jwt e symfony (Italian)
Autenticazione delle api con jwt e symfony (Italian)
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
Building a documented RESTful API in just a few hours with Symfony
Building a documented RESTful API in just a few hours with SymfonyBuilding a documented RESTful API in just a few hours with Symfony
Building a documented RESTful API in just a few hours with Symfony
 
Http and REST APIs.
Http and REST APIs.Http and REST APIs.
Http and REST APIs.
 
DDD in PHP
DDD in PHPDDD in PHP
DDD in PHP
 
Service approach for development REST API in Symfony2
Service approach for development REST API in Symfony2Service approach for development REST API in Symfony2
Service approach for development REST API in Symfony2
 
Domain Model
Domain ModelDomain Model
Domain Model
 

Ähnlich wie Rich domain model with symfony 2.5 and doctrine 2.5

Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosDivante
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency InjectionRifat Nabi
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From IusethisMarcus Ramberg
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐいHisateru Tanaka
 
Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Jeff Carouth
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Shinya Ohyanagi
 
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011Alessandro Nadalin
 
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
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxMichelangelo van Dam
 
Apostrophe
ApostropheApostrophe
Apostrophetompunk
 
Как получить чёрный пояс по WordPress?
Как получить чёрный пояс по WordPress?Как получить чёрный пояс по WordPress?
Как получить чёрный пояс по WordPress?Yevhen Kotelnytskyi
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolveXSolve
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceIvan Chepurnyi
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hackingJeroen van Dijk
 

Ähnlich wie Rich domain model with symfony 2.5 and doctrine 2.5 (20)

Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 
Lithium Best
Lithium Best Lithium Best
Lithium Best
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
Apostrophe
ApostropheApostrophe
Apostrophe
 
Как получить чёрный пояс по WordPress?
Как получить чёрный пояс по WordPress?Как получить чёрный пояс по WordPress?
Как получить чёрный пояс по WordPress?
 
PHPSpec BDD for PHP
PHPSpec BDD for PHPPHPSpec BDD for PHP
PHPSpec BDD for PHP
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolve
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
 
Intermediate PHP
Intermediate PHPIntermediate PHP
Intermediate PHP
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 

Kürzlich hochgeladen

Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DaySri Ambati
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 

Kürzlich hochgeladen (20)

Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 

Rich domain model with symfony 2.5 and doctrine 2.5

  • 1. Rich Domain Model with Symfony2 and Doctrine2 Leonardo Proietti @_leopro_ Symfony Day Italy Milan, 10 October 2014
  • 4. domain = problem space domain model = solution space
  • 5. Domain “A Domain [...] is what an organization does and the world it does in.” (Vaughn Vernon, “Implementing Domain-Driven Design”)
  • 6. Domain Model “A model is a simplification. It is an interpretation of reality that abstracts the aspects relevant to solving problem at hand and ignores extraneous detail.” (Eric Evans, "Domain Driven Design")
  • 8. Domain Model “In DDD, domain model refers to a class.” (Julie Lerman, http://msdn.microsoft.com/en-us/magazine/dn385704.aspx)
  • 9. Domain Model “An object model of the domain that incorporates both behavior and data.” (Martin Fowler, http://martinfowler.com/eaaCatalog/domainModel.html)
  • 10. Domain Model “An object model of the domain that incorporates both behavior and data.” (Martin Fowler, http://martinfowler.com/eaaCatalog/domainModel.html)
  • 12. First, because it’s not anemic Martin Fowler http://martinfowler.com/bliki/AnemicDomainModel.html
  • 13. <?php class Anemic { private $shouldNotChangeAfterCreation; private $shouldBeChangedOnlyOnEdit; private $couldBeChangedAnytime; public function setCouldBeChangedAnytime($couldBeChangedAnytime) {} public function setShouldBeChangedOnlyOnEdit($shouldBeChangedOnlyOnEdit) {} public function setShouldNotChangeAfterCreation($shouldNotChangeAfterCreation {} }
  • 14. <?php class SomeService { public function doStuffOnCreation() { $anemic = new Anemic(); $anemic->setCouldBeChangedAnytime('abc'); $anemic->setShouldNotChangeAfterCreation('def'); $unitOfWork->persist($anemic); $unitOfWork->flush(); } }
  • 15. <?php class SomeService { public function doStuffOnEdit() { $anemic = new Anemic(); $anemic->setCouldBeChangedAnytime('abc'); $anemic->setShouldBeChangedOnlyOnEdit(‘123’); $unitOfWork->persist($anemic); $unitOfWork->flush(); } }
  • 16. <?php class SomeService { public function doStuffOnEdit() { Loss of memory $anemic = new Anemic(); $anemic->setCouldBeChangedAnytime('abc'); $anemic->setShouldBeChangedOnlyOnEdit(‘123’); $unitOfWork->persist($anemic); $unitOfWork->flush(); } }
  • 17. <?php class SomeService { public function doStuffOnEdit() { $anemic = new Anemic(); $anemic->setCouldBeChangedAnytime('abc'); $anemic->setShouldBeChangedOnlyOnEdit(‘123’); $anemic->setShouldNotChangeAfterCreation('def'); $unitOfWork->persist($anemic); $unitOfWork->flush(); } }
  • 18. <?php class BehaviouralClass { private $shouldNotChangeAfterCreation; private $shouldBeChangedOnlyOnEdit; private $couldBeChangedAnytime; public function __construct($shouldNotChangeAfterCreation, $couldBeChangedAnytime) { $this->shouldNotChangeAfterCreation = $shouldNotChangeAfterCreation; $this->couldBeChangedAnytime = $couldBeChangedAnytime; } public function modify($shouldBeChangedOnlyOnEdit, $couldBeChangedAnytime = null) { $this->shouldBeChangedOnlyOnEdit = $shouldBeChangedOnlyOnEdit; $this->couldBeChangedAnytime = $couldBeChangedAnytime; } }
  • 19. <?php class BehaviouralClass { private $shouldNotChangeAfterCreation; private $shouldBeChangedOnlyOnEdit; private $couldBeChangedAnytime; It's not still rich, lacks of ... public function __construct($shouldNotChangeAfterCreation, $couldBeChangedAnytime) { $this->shouldNotChangeAfterCreation = $shouldNotChangeAfterCreation; $this->couldBeChangedAnytime = $couldBeChangedAnytime; } public function modify($shouldBeChangedOnlyOnEdit, $couldBeChangedAnytime = null) { $this->shouldBeChangedOnlyOnEdit = $shouldBeChangedOnlyOnEdit; $this->couldBeChangedAnytime = $couldBeChangedAnytime; } }
  • 20. Ubiquitous Language “the domain model can provide the backbone for that common language [...]. The vocabulary of that ubiquitius language includes the names of classes and prominent operations” (Eric Evans, "Domain Driven Design")
  • 21. What does “coffee” mean? Alberto Brandolini AKA @ziobrando
  • 22. A bit of strategy
  • 23. Our domain is an online game that simulate the soccer’s world.
  • 25. What does “player” mean in our domain?
  • 26. The meaning of “player” Within game engine context a model of a real soccer player, modelled with behaviours to fit the requirements of the game engine.
  • 27. The meaning of “player” Within data import context a model of a real soccer player, but modelled for a simple CRUD.
  • 28. The meaning of “player” Within user profile context a model of the user of the website, who plays the game.
  • 29. League Team Calendar Player Lineup Coach Core context
  • 30. League Team Calendar Player Lineup Coach (the player in “user context”) Core context
  • 31. Player Game context User context Uuid Name Roles Uuid Email Password
  • 33. <?php class League { private $id; private $name; private $teams; public function __construct(Uuid $uuid, $name) { $this->id = $uuid; $this->name = $name; $this->teams = new ArrayCollection(); } public function registerTeam(Team $team) { $this->teams->add($team); } }
  • 34. <?php class League { private $id; private $name; private $teams; A team must do a registration public function __construct({ to the league Uuid $uuid, $name) $this->id = $uuid; $this->name = $name; $this->teams = new ArrayCollection(); } public function registerTeam(Team $team) { $this->teams->add($team); } }
  • 35. <?php class League { private $id; private $genericInfo; private $teams; public function __construct(Uuid $uuid, LeagueGenericInfo $leagueGenericInfo) { $this->id = $uuid; $this->genericInfo = $leagueGenericInfo; $this->teams = new ArrayCollection(); } // ...}
  • 36. <?php class LeagueGenericInfo { private $name; private $description; private $country; public function __construct($country, $description, $name) { $this->country = $country; $this->description = $description; $this->name = $name; } // … getters and behaviours}
  • 37. <?php class LeagueGenericInfo { private $name; private $description; private $country; Value object public function __construct($country, $description, $name) { $this->country = $country; $this->description = $description; $this->name = $name; } // … getters and behaviours}
  • 39. <?php class League { // ... public function registerTeam(Team $team) { if (!$this->canLeagueAcceptAnotherRegistration()) { throw new DomainException('Not more places available'); } $this->teams->add($team); } private function canLeagueAcceptAnotherRegistration() { if ($this->teams->count() == 8) { return false; } return true; } }
  • 40. <?php class League { // ... public function registerTeam(Team $team) { if (!$this->canLeagueAcceptAnotherRegistration()) { League protects its invariants throw new DomainException('Not more places available'); } $this->teams->add($team); } private function canLeagueAcceptAnotherRegistration() { if ($this->teams->count() == 8) { return false; } return true; } }
  • 41. <?php class League { // ... public function getTeams() { return $this->teams; } }
  • 42. <?php class League { // ... private function getTeams() { return $this->teams; } }
  • 43. <?php class LeagueGenericInfo { private $name; private $description; private $country; private static $countries; public function __construct($country, $description, $name) { if(!isset(static::$countries)) { static::$countries = require __DIR__.'/countries.php'; } if (!array_key_exists($name, static::$countries)) { throw new UnknownCountryException($country); } $this->country = $country; // .. thanks to Mathias Verraes for “Money” ;-) }}
  • 44. <?php class LeagueGenericInfo { private $name; private $description; private $country; Input validation private static $countries; public function __construct($country, $description, $name) { if(!isset(static::$countries)) { static::$countries = require __DIR__.'/countries.php'; } if (!array_key_exists($name, static::$countries)) { throw new UnknownCountryException($country); } $this->country = $country; // .. thanks to Mathias Verraes for “Money” ;-) }}
  • 45. <?php class LeagueGenericInfo { private $name; private $description; private $country; Could be private static also $countries; placed in commands public function __construct($country, $description, $name) { if(!isset(static::$countries)) { static::$countries = require __DIR__.'/countries.php'; } if (!array_key_exists($name, static::$countries)) { throw new UnknownCountryException($country); } $this->country = $country; // .. thanks to Mathias Verraes for “Money” ;-) }}
  • 46. <?php class League { public function render() { $properties = [ 'id' => $this->id, 'name' => $this->genericInfo->getName(), 'description' => $this->genericInfo->getDescription(), 'country' => $this->genericInfo->getCountry(), ]; $teams = new ArrayCollection(); foreach ($this->teams as $team) { $teams->add($team->render()); } $properties['teams'] = $teams; return new ArrayCollection($properties); } }
  • 47. <?php class League { public function render() { $properties = [ 'id' => $this->id, 'name' => $this->genericInfo->getName(), 'description' => $this->genericInfo->getDescription(), 'country' => $this->genericInfo->getCountry(), ]; Return a read-only object $teams = new ArrayCollection(); foreach ($this->teams as $team) { $teams->add($team->render()); } $properties['teams'] = $teams; return new ArrayCollection($properties); } }
  • 48. <?php class Team { private $id; private $players; public function __construct(Uuid $uuid) { $this->id = $uuid; $this->players = new ArrayCollection(); } public function firePlayer($id) { foreach ($this->players as $key => $player) { if ($player->getId() == $id) { $this->players->remove($key); } } } }
  • 49. <?php class Team { private $id; private $players; Traverse the collections public function __construct(Uuid $uuid) { $this->id = $uuid; $this->players = new ArrayCollection(); } public function firePlayer(Player $playerToFire) { foreach ($this->players as $key => $player) { if ($player->getId() == $playerToFire->getId()) { $this->players->remove($key); } } } }
  • 50. The Player should have a relation towards the Team?
  • 52. <?php class LeagueTest extends PHPUnit_Framework_TestCase { /** * @test * @expectedException */ public function leagueMustHaveMaximumEightTeams() { // … $genericInfo = new LeagueGenericInfo('it', 'my league', 'awesome league'); $league = new League($uuid, $genericInfo); $team = $this->getMockBuilder('Team') // … for ($x=0; $x<=8; $x++) { $league->registerTeam($team); } } }
  • 53. <?php class LeagueTest extends PHPUnit_Framework_TestCase { /** * @test * @expectedException */ public function leagueMustHaveMaximumEightTeams() { // … $genericInfo = new LeagueGenericInfo('it', 'my league', 'awesome league'); $league = new League($uuid, $genericInfo); $team = $this->getMockBuilder('Team') // … for ($x=0; $x<=8; $x++) { $league->registerTeam($team); } } }
  • 54. <?php class LeagueTest extends PHPUnit_Framework_TestCase { /** * @test * @expectedException */ public function leagueMustHaveMaximumEightTeams() { // … The same Team can do more than one registration to the League?!? $genericInfo = new LeagueGenericInfo('it', 'my league', 'awesome league'); $league = new League($uuid, $genericInfo); $team = $this->getMockBuilder('Team') // … for ($x=0; $x<=8; $x++) { $league->registerTeam($team); } } }
  • 55. <?php class League { //.. public function registerTeam(Team $team) { $this->canLeagueAcceptRegistrationOf($team); $this->teams->add($team); } private function canLeagueAcceptRegistrationOf(Team $applicantTeam) { if (!$this->canLeagueAcceptAnotherRegistration()) { throw new DomainException('Not more places available'); } foreach ($this->teams as $key => $team) { if ($team->getId() == $applicantTeam->getId()) { throw new DomainException('Team already registered'); } } } }
  • 56. <?php class League { //.. public function registerTeam(Team $team) { $this->canLeagueAcceptRegistrationOf($team); $this->teams->add($team); } And so on ... private function canLeagueAcceptRegistrationOf(Team $applicantTeam) { if (!$this->canLeagueAcceptAnotherRegistration()) { throw new DomainException('Not more places available'); } foreach ($this->teams as $key => $team) { if ($team->getId() == $applicantTeam->getId()) { throw new DomainException('Team already registered'); } } } }
  • 59. We are using the entities of the Persistence Model as entities of our Domain Awareness Model
  • 60. League.orm.yml League: type: entity table: league embedded: id: class: ValueObjectUuid genericInfo: class: ValueObjectLeagueGenericInfo oneToMany: contratti: targetEntity: Team mappedBy: league fetch: EXTRA_LAZY
  • 61. League.orm.yml League: type: entity table: league embedded: id: class: ValueObjectUuid genericInfo: class: ValueObjectLeagueGenericInfo oneToMany: contratti: targetEntity: Team mappedBy: league fetch: EXTRA_LAZY
  • 62. League.orm.yml League: type: entity table: league embedded: id: class: ValueObjectUuid genericInfo: class: ValueObjectLeagueGenericInfo oneToMany: contratti: targetEntity: Team mappedBy: league fetch: EXTRA_LAZY Uuid.orm.yml Uuid: type: embeddable id: uuid: type: string length: 36
  • 63. League.orm.yml League: type: entity table: league embedded: id: class: ValueObjectUuid genericInfo: class: ValueObjectLeagueGenericInfo oneToMany: contratti: targetEntity: Team mappedBy: league fetch: EXTRA_LAZY Uuid.orm.yml Uuid: type: embeddable id: uuid: type: string length: 36
  • 64. <?php class TeamRepository implements TeamRepositoryInterface { private $em; public function __construct(EntityManager $em) { $this->em = $em; } }
  • 65. Persisting entities <?php class TeamRepository implements TeamRepositoryInterface { public function add(Team $team) { $this->em->persist($team); $this->em->flush(); } }
  • 66. Avoid collection hydration (foreach, toArray) <?php class TeamRepository implements TeamRepositoryInterface { public function getWithoutPlayers($id) { $qb = $this->em->createQueryBuilder(); $qb ->select('t', 'p') ->from("Team", 't') ->leftJoin('t.players', 'p', Join::WITH, $qb->expr()->andX( $qb->expr()->eq('p.id.uuid', ':pid') )) ->where('c.id.uuid = :id') ->setMaxResults(1); $qb->setParameter('id', $id); $qb->setParameter('pid', null); return $qb->getQuery()->getOneOrNullResult(); } }
  • 67. Retrieve an object joined with empty collection <?php class TeamRepository implements TeamRepositoryInterface { public function getWithPlayers($id) { $qb = $this->em->createQueryBuilder(); $qb ->select('t', 'p') ->from("Team", 't') ->leftJoin(t.players', 'p', Join::WITH, $qb->expr()->andX( $qb->expr()->eq('p.status', ':status') )) ->where('t.id.uuid = :id'); $qb->setParameter('status', 'on_the_market'); $qb->setParameter('id', $id); return $qb->getQuery()->getOneOrNullResult(); } }
  • 68. Get paginated list of Teams with Player joined <?php use DoctrineORMToolsPaginationPaginator; class TeamRepository implements TeamRepositoryInterface { public function paginate($first, $max) { $qb = $this->em->createQueryBuilder(); $qb ->select('t', 'p') ->from("Team", 't') ->leftJoin('t.players', 'p') ->setFirstResult($first) ->setMaxResults($max); $paginator = new Paginator($qb->getQuery()); return $paginator->getIterator(); } }
  • 70. <?php class FirePlayerCommand implements Command { public $teamId; public $playerId; public function getRequest() { return new Request( [ 'teamId' => $this->teamId, 'playerId' => $this->playerId ] ); } }
  • 71. <?php class Request extends ArrayCollection implements RequestInterface { public function __construct(array $values) { parent::__construct($values); } public function get($key, $default = null) { if (!parent::containsKey($key)) { throw new DomainException(); } $value = parent::get($key); if (!$value && $default) { return $default; } return $value; } }
  • 72. <?php class CommandHandler { private $dispatcher; private $useCases; public function __construct(EventDispatcherInterface $dispatcher) { $this->dispatcher = $dispatcher; } public function registerUseCases($useCases) { foreach ($useCases as $useCase) { if ($useCase instanceof UseCase) { $this->useCases[$useCase->getManagedCommand()] = $useCase; } else { throw new LogicException(''); } } } // ...}
  • 73. <?php class CommandHandler { // ... public function execute(Command $command) { try { $this->dispatcher ->dispatch(Events::PRE_COMMAND, new CommandEvent($command)); $this->useCases[get_class($command)]->run($command); $response = new Response(); $this->dispatcher ->dispatch(Events::POST_COMMAND, new PostCommandEvent($command, $response)); return $response; } catch (DomainException $e) { $this->dispatcher ->dispatch(Events::EXCEPTION, new ExceptionEvent($command, $e)); return new Response($e->getMessage(), Response::STATUS_KO); } } }
  • 74. <?php class CommandHandler { // ... public function execute(Command $command) { try { $this->dispatcher ->dispatch(Events::PRE_COMMAND, new CommandEvent($command)); $this->useCases[get_class($command)]->run($command); $response = new Response(); $this->dispatcher ->dispatch(Events::POST_COMMAND, new PostCommandEvent($command, $response)); return $response; } catch (DomainException $e) { $this->dispatcher ->dispatch(Events::EXCEPTION, new ExceptionEvent($command, $e)); return new Response($e->getMessage(), Response::STATUS_KO); } } }
  • 75. <?php class FirePlayerUseCase implements UseCase { private $repository; public function __construct(TeamRepositoryInterface $repository) { $this->repository = $repository; } public function run(Command $command) { $request = $command->getRequest(); $team = $this->repository->get( $request->get('teamId') ); $team->firePlayer( $request->get('playerId') ); $this->repository->add($team); } }
  • 76. Commands and Use Cases could be used standalone
  • 77. <?php class CommandHandlerCompilerPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { if (!$container->hasDefinition('command_handler')) { return; } $definition = $container->getDefinition('command_handler'); $taggedServices = $container->findTaggedServiceIds('use_case'); $useCases = array(); foreach ($taggedServices as $id => $attributes) { $useCases[] = new Reference($id); } $definition->addMethodCall( 'registerUseCases', array($useCases) ); } }
  • 78. <service id="use_case.fire_player" public="false" class="UseCaseFirePlayerUseCase"> <argument type="service" id="repository_team"/> <tag name="use_case" /> </service>
  • 79. <?php class MyController extends Controller { public function modifyLeagueAction(Request $request, $id) { $reader = $this->get('reader'); $league = $reader->getLeague($id); $command = ModifyLeagueCommand::fromArray($league); $form = $this->createForm(new ModifyLeagueType(), $command); $form->handleRequest($request); if ($form->isValid()) { $commandHandler = $this->get('command_handler'); $response = $commandHandler->execute($command); if ($response->isOk()) { //... } } return array( 'form' => $form->createView() ); } }
  • 80. <?php class MyController extends Controller { public function modifyLeagueAction(Request $request, $id) { $reader = $this->get('reader'); $league = $reader->getLeague($id); $command = ModifyLeagueCommand::fromArray($league); $form = $this->createForm(new ModifyLeagueType(), $command); $form->handleRequest($request); if ($form->isValid()) { $commandHandler = $this->get('command_handler'); $response = $commandHandler->execute($command); if ($response->isOk()) { //... } } return array( 'form' => $form->createView() ); } }
  • 81. <?php class MyController extends Controller { public function modifyLeagueAction(Request $request, $id) { $reader = $this->get('reader'); Consider using a service for $league = $reader->getLeague($id); reading $command = ModifyLeagueCommand::operations, fromArray($league); instead $form = $this->createForm(new ModifyLeagueType(), $command); use $form->the handleRequest($repository request); directly if ($form->isValid()) { $commandHandler = $this->get('command_handler'); $response = $commandHandler->execute($command); if ($response->isOk()) { //... } } return array( 'form' => $form->createView() ); } }
  • 82. <?php class ModifyLeagueType extends CreateNewsType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('leagueId’, ‘’hidden') ->add('name') ->add('save', 'submit') ; } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => ModifyLeagueCommand::CLASS, )); } }
  • 83. <?php class ModifyLeagueCommand implements Command { public $leagueId; public $name; public function getRequest() { return new Request( [ leagueId => $this->leagueId, name => $this->name ] ); } }
  • 84. Command validation ModifyLeagueCommand: properties: leagueId: - NotBlank: ~ name: - NotBlank: ~
  • 85. Using CQRS and Event Sourcing things change deeply
  • 86.
  • 87. ?
  • 89. Credits A special thanks to @_orso_ the first who told me about rich models - Eric Evans - "Domain Driven Design" - Vaughn Vernon - “Implementing Domain-Driven Design” - http://www.slideshare.net/ziobrando/gestire-la-complessit-con-domain-driven-design - http://verraes.net/2013/12/related-entities-vs-child-entities/ - http://www.whitewashing.de/2012/08/22/building_an_object_model__no_setters_allowed.html - http://www.infoq.com/articles/ddd-contextmapping - http://nicolopignatelli.me/valueobjects-a-php-immutable-class-library/ - http://welcometothebundle.com/domain-driven-design-and-symfony-for-simple-app/ - http://www.slideshare.net/perprogramming/application-layer-33335917 - http://lostechies.com/jimmybogard/2008/08/21/services-in-domain-driven-design/ - http://www.slideshare.net/thinkddd/practical-domain-driven-design-cqrs-and-messaging-architectures - http://lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world/ - http://gojko.net/2009/09/30/ddd-and-relational-databases-the-value-object-dilemma/
  • 90. Credits - http://verraes.net/2013/06/unbreakable-domain-models/ - http://www.mehdi-khalili.com/orm-anti-patterns-part-4-persistence-domain-model - http://martinfowler.com/bliki/BoundedContext.html - http://www.substanceofcode.com/2007/01/17/from-anemic-to-rich-domain-model/ - http://gorodinski.com/blog/2012/04/25/read-models-as-a-tactical-pattern-in-domain-driven-design-ddd/ - http://www.sapiensworks.com/blog/post/2013/05/01/DDD-Persisting-Aggregate-Roots-In-A-Unit-Of-Work.aspx - http://simon-says-architecture.com/2011/09/06/ddd-by-the-book/ - http://scaledagileframework.com/domain-modeling/ - http://www.codeproject.com/Articles/555855/Introduction-to-CQRS

Hinweis der Redaktion

  1. A model is a simplification. It is an interpretation of reality that abstracts the aspects relevant to solving problem at hand and ignores extraneous detail.
  2. il matching uno a uno tra sottodominio e bounded context è una condizione desiderabile, non un vincolo
  3. il modello cambia spesso, tanto quanto la conoscenza che acquisiamo e che ci porta a comprendere come risolvere i problemi; TDD si sposa bene
  4. non accoppiare con le annotations
  5. non accoppiare con le annotations
  6. non accoppiare con le annotations
  7. non accoppiare con le annotations
  8. non accoppiare con le annotations
  9. non accoppiare con le annotations
  10. non accoppiare con le annotations
  11. non accoppiare con le annotations
  12. non accoppiare con le annotations
  13. non accoppiare con le annotations
  14. non accoppiare con le annotations
  15. non accoppiare con le annotations
  16. non accoppiare con le annotations
  17. non accoppiare con le annotations
  18. non accoppiare con le annotations
  19. non accoppiare con le annotations
  20. non accoppiare con le annotations
  21. non accoppiare con le annotations
  22. non accoppiare con le annotations
  23. non accoppiare con le annotations