Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Clean Architecture using
DDD layering in PHP
Leonardo Proietti
@_leopro_
1. Clean Architecture
Definition of Clean Architecture
Definition of Clean Architecture
Independent of Frameworks
Testable
Independent of UI
Independent of Database
Independent ...
Definition of Clean Architecture
Independent of Frameworks
Testable
Independent of UI
Independent of Database
Independent ...
Definition of Clean Architecture
Independent of Frameworks
Testable
Independent of UI
Independent of Database
Independent ...
Definition of Clean Architecture
Independent of Frameworks
Testable
Independent of UI
Independent of Database
Independent ...
Definition of Clean Architecture
Independent of Frameworks
Testable
Independent of UI
Independent of Database
Independent ...
Definition of Clean Architecture
Independent of Frameworks
Testable
Independent of UI
Independent of Database
Independent ...
Hey bro, I respect your opinion but ...
It isn't just my opinion
Do you know "Uncle Bob", isn't it?
I’m just another dwarf.
The Clean Architecture
The Dependency Rule
“This rule says that code dependencies can
only point inwards. Nothing in an inner circle
can know any...
The Dependency Rule
“This rule says that code dependencies can
only point inwards. Nothing in an inner circle
can know any...
2. Domain Driven Design
What is Domain Driven Design?
What is Domain Driven Design?
“it is a way of thinking and a set of priorities,
aimed at accelerating software projects th...
What is Domain Driven Design?
“it is a way of thinking and a set of priorities,
aimed at accelerating software projects th...
What is Domain Driven Design?
“it is a way of thinking and a set of priorities,
aimed at accelerating software projects th...
What is Domain Driven Design?
“is a collection of principles and patterns that
help developers craft elegant object system...
What is Domain Driven Design?
“is a collection of principles and patterns that
help developers craft elegant object system...
What is Domain Driven Design?
“is an approach to software development for
complex needs by connecting the
implementation t...
What is Domain Driven Design?
“is an approach to software development for
complex needs by connecting the
implementation t...
Mmhh interesting … but what does it mean?
Make yourself comfortable
3. DDD Core
Domain
“Every software program relates to some
activity or interest of its user. That subject area
to which the user appli...
Model
“A model is a simplification. It is an
interpretation of reality that abstracts the
aspects relevant to solving prob...
Model
“A model is a simplification. It is an
interpretation of reality that abstracts the
aspects relevant to solving prob...
Sounds familiar?
Model
“A domain model [...] is not just the knowledge
in a domain expert’s head; it is a rigorously
organized and selectiv...
Model
“A domain model [...] is not just the knowledge
in a domain expert’s head; it is a rigorously
organized and selectiv...
Next it’s maybe the most important thing in DDD
Ubiquitous Language
“the domain model can provide the backbone
for that common language [...]. The vocabulary
of that UBIQ...
Ubiquitous Language
It’s a shared jargon between domain experts
and developers, based on Domain Model
Take care of Ubiquitous Language
What does “coffee” mean?
Alberto Brandolini AKA ziobrando
Ubiquitous Language
“changes to the language will be recognized as
changes in the domain model”
(Eric Evans, "Domain Drive...
Context
“The setting in which a word or statement
appears that determines its meaning.”
4. DDD Building Blocks
Entity
An object with “clear identity and a life-cycle
with state transitions that we care about.”
(http://dddsample.sourc...
Are these entities?
It depends.
It depends.
“We don't assign seats on our flights,
so feel free to sit in any available seat”
Value Object
“An object that contains attributes but has no
conceptual identity. They should be treated as
immutable.”
(ht...
Value Object
“A small simple object, like money or a date
range, whose equality isn't based on identity.”
(http://martinfo...
Are these value objects?
In most of the contexts, but ...
Beware about Anemic Domain Model
Beware about Anemic Domain Model
Both Entity and Value Object
should have data and behaviours.
(http://www.martinfowler.co...
Few other concepts
Repository
Aggregate
Domain Event
Repository
“A REPOSITORY represents all objects of a
certain type as a conceptual set. It acts like a
collection, except w...
Repository
“All repositories provide methods that allow
client to request objects matching some
criteria”
(Eric Evans, "Do...
Repository
“Although most queries return an object or a
collection of objects, it also fits within the
concept to return s...
Aggregate
“A DDD aggregate is a cluster of domain
objects that can be treated as a single unit.”
(http://martinfowler.com/...
Aggregate
“DDD aggregates are domain concepts (order,
clinic visit, playlist), while collections are
generic.”
(http://mar...
Domain Event
“Captures the memory of something interesting
which affects the domain”
(http://martinfowler.com/eaaDev/Domai...
How long does it take?
5. DDD Layering
Layering
“We need to decouple the domain objects from
other functions of the system, so we can avoid
confusing the domain ...
Layering
“We need to decouple the domain objects from
other functions of the system, so we can avoid
confusing the domain ...
Layering
(http://guptavikas.wordpress.com/2009/12/01/domain-driven-design-an-introduction/)
Layering
(http://dddsample.sourceforge.net/architecture.html)
Domain
“The domain layer is the heart of the software,
and this is where the interesting stuff happens.”
(http://dddsample...
Application
“The application layer is responsible for driving
the workflow of the application, matching the
use cases at h...
Interface
“This layer holds everything that interacts with
other systems”
(http://dddsample.sourceforge.net/architecture.h...
Interface
“This layer holds everything that interacts with
other systems”
(http://dddsample.sourceforge.net/architecture.h...
Infrastructure
“In simple terms, the infrastructure consists of everything
that exists independently of our application: e...
Separation of concerns
Services
Services
“Sometimes, it just isn’t a thing.”
(Eric Evans, "Domain Driven Design")
Services
Domain Services
Application Services
Infrastructural Services
Domain Services
“If a SERVICE were devised to make
appropriate debits and credits for a found
transfer, that capability wo...
Application Services
“if the banking application can convert and
export our transactions into a spreadsheet file
[...] tha...
Infrastructural Services
“a bank might have an application that sends
an e-mail [...]. The interface that encapsulates
the...
6. Code First
Persistence Ignorance
“In DDD, we don't consider any databases.
DDD is all about the domain, not about the
database, and P...
YAGNI
You aren't gonna need it.
You don’t need a Database or a Framework to
modelling the Domain
YAGNI
You aren't gonna need it.
You don’t need a Database or a Framework to
modelling the Domain
Where should I start then?!?
Understanding the Domain
Talking with domain experts
DDD is Agile, we should be iterative
(http://dddsample.sourceforge.net/architecture.html)
7. Let’s code
Our starting domain
I need a tool to plan my trips.
Every trip must have at least one route
and every route has one or mor...
Code
Here, a complete sample code:
https://github.com/leopro/trip-planner
You can follow the building steps, starting from...
Code
Let’s focus on some steps
composer.json
{
"name": "my trip planner",
"autoload": {
"psr-0": { "": "src/" }
},
"require": {
"php": ">=5.3.3",
"doctri...
composer.json
{
"name": "my trip planner",
"autoload": {
"psr-0": { "": "src/" }
},
"require": {
"php": ">=5.3.3",
"doctri...
composer.json
{
"name": "my trip planner",
"autoload": {
"psr-0": { "": "src/" }
},
"require": {
"php": ">=5.3.3",
"doctri...
composer.json
{
"name": "my trip planner",
"autoload": {
"psr-0": { "": "src/" }
},
"require": {
"php": ">=5.3.3",
"doctri...
composer.json
{
"name": "my trip planner",
"autoload": {
"psr-0": { "": "src/" }
},
"require": {
"php": ">=5.3.3",
"doctri...
<?php
namespace LeoproTripPlannerDomainContract;
use DoctrineCommonCollectionsCollection as DoctrineCollection;
interface ...
<?php
namespace LeoproTripPlannerDomainAdapter;
use DoctrineCommonCollectionsArrayCollection as DoctrineArrayCollection;
u...
Domain
Our starting domain
I need a tool to plan my trips.
Every trip must have at least one route
and every route has one or mor...
<?php
namespace LeoproTripPlannerDomainTests;
use LeoproTripPlannerDomainEntityTrip;
class TripTest extends PHPUnit_Framew...
<?php
namespace LeoproTripPlannerDomainEntity;
use LeoproTripPlannerDomainAdapterArrayCollection;
class Trip
{
private $na...
<?php
namespace LeoproTripPlannerDomainEntity;
class Route
{
}
Our starting domain
I need a tool to plan my trips.
Every trip must have at least one route
and every route has one or mor...
<?php
namespace LeoproTripPlannerDomainTests;
use LeoproTripPlannerDomainEntityRoute;
class RouteTest extends PHPUnit_Fram...
<?php
namespace LeoproTripPlannerDomainEntity;
class Route
{
private $name;
private $legs;
private function __construct($n...
<?php
namespace LeoproTripPlannerDomainEntity;
class Route
{
private $name;
private $legs;
private function __construct($n...
The model is changing
I need a tool to plan my trips.
Every trip must have at least one route
and every route has one or m...
/**
* @expectedException ...DateAlreadyUsedException
*/
public function testNoDuplicationDateForTheSameRoute()
{
$route = ...
<?php
namespace LeoproTripPlannerDomainEntity;
class Route
{
//...
public function addLeg($date)
{
$leg = Leg::create($dat...
Our starting domain
I need a tool to plan my trips.
Every trip must have at least one route
and every route has one or mor...
<?php
namespace LeoproTripPlannerDomainTests;
use LeoproTripPlannerDomainEntityLeg;
class LegTest extends PHPUnit_Framewor...
<?php
namespace LeoproTripPlannerDomainEntity;
use LeoproTripPlannerDomainValueObjectDate;
class Leg
{
private $date;
priv...
<?php
namespace LeoproTripPlannerDomainEntity;
use LeoproTripPlannerDomainValueObjectPoint;
class Location
{
private $name...
<?php
namespace LeoproTripPlannerDomainTests;
use LeoproTripPlannerDomainValueObjectPoint;
class PointTest extends PHPUnit...
<?php
namespace LeoproTripPlannerDomainTests;
use LeoproTripPlannerDomainValueObjectPoint;
class PointTest extends PHPUnit...
<?php
namespace LeoproTripPlannerDomainValueObject;
class Point
{
private $latitude;
private $longitude;
public function _...
public function getCartographicDistance(Point $point)
{
$earthRadius = 3958.75;
$dLat = deg2rad($point->getLatitude() - $t...
public function getCartographicDistance(Point $point)
{
$earthRadius = 3958.75;
$dLat = deg2rad($point->getLatitude() - $t...
Application
<?php
namespace LeoproTripPlannerApplicationCommand;
use LeoproTripPlannerApplicationUseCaseUseCaseInterface;
class Comman...
<?php
namespace LeoproTripPlannerApplicationCommand;
use LeoproTripPlannerApplicationUseCaseUseCaseInterface;
class Comman...
<?php
namespace LeoproTripPlannerApplicationCommand;
use LeoproTripPlannerApplicationUseCaseUseCaseInterface;
class Comman...
<?php
namespace LeoproTripPlannerApplicationCommand;
use LeoproTripPlannerApplicationContractCommandInterface;
use LeoproT...
<?php
namespace LeoproTripPlannerApplicationUseCase;
class CreateTripUseCase extends AbstractUseCase implements UseCaseInt...
<?php
namespace LeoproTripPlannerApplicationUseCase;
class CreateTripUseCase extends AbstractUseCase implements UseCaseInt...
<?php
namespace LeoproTripPlannerDomainContract;
use LeoproTripPlannerDomainEntityTrip;
use LeoproTripPlannerDomainValueOb...
<?php
namespace LeoproTripPlannerDomainContract;
use LeoproTripPlannerDomainEntityTrip;
use LeoproTripPlannerDomainValueOb...
<?php
namespace LeoproTripPlannerApplicationContract;
interface Validator
{
/**
* @param $value
* @return LeoproTripPlanne...
About validation
In DDD, entities should be always
valid.
About validation
But if you ask
“where do I put validation?”
you'll get different answers.
About validation
If you are using commands,
validate the command itself, is a
good trade-off.
Infrastructure
Framework's revenge
composer.json
"require": {
"php": ">=5.3.3",
"doctrine/collections": "v1.2",
"symfony/symfony": "~2.4",
"doctrine/dbal": "...
Mapping entities
app/config/config.yml
orm:
auto_generate_proxy_classes: "%kernel.debug%"
auto_mapping: false
mappings:
TripPlannerDomain:
...
InfrastructureBundle/Resources/config/entity/Route.orm.yml
LeoproTripPlannerDomainEntityTrip:
type: entity
table: trip
emb...
Validate commands
InfrastructureBundle/Resources/config/validation.yml
LeoproTripPlannerApplicationCommandCreateTripCommand:
properties:
nam...
Configuring services
src/LeoPro/TripPlanner/InfrastructureBundle/Resources/config/services.xml
<services>
<!-- Exposed Services -->
<service id...
src/LeoPro/TripPlanner/InfrastructureBundle/Resources/config/services.xml
<services>
<!-- Not Exposed Services -->
<servic...
src/LeoPro/TripPlanner/InfrastructureBundle/Resources/config/services.xml
<services>
<!-- Adapter -->
<service id="infrast...
Adapter
Ops … some parts of the
frameworks do not fit our
interfaces.
<?php
namespace LeoproTripPlannerInfrastructureBundleAdapter;
use LeoproTripPlannerApplicationContractValidator as Applica...
Repository
<?php
namespace LeoproTripPlannerInfrastructureBundleRepository;
use DoctrineORMEntityManager;
use LeoproTripPlannerDomain...
<?php
namespace LeoproTripPlannerInfrastructureBundleRepository;
use DoctrineORMEntityManager;
use LeoproTripPlannerDomain...
<?php
namespace LeoproTripPlannerInfrastructureBundleRepository;
use DoctrineORMEntityManager;
use LeoproTripPlannerDomain...
<?php
namespace LeoproTripPlannerInfrastructureBundleRepository;
use DoctrineORMEntityManager;
use LeoproTripPlannerDomain...
<?php
namespace LeoproTripPlannerInfrastructureBundleRepository;
use LeoproTripPlannerDomainContractTripRepository as Trip...
<?php
namespace LeoproTripPlannerInfrastructureBundleRepository;
use LeoproTripPlannerDomainContractTripRepository as Trip...
Presentation
<?php
namespace LeoproTripPlannerPresentationBundleController;
use LeoproTripPlannerPresentationBundleFormTypeCreateTripTy...
<?php
namespace LeoproTripPlannerPresentationBundleFormType;
use LeoproTripPlannerApplicationCommandCreateTripCommand;
cla...
One step to the finish line
What have I learned?
A clean architecture helps in avoiding the Big Ball of Mud.
Also starting with a very simple domain
Iteration by iteration
Complexity could grow
If our system is tightly coupled
and the domain is scattered
we are losing the chance of responding to changes.
Talking about testing ...
… independence from frameworks, database, UI ...
… means talking about business.
Let the code speak the language of the business
First, taking care of the model
Then choosing the right tool
...
We reached the finish line, well done.
Thank you :-)
Credits
● Eric Evans, - "Domain Driven Design"
● “Uncle Bob” - http://blog.8thlight.com/uncle-bob/archive.html and books
●...
Credits
● http://devlicio.us/blogs/casey/archive/2009/02/16/ddd-aggregates-and-
aggregate-roots.aspx
● http://www.slidesha...
Credits
● http://www.slideshare.net/ziobrando/gestire-la-complessit-con-domain-
driven-design
● http://lostechies.com/jimm...
Credits
● http://lostechies.com/jamesgregory/2009/05/09/entity-interface-anti-pattern
● http://www.slideshare.net/piotrpel...
Clean architecture with ddd layering in php
Upcoming SlideShare
Loading in …5
×

Clean architecture with ddd layering in php

34,835 views

Published on

The slides of my talk at PUGRoma.

Here, a complete sample code
https://github.com/leopro/trip-planner

Presentation is also here: http://t.co/5EK56yYBmQ

Published in: Internet, Technology, Business

Clean architecture with ddd layering in php

  1. 1. Clean Architecture using DDD layering in PHP Leonardo Proietti @_leopro_
  2. 2. 1. Clean Architecture
  3. 3. Definition of Clean Architecture
  4. 4. Definition of Clean Architecture Independent of Frameworks Testable Independent of UI Independent of Database Independent of any external agency
  5. 5. Definition of Clean Architecture Independent of Frameworks Testable Independent of UI Independent of Database Independent of any external agency
  6. 6. Definition of Clean Architecture Independent of Frameworks Testable Independent of UI Independent of Database Independent of any external agency
  7. 7. Definition of Clean Architecture Independent of Frameworks Testable Independent of UI Independent of Database Independent of any external agency
  8. 8. Definition of Clean Architecture Independent of Frameworks Testable Independent of UI Independent of Database Independent of any external agency
  9. 9. Definition of Clean Architecture Independent of Frameworks Testable Independent of UI Independent of Database Independent of any external agency
  10. 10. Hey bro, I respect your opinion but ...
  11. 11. It isn't just my opinion
  12. 12. Do you know "Uncle Bob", isn't it?
  13. 13. I’m just another dwarf.
  14. 14. The Clean Architecture
  15. 15. The Dependency Rule “This rule says that code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle.” (http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html)
  16. 16. The Dependency Rule “This rule says that code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle.” (http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html)
  17. 17. 2. Domain Driven Design
  18. 18. What is Domain Driven Design?
  19. 19. What is Domain Driven Design? “it is a way of thinking and a set of priorities, aimed at accelerating software projects that have to deal with complicated domains” (Eric Evans, "Domain Driven Design")
  20. 20. What is Domain Driven Design? “it is a way of thinking and a set of priorities, aimed at accelerating software projects that have to deal with complicated domains” (Eric Evans, "Domain Driven Design")
  21. 21. What is Domain Driven Design? “it is a way of thinking and a set of priorities, aimed at accelerating software projects that have to deal with complicated domains” (Eric Evans, "Domain Driven Design")
  22. 22. What is Domain Driven Design? “is a collection of principles and patterns that help developers craft elegant object systems” (http://msdn.microsoft.com/en-us/magazine/dd419654.aspx)
  23. 23. What is Domain Driven Design? “is a collection of principles and patterns that help developers craft elegant object systems” (http://msdn.microsoft.com/en-us/magazine/dd419654.aspx)
  24. 24. What is Domain Driven Design? “is an approach to software development for complex needs by connecting the implementation to an evolving model” (http://en.wikipedia.org/wiki/Domain-driven_design)
  25. 25. What is Domain Driven Design? “is an approach to software development for complex needs by connecting the implementation to an evolving model” (http://en.wikipedia.org/wiki/Domain-driven_design)
  26. 26. Mmhh interesting … but what does it mean?
  27. 27. Make yourself comfortable
  28. 28. 3. DDD Core
  29. 29. Domain “Every software program relates to some activity or interest of its user. That subject area to which the user applies the program is the domain of the software” (Eric Evans, "Domain Driven Design")
  30. 30. 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")
  31. 31. 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")
  32. 32. Sounds familiar?
  33. 33. Model “A domain model [...] is not just the knowledge in a domain expert’s head; it is a rigorously organized and selective abstraction of that knowledge.” (Eric Evans, "Domain Driven Design")
  34. 34. Model “A domain model [...] is not just the knowledge in a domain expert’s head; it is a rigorously organized and selective abstraction of that knowledge.” (Eric Evans, "Domain Driven Design")
  35. 35. Next it’s maybe the most important thing in DDD
  36. 36. Ubiquitous Language “the domain model can provide the backbone for that common language [...]. The vocabulary of that UBIQUITOUS LANGUAGE includes the names of classes and prominent operations” (Eric Evans, "Domain Driven Design")
  37. 37. Ubiquitous Language It’s a shared jargon between domain experts and developers, based on Domain Model
  38. 38. Take care of Ubiquitous Language
  39. 39. What does “coffee” mean? Alberto Brandolini AKA ziobrando
  40. 40. Ubiquitous Language “changes to the language will be recognized as changes in the domain model” (Eric Evans, "Domain Driven Design")
  41. 41. Context “The setting in which a word or statement appears that determines its meaning.”
  42. 42. 4. DDD Building Blocks
  43. 43. Entity An object with “clear identity and a life-cycle with state transitions that we care about.” (http://dddsample.sourceforge.net/characterization.html)
  44. 44. Are these entities?
  45. 45. It depends.
  46. 46. It depends. “We don't assign seats on our flights, so feel free to sit in any available seat”
  47. 47. Value Object “An object that contains attributes but has no conceptual identity. They should be treated as immutable.” (http://en.wikipedia.org/wiki/Domain-driven_design)
  48. 48. Value Object “A small simple object, like money or a date range, whose equality isn't based on identity.” (http://martinfowler.com/eaaCatalog/valueObject.html)
  49. 49. Are these value objects?
  50. 50. In most of the contexts, but ...
  51. 51. Beware about Anemic Domain Model
  52. 52. Beware about Anemic Domain Model Both Entity and Value Object should have data and behaviours. (http://www.martinfowler.com/bliki/AnemicDomainModel.html)
  53. 53. Few other concepts Repository Aggregate Domain Event
  54. 54. Repository “A REPOSITORY represents all objects of a certain type as a conceptual set. It acts like a collection, except with more elaborate querying capability” (Eric Evans, "Domain Driven Design")
  55. 55. Repository “All repositories provide methods that allow client to request objects matching some criteria” (Eric Evans, "Domain Driven Design")
  56. 56. Repository “Although most queries return an object or a collection of objects, it also fits within the concept to return some types of summary calculation” (Eric Evans, "Domain Driven Design")
  57. 57. Aggregate “A DDD aggregate is a cluster of domain objects that can be treated as a single unit.” (http://martinfowler.com/bliki/DDD_Aggregate.html)
  58. 58. Aggregate “DDD aggregates are domain concepts (order, clinic visit, playlist), while collections are generic.” (http://martinfowler.com/bliki/DDD_Aggregate.html)
  59. 59. Domain Event “Captures the memory of something interesting which affects the domain” (http://martinfowler.com/eaaDev/DomainEvent.html)
  60. 60. How long does it take?
  61. 61. 5. DDD Layering
  62. 62. Layering “We need to decouple the domain objects from other functions of the system, so we can avoid confusing the domain concepts wiht other concepts” (Eric Evans, "Domain Driven Design")
  63. 63. Layering “We need to decouple the domain objects from other functions of the system, so we can avoid confusing the domain concepts wiht other concepts” (Eric Evans, "Domain Driven Design")
  64. 64. Layering (http://guptavikas.wordpress.com/2009/12/01/domain-driven-design-an-introduction/)
  65. 65. Layering (http://dddsample.sourceforge.net/architecture.html)
  66. 66. Domain “The domain layer is the heart of the software, and this is where the interesting stuff happens.” (http://dddsample.sourceforge.net/architecture.html)
  67. 67. Application “The application layer is responsible for driving the workflow of the application, matching the use cases at hand” (http://dddsample.sourceforge.net/architecture.html)
  68. 68. Interface “This layer holds everything that interacts with other systems” (http://dddsample.sourceforge.net/architecture.html)
  69. 69. Interface “This layer holds everything that interacts with other systems” (http://dddsample.sourceforge.net/architecture.html) Controller Form View API
  70. 70. Infrastructure “In simple terms, the infrastructure consists of everything that exists independently of our application: external libraries, database engine, application server, messaging backend and so on.” (http://dddsample.sourceforge.net/architecture.html)
  71. 71. Separation of concerns
  72. 72. Services
  73. 73. Services “Sometimes, it just isn’t a thing.” (Eric Evans, "Domain Driven Design")
  74. 74. Services Domain Services Application Services Infrastructural Services
  75. 75. Domain Services “If a SERVICE were devised to make appropriate debits and credits for a found transfer, that capability would belong in the domain layer” (Eric Evans, "Domain Driven Design")
  76. 76. Application Services “if the banking application can convert and export our transactions into a spreadsheet file [...] that export is an application SERVICE” (Eric Evans, "Domain Driven Design")
  77. 77. Infrastructural Services “a bank might have an application that sends an e-mail [...]. The interface that encapsulates the email system, [...] is a SERVICE in the infrastructure layer” (Eric Evans, "Domain Driven Design")
  78. 78. 6. Code First
  79. 79. Persistence Ignorance “In DDD, we don't consider any databases. DDD is all about the domain, not about the database, and Persistence Ignorance (PI) is a very important aspect of DDD” (http://williamdurand.fr/2013/08/20/ddd-with-symfony2-making-things-clear/)
  80. 80. YAGNI You aren't gonna need it. You don’t need a Database or a Framework to modelling the Domain
  81. 81. YAGNI You aren't gonna need it. You don’t need a Database or a Framework to modelling the Domain
  82. 82. Where should I start then?!?
  83. 83. Understanding the Domain
  84. 84. Talking with domain experts
  85. 85. DDD is Agile, we should be iterative (http://dddsample.sourceforge.net/architecture.html)
  86. 86. 7. Let’s code
  87. 87. Our starting domain I need a tool to plan my trips. Every trip must have at least one route and every route has one or more leg. A leg has one date and one location.
  88. 88. Code Here, a complete sample code: https://github.com/leopro/trip-planner You can follow the building steps, starting from the first commit.
  89. 89. Code Let’s focus on some steps
  90. 90. composer.json { "name": "my trip planner", "autoload": { "psr-0": { "": "src/" } }, "require": { "php": ">=5.3.3", "doctrine/collections": "v1.2", }, "require-dev": { "phpunit/phpunit": "4.0.*" }, "config": { "bin-dir": "bin" } }
  91. 91. composer.json { "name": "my trip planner", "autoload": { "psr-0": { "": "src/" } }, "require": { "php": ">=5.3.3", "doctrine/collections": "v1.2", }, "require-dev": { "phpunit/phpunit": "3.7.*" }, "config": { "bin-dir": "bin" } } I don't need anything more to start
  92. 92. composer.json { "name": "my trip planner", "autoload": { "psr-0": { "": "src/" } }, "require": { "php": ">=5.3.3", "doctrine/collections": "v1.2", }, "require-dev": { "phpunit/phpunit": "3.7.*" }, "config": { "bin-dir": "bin" } } Ok, I have a dependency on doctrine/collections ...
  93. 93. composer.json { "name": "my trip planner", "autoload": { "psr-0": { "": "src/" } }, "require": { "php": ">=5.3.3", "doctrine/collections": "v1.2", }, "require-dev": { "phpunit/phpunit": "3.7.*" }, "config": { "bin-dir": "bin" } } … the missing (SPL) Collection/Array/OrderedMap interface
  94. 94. composer.json { "name": "my trip planner", "autoload": { "psr-0": { "": "src/" } }, "require": { "php": ">=5.3.3", "doctrine/collections": "v1.2", }, "require-dev": { "phpunit/phpunit": "3.7.*" }, "config": { "bin-dir": "bin" } } Anyway, you can put a boundary
  95. 95. <?php namespace LeoproTripPlannerDomainContract; use DoctrineCommonCollectionsCollection as DoctrineCollection; interface Collection extends DoctrineCollection {}
  96. 96. <?php namespace LeoproTripPlannerDomainAdapter; use DoctrineCommonCollectionsArrayCollection as DoctrineArrayCollection; use LeoproTripPlannerDomainContractCollection; class ArrayCollection extends DoctrineArrayCollection implements Collection {}
  97. 97. Domain
  98. 98. Our starting domain I need a tool to plan my trips. Every trip must have at least one route and every route has one or more leg. A leg has one date and one location.
  99. 99. <?php namespace LeoproTripPlannerDomainTests; use LeoproTripPlannerDomainEntityTrip; class TripTest extends PHPUnit_Framework_TestCase { public function testCreateTripReturnATripWithFirstRoute() { $trip = Trip::create('my first planning'); $this->assertInstanceOf('LeoproTripPlannerDomainEntityTrip', $trip); $this->assertEquals(1, $trip->getRoutes()->count()); } }
  100. 100. <?php namespace LeoproTripPlannerDomainEntity; use LeoproTripPlannerDomainAdapterArrayCollection; class Trip { private $name, private $routes; private function __construct($name, Route $route) { $this->name = $name; $this->routes = new ArrayCollection(array($route)); } public function create($name) { return new self($name, new Route); } public function getRoutes() { return $this->routes; } }
  101. 101. <?php namespace LeoproTripPlannerDomainEntity; class Route { }
  102. 102. Our starting domain I need a tool to plan my trips. Every trip must have at least one route and every route has one or more leg. A leg has one date and one location.
  103. 103. <?php namespace LeoproTripPlannerDomainTests; use LeoproTripPlannerDomainEntityRoute; class RouteTest extends PHPUnit_Framework_TestCase { public function testCreateRouteAddingALeg() { $route = Route::create('my first trip'); $route->addLeg('06-06-2014'); $this->assertEquals(1, $route->getLegs()->count()); }
  104. 104. <?php namespace LeoproTripPlannerDomainEntity; class Route { private $name; private $legs; private function __construct($name) { $this->name = $name; $this->legs = new ArrayCollection(); } public static function create($tripName) { return new self('first route for trip: ' . $tripName); } public function addLeg($date) { $leg = Leg::create($date); $this->legs->add($leg); } //...
  105. 105. <?php namespace LeoproTripPlannerDomainEntity; class Route { private $name; private $legs; private function __construct($name) { $this->name = $name; $this->legs = new ArrayCollection(); } public static function create($tripName) { return new self('first route for trip: ' . $tripName); } public function addLeg($date) { $leg = Leg::create($date); $this->legs->add($leg); } //... Wait, we really want two legs with the same date?
  106. 106. The model is changing I need a tool to plan my trips. Every trip must have at least one route and every route has one or more leg and two leg with the same date for the same route are not allowed . A leg has one date and one location.
  107. 107. /** * @expectedException ...DateAlreadyUsedException */ public function testNoDuplicationDateForTheSameRoute() { $route = Route::create('my first trip'); $route->addLeg('06-06-2014'); $route->addLeg('06-06-2014'); }
  108. 108. <?php namespace LeoproTripPlannerDomainEntity; class Route { //... public function addLeg($date) { $leg = Leg::create($date); $dateAlreadyUsed = function($key, $element) use($leg) { return $element->getDate() == $leg->getDate(); }; if ($this->legs->exists($dateAlreadyUsed)) { throw new DateAlreadyUsedException($date . ' already used'); } $this->legs->add($leg); } //...
  109. 109. Our starting domain I need a tool to plan my trips. Every trip must have at least one route and every route has one or more leg. A leg has one date and one location.
  110. 110. <?php namespace LeoproTripPlannerDomainTests; use LeoproTripPlannerDomainEntityLeg; class LegTest extends PHPUnit_Framework_TestCase { public function testCreateLegReturnsALegWithDateAndLocation() { $leg = Leg::create('01/01/2014', 'd/m/Y', -3.386665, 36.736908); $this->assertInstanceOf('LeoproTripPlannerDomainEntityLeg', $leg); $location = $leg->getLocation(); $this->assertInstanceOf('LeoproTripPlannerDomainEntityLocation', $location); $point = $location->getPoint(); $this->assertInstanceOf('LeoproTripPlannerDomainValueObjectPoint', $point); $this->assertEquals(-3.386665, $point->getLatitude()); $this->assertEquals(36.736908, $point->getLongitude()); } }
  111. 111. <?php namespace LeoproTripPlannerDomainEntity; use LeoproTripPlannerDomainValueObjectDate; class Leg { private $date; private $location; private function __construct(Date $date, Location $location) { $this->date = $date; $this->location = $location; } public static function create($date, $dateFormat, $latitude, $longitude) { $date = new Date($date, $dateFormat); return new self( $date, Location::create($date->getFormattedDate(), $latitude, $longitude) ); } //..
  112. 112. <?php namespace LeoproTripPlannerDomainEntity; use LeoproTripPlannerDomainValueObjectPoint; class Location { private $name; private $point; private function __construct($name, Point $point) { $this->name = $name; $this->point = $point; } public static function create($name, $latitude, $longitude) { return new self($name, new Point($latitude, $longitude) ); } public function getPoint() { return $this->point; } }
  113. 113. <?php namespace LeoproTripPlannerDomainTests; use LeoproTripPlannerDomainValueObjectPoint; class PointTest extends PHPUnit_Framework_TestCase { public function testDistance() { $firstPoint = new Point(-3.386665, 36.736908); $secondPoint = new Point(-3.428112, 35.932846); $this->assertEquals(89, $firstPoint->getCartographicDistance($secondPoint)); $this->assertEquals(98, $firstPoint->getApproximateRoadDistance($secondPoint)); } }
  114. 114. <?php namespace LeoproTripPlannerDomainTests; use LeoproTripPlannerDomainValueObjectPoint; class PointTest extends PHPUnit_Framework_TestCase { public function testDistance() { $firstPoint = new Point(-3.386665, 36.736908); $secondPoint = new Point(-3.428112, 35.932846); $this->assertEquals(89, $firstPoint->getCartographicDistance($secondPoint)); $this->assertEquals(98, $firstPoint->getApproximateRoadDistance($secondPoint)); } } Value Object getCartographicDistance() getApproximateRoadDistance()
  115. 115. <?php namespace LeoproTripPlannerDomainValueObject; class Point { private $latitude; private $longitude; public function __construct($latitude, $longitude) { $this->latitude = $latitude; $this->longitude = $longitude; } //.. public function getApproximateRoadDistance(Point $point, $degreeApproximation = 10) { $distance = $this->getCartographicDistance($point); return round($distance + $distance * ($degreeApproximation / 100)); }
  116. 116. public function getCartographicDistance(Point $point) { $earthRadius = 3958.75; $dLat = deg2rad($point->getLatitude() - $this->latitude); $dLng = deg2rad($point->getLongitude() - $this->longitude); $a = sin($dLat / 2) * sin($dLat / 2) + cos(deg2rad($this->latitude)) * cos(deg2rad($point->getLatitude())) * sin($dLng / 2) * sin($dLng / 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); $dist = $earthRadius * $c; $meterConversion = 1.609344; $geopointDistance = $dist * $meterConversion; return round($geopointDistance, 0); }
  117. 117. public function getCartographicDistance(Point $point) { $earthRadius = 3958.75; $dLat = deg2rad($point->getLatitude() - $this->latitude); $dLng = deg2rad($point->getLongitude() - $this->longitude); $a = sin($dLat / 2) * sin($dLat / 2) + cos(deg2rad($this->latitude)) * cos(deg2rad($point->getLatitude())) * sin($dLng / 2) * sin($dLng / 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); $dist = $earthRadius * $c; $meterConversion = 1.609344; $geopointDistance = $dist * $meterConversion; return round($geopointDistance, 0); } Got the point?
  118. 118. Application
  119. 119. <?php namespace LeoproTripPlannerApplicationCommand; use LeoproTripPlannerApplicationUseCaseUseCaseInterface; class CommandHandler { private $useCases; public function registerCommands(array $useCases) { foreach ($useCases as $useCase) { if ($useCase instanceof UseCaseInterface) { $this->useCases[$useCase->getManagedCommand()] = $useCase; } else { throw new LogicException(‘...'); } } } //...
  120. 120. <?php namespace LeoproTripPlannerApplicationCommand; use LeoproTripPlannerApplicationUseCaseUseCaseInterface; class CommandHandler { //... public function execute($command) { try { $commandClass = get_class($command); if (!array_key_exists($commandClass, $this->useCases)) { throw new LogicException($commandClass . ' is not a managed command'); } $this->useCases[get_class($command)]->run($command); } catch (Exception $e) { throw $e; } } }
  121. 121. <?php namespace LeoproTripPlannerApplicationCommand; use LeoproTripPlannerApplicationUseCaseUseCaseInterface; class CommandHandler { //... public function execute($command) { try { $commandClass = get_class($command); if (!array_key_exists($commandClass, $this->useCases)) { throw new LogicException($commandClass . ' is not a managed command'); } $this->useCases[get_class($command)]->run($command); } catch (Exception $e) { throw $e; } } } You can move the state of the domain, through commands
  122. 122. <?php namespace LeoproTripPlannerApplicationCommand; use LeoproTripPlannerApplicationContractCommandInterface; use LeoproTripPlannerDomainAdapterArrayCollection; class CreateTripCommand implements CommandInterface { private $name; public function __construct($name) { $this->name = $name; } public function getRequest() { return new ArrayCollection( array( 'name' => $this->name ) ); } }
  123. 123. <?php namespace LeoproTripPlannerApplicationUseCase; class CreateTripUseCase extends AbstractUseCase implements UseCaseInterface { private $tripRepository; public function __construct(TripRepository $tripRepository) { $this->tripRepository = $tripRepository; } public function run(CommandInterface $command) { $this->exceptionIfCommandNotManaged($command); $request = $command->getRequest(); $trip = Trip::createWithFirstRoute(new TripIdentity(uniqid()), $request->get('name')); $this->tripRepository->add($trip); return $trip; } }
  124. 124. <?php namespace LeoproTripPlannerApplicationUseCase; class CreateTripUseCase extends AbstractUseCase implements UseCaseInterface { private $tripRepository; public function __construct(TripRepository $tripRepository) { $this->tripRepository = $tripRepository; } public function run(CommandInterface $command) { $this->exceptionIfCommandNotManaged($command); $request = $command->getRequest(); $trip = Trip::createWithFirstRoute(new TripIdentity(uniqid()), $request->get('name')); $this->tripRepository->add($trip); return $trip; } } Defining a TripRepository interface ...
  125. 125. <?php namespace LeoproTripPlannerDomainContract; use LeoproTripPlannerDomainEntityTrip; use LeoproTripPlannerDomainValueObjectTripIdentity; interface TripRepository { /** * @param TripIdentity $identity * @return LeoproTripPlannerDomainEntityTrip */ public function get(TripIdentity $identity); /** * @param Trip $trip * @return void */ public function add(Trip $trip); }
  126. 126. <?php namespace LeoproTripPlannerDomainContract; use LeoproTripPlannerDomainEntityTrip; use LeoproTripPlannerDomainValueObjectTripIdentity; interface TripRepository { /** * @param TripIdentity $identity * @return LeoproTripPlannerDomainEntityTrip */ public function get(TripIdentity $identity); /** * @param Trip $trip * @return void */ public function add(Trip $trip); } … and interfaces for Validator and Event Dispatcher
  127. 127. <?php namespace LeoproTripPlannerApplicationContract; interface Validator { /** * @param $value * @return LeoproTripPlannerDomainContractCollection */ public function validate($value); } interface EventDispatcher { /** * @param array $listeners * @return EventListener[] */ public function registerListeners(array $listeners); /** * @param $event */ public function notify($name, $event); }
  128. 128. About validation In DDD, entities should be always valid.
  129. 129. About validation But if you ask “where do I put validation?” you'll get different answers.
  130. 130. About validation If you are using commands, validate the command itself, is a good trade-off.
  131. 131. Infrastructure
  132. 132. Framework's revenge
  133. 133. composer.json "require": { "php": ">=5.3.3", "doctrine/collections": "v1.2", "symfony/symfony": "~2.4", "doctrine/dbal": "dev-master", "doctrine/orm": "dev-master", "doctrine/doctrine-bundle": "dev-master", "twig/extensions": "~1.0", "symfony/assetic-bundle": "~2.3", "symfony/swiftmailer-bundle": "~2.3", "symfony/monolog-bundle": "~2.4", "sensio/distribution-bundle": "~2.3", "sensio/framework-extra-bundle": "~3.0", "sensio/generator-bundle": "~2.3", "incenteev/composer-parameter-handler": "~2.0", "doctrine/data-fixtures": "dev-master", "doctrine/migrations": "dev-master", "doctrine/doctrine-migrations-bundle": "dev-master", "doctrine/doctrine-fixtures-bundle": "dev-master" },
  134. 134. Mapping entities
  135. 135. app/config/config.yml orm: auto_generate_proxy_classes: "%kernel.debug%" auto_mapping: false mappings: TripPlannerDomain: type: yml prefix: LeoproTripPlannerDomainEntity dir: %kernel.root_dir%/../src/Leopro/TripPlanner/InfrastructureBundle/Resources/config/doctrine/entity is_bundle: false TripPlannerDomainValueObjects: type: yml prefix: LeoproTripPlannerDomainValueObject dir: %kernel.root_dir%/../src/Leopro/TripPlanner/InfrastructureBundle/Resources/config/doctrine/value_object is_bundle: false
  136. 136. InfrastructureBundle/Resources/config/entity/Route.orm.yml LeoproTripPlannerDomainEntityTrip: type: entity table: trip embedded: identity: class: LeoproTripPlannerDomainValueObjectTripIdentity fields: name: type: string length: 250 manyToMany: routes: targetEntity: LeoproTripPlannerDomainEntityRoute joinTable: name: trip_routes joinColumns: link_id: referencedColumnName: identity_id inverseJoinColumns: report_id: referencedColumnName: internalIdentity cascade: ["persist"]
  137. 137. Validate commands
  138. 138. InfrastructureBundle/Resources/config/validation.yml LeoproTripPlannerApplicationCommandCreateTripCommand: properties: name: - NotBlank: ~ LeoproTripPlannerApplicationCommandAddLegToRouteCommand: properties: tripIdentity: - NotBlank: ~ routeIdentity: - NotBlank: ~ date: - NotBlank: ~ dateFormat: - NotBlank: ~ latitude: - NotBlank: ~ longitude: - NotBlank: ~
  139. 139. Configuring services
  140. 140. src/LeoPro/TripPlanner/InfrastructureBundle/Resources/config/services.xml <services> <!-- Exposed Services --> <service id="trip_repository" alias="trip_repository.doctrine"></service> <service id="command_handler" class="%application.command_handler.class%"> <argument type="service" id="infrastructure.validator"/> <argument type="service" id="application.event_dispatcher"/> </service> </services>
  141. 141. src/LeoPro/TripPlanner/InfrastructureBundle/Resources/config/services.xml <services> <!-- Not Exposed Services --> <service id="application.event_dispatcher" public="false" class="%application.event_dispatcher.class%"> </service> <service id="use_case.create_trip" public="false" class="...CreateTripUseCase"> <argument type="service" id="trip_repository"/> <tag name="use_case"/> </service> <service id="use_case.add_leg_to_route" public="false" class="LeoproTripPlannerApplicationUseCaseAddLegToRouteUseCase"> <argument type="service" id="trip_repository"/> <tag name="use_case"/> </service> <service id="use_case.update_location" public="false" class="LeoproTripPlannerApplicationUseCaseUpdateLocationUseCase"> <argument type="service" id="trip_repository"/> <tag name="use_case"/> </service> </services>
  142. 142. src/LeoPro/TripPlanner/InfrastructureBundle/Resources/config/services.xml <services> <!-- Adapter --> <service id="infrastructure.validator" public="false" class="%infrastructure.validator.class%"> <argument type="service" id="validator"/> </service> <service id="infrastructure.event_dispatcher_adapter" public="false" class="%infrastructure.event_dispatcher_adapter.class%"> <argument type="service" id="event_dispatcher"/> <tag name="event_dispatcher_listener"/> </service> <!-- Concrete Implementations --> <service id="trip_repository.doctrine" public="false" class="%infrastructure.trip_repository.doctrine.class%"> <argument type="service" id="doctrine.orm.entity_manager"/> </service> </services>
  143. 143. Adapter Ops … some parts of the frameworks do not fit our interfaces.
  144. 144. <?php namespace LeoproTripPlannerInfrastructureBundleAdapter; use LeoproTripPlannerApplicationContractValidator as ApplicationValidatorInterface; use LeoproTripPlannerDomainAdapterArrayCollection; use SymfonyComponentValidatorValidatorValidatorInterface; class Validator implements ApplicationValidatorInterface { private $validator; public function __construct(ValidatorInterface $validator) { $this->validator = $validator; } public function validate($value) { $applicationErrors = new ArrayCollection(); $errors = $this->validator->validate($value); foreach ($errors as $error) { $applicationErrors->set($error->getPropertyPath(), $error->getMessage()); } return $applicationErrors; } }
  145. 145. Repository
  146. 146. <?php namespace LeoproTripPlannerInfrastructureBundleRepository; use DoctrineORMEntityManager; use LeoproTripPlannerDomainContractTripRepository as TripRepositoryInterface; class TripRepository implements TripRepositoryInterface { private $em; public function __construct(EntityManager $em) { $this->em = $em; } public function get(TripIdentity $identity) { $qb = $this->em->createQueryBuilder() ->select('t') ->from("TripPlannerDomain:Trip", 't') ->where('t.identity.id = :identity'); $qb->setParameter('identity', $identity); return $qb->getQuery()->getOneOrNullResult(); }
  147. 147. <?php namespace LeoproTripPlannerInfrastructureBundleRepository; use DoctrineORMEntityManager; use LeoproTripPlannerDomainContractTripRepository as TripRepositoryInterface; class TripRepository implements TripRepositoryInterface { public function add(Trip $trip) { $this->em->persist($trip); $this->em->flush(); } }
  148. 148. <?php namespace LeoproTripPlannerInfrastructureBundleRepository; use DoctrineORMEntityManager; use LeoproTripPlannerDomainContractTripRepository as TripRepositoryInterface; class TripRepository implements TripRepositoryInterface { public function add(Trip $trip) { $this->em->persist($trip); $this->em->flush(); } } Then it’s like a Doctrine repository?!?
  149. 149. <?php namespace LeoproTripPlannerInfrastructureBundleRepository; use DoctrineORMEntityManager; use LeoproTripPlannerDomainContractTripRepository as TripRepositoryInterface; class TripRepository implements TripRepositoryInterface { public function add(Trip $trip) { $this->em->persist($trip); $this->em->flush(); } } No, it’s quite different
  150. 150. <?php namespace LeoproTripPlannerInfrastructureBundleRepository; use LeoproTripPlannerDomainContractTripRepository as TripRepositoryInterface; class TripRepository implements TripRepositoryInterface { private $myThirdPartApiClient; public function __construct(ApiClient $myThirdPartApiClient) { $this->myThirdPartApiClient = $myThirdPartApiClient; } public function get(TripIdentity $identity) { $this->myThirdPartApiClient->get($identity); } public function add(Trip $trip) { $this->myThirdPartApiClient->store($trip); }
  151. 151. <?php namespace LeoproTripPlannerInfrastructureBundleRepository; use LeoproTripPlannerDomainContractTripRepository as TripRepositoryInterface; class TripRepository implements TripRepositoryInterface { private $myThirdPartApiClient; public function __construct(ApiClient $myThirdPartApiClient) { $this->myThirdPartApiClient = $myThirdPartApiClient; } public function get(TripIdentity $identity) { $this->myThirdPartApiClient->get($identity); } public function add(Trip $trip) { $this->myThirdPartApiClient->store($trip); } It’s another possible Repository implementation
  152. 152. Presentation
  153. 153. <?php namespace LeoproTripPlannerPresentationBundleController; use LeoproTripPlannerPresentationBundleFormTypeCreateTripType; class ApiController extends Controller { /** * @Route("/", name="create_trip") * @Template */ public function createTripAction(Request $request) { $form = $this->createForm(new CreateTripType()); $form->handleRequest($request); if ($form->isValid()) { $trip = $this->get('command_handler')->execute($form->getData()); return new Response('ok'); } return array( 'form' => $form->createView(), ); } }
  154. 154. <?php namespace LeoproTripPlannerPresentationBundleFormType; use LeoproTripPlannerApplicationCommandCreateTripCommand; class CreateTripType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name') ->add('save', 'submit'); } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'LeoproTripPlannerApplicationCommandCreateTripCommand', 'empty_data' => function (FormInterface $form) { $command = new CreateTripCommand( $form->get('name')->getData() ); return $command; }, )); } }
  155. 155. One step to the finish line
  156. 156. What have I learned?
  157. 157. A clean architecture helps in avoiding the Big Ball of Mud.
  158. 158. Also starting with a very simple domain
  159. 159. Iteration by iteration
  160. 160. Complexity could grow
  161. 161. If our system is tightly coupled
  162. 162. and the domain is scattered
  163. 163. we are losing the chance of responding to changes.
  164. 164. Talking about testing ...
  165. 165. … independence from frameworks, database, UI ...
  166. 166. … means talking about business.
  167. 167. Let the code speak the language of the business
  168. 168. First, taking care of the model
  169. 169. Then choosing the right tool
  170. 170. ...
  171. 171. We reached the finish line, well done.
  172. 172. Thank you :-)
  173. 173. Credits ● Eric Evans, - "Domain Driven Design" ● “Uncle Bob” - http://blog.8thlight.com/uncle-bob/archive.html and books ● http://williamdurand.fr/2013/08/20/ddd-with-symfony2-making-things-clear/ ● http://williamdurand.fr/2013/08/07/ddd-with-symfony2-folder-structure-and- code-first/ ● http://verraes.net/2013/04/decoupling-symfony2-forms-from-entities/ ● http://www.whitewashing. de/2012/08/22/building_an_object_model__no_setters_allowed.html ● http://nicolopignatelli.me/valueobjects-a-php-immutable-class-library/ ● http://welcometothebundle.com/domain-driven-design-and-symfony-for- simple-app/ ● http://www.slideshare.net/SteveRhoades2/implementing-ddd-concepts-in- php
  174. 174. Credits ● http://devlicio.us/blogs/casey/archive/2009/02/16/ddd-aggregates-and- aggregate-roots.aspx ● http://www.slideshare.net/perprogramming/application-layer-33335917 ● http://lostechies.com/jimmybogard/2008/08/21/services-in-domain-driven- design/ ● http://gorodinski.com/blog/2012/04/14/services-in-domain-driven-design- ddd/ ● http://www.slideshare.net/jeppec/agile-ddd-cqrs ● http://www.slideshare.net/thinkddd/practical-domain-driven-design-cqrs- and-messaging-architectures ● http://www.codeproject.com/Articles/339725/Domain-Driven-Design-Clear- Your-Concepts-Before-Yo ● http://lostechies.com/jimmybogard/2008/05/21/entities-value-objects- aggregates-and-roots/
  175. 175. Credits ● http://www.slideshare.net/ziobrando/gestire-la-complessit-con-domain- driven-design ● http://lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world/ ● http://lostechies.com/jimmybogard/2009/09/03/ddd-repository- implementation-patterns/ ● http://www.sapiensworks.com/blog/post/2012/04/18/DDD-Aggregates- And-Aggregates-Root-Explained.aspx ● http://www.udidahan.com/2009/06/29/dont-create-aggregate-roots/ ● http://jblewitt.com/blog/?p=241 ● http://www.sapiensworks.com/blog/post/2013/10/18/Modelling-Aggregate- Roots-Relationships.aspx ● http://www.sapiensworks.com/blog/post/2013/01/15/Domain-Driven- Design-Aggregate-Root-Modelling-Fallacy.aspx
  176. 176. Credits ● http://lostechies.com/jamesgregory/2009/05/09/entity-interface-anti-pattern ● http://www.slideshare.net/piotrpelczar/cqrs-28299581 ● http://richarddingwall.name/2009/10/13/life-inside-an-aggregate-root-part- 1/ ● http://lostechies.com/jimmybogard/2010/02/24/strengthening-your-domain- aggregate-construction/ ● http://guptavikas.wordpress.com/2009/12/21/domain-driven-design- creating-domain-objects/ ● http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design- ddd/ ● http://verraes.net/2013/12/related-entities-vs-child-entities/ ● http://devlicio.us/blogs/casey/archive/2009/02/20/ddd-the-repository- pattern.aspx ● http://gojko.net/2009/09/30/ddd-and-relational-databases-the-value-object- dilemma/

×