Top Banner
Clean Architecture using DDD layering in PHP Leonardo Proietti @_leopro_
177

Clean architecture with ddd layering in php

Aug 23, 2014

Download

Internet

_leopro_

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
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Clean architecture with ddd layering in php

Clean Architecture using DDD layering in PHP

Leonardo Proietti@_leopro_

Page 2: Clean architecture with ddd layering in php

1. Clean Architecture

Page 3: Clean architecture with ddd layering in php

Definition of Clean Architecture

Page 4: Clean architecture with ddd layering in php

Definition of Clean Architecture

Independent of FrameworksTestable

Independent of UIIndependent of Database

Independent of any external agency

Page 5: Clean architecture with ddd layering in php

Definition of Clean Architecture

Independent of FrameworksTestable

Independent of UIIndependent of Database

Independent of any external agency

Page 6: Clean architecture with ddd layering in php

Definition of Clean Architecture

Independent of FrameworksTestable

Independent of UIIndependent of Database

Independent of any external agency

Page 7: Clean architecture with ddd layering in php

Definition of Clean Architecture

Independent of FrameworksTestable

Independent of UIIndependent of Database

Independent of any external agency

Page 8: Clean architecture with ddd layering in php

Definition of Clean Architecture

Independent of FrameworksTestable

Independent of UIIndependent of Database

Independent of any external agency

Page 9: Clean architecture with ddd layering in php

Definition of Clean Architecture

Independent of FrameworksTestable

Independent of UIIndependent of Database

Independent of any external agency

Page 10: Clean architecture with ddd layering in php

Hey bro, I respect your opinion but ...

Page 11: Clean architecture with ddd layering in php

It isn't just my opinion

Page 12: Clean architecture with ddd layering in php

Do you know "Uncle Bob", isn't it?

Page 13: Clean architecture with ddd layering in php

I’m just another dwarf.

Page 14: Clean architecture with ddd layering in php

The Clean Architecture

Page 15: Clean architecture with ddd layering in php

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)

Page 16: Clean architecture with ddd layering in php

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)

Page 17: Clean architecture with ddd layering in php

2. Domain Driven Design

Page 18: Clean architecture with ddd layering in php

What is Domain Driven Design?

Page 19: Clean architecture with ddd layering in php

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")

Page 20: Clean architecture with ddd layering in php

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")

Page 21: Clean architecture with ddd layering in php

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")

Page 22: Clean architecture with ddd layering in php

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)

Page 23: Clean architecture with ddd layering in php

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)

Page 24: Clean architecture with ddd layering in php

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)

Page 25: Clean architecture with ddd layering in php

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)

Page 26: Clean architecture with ddd layering in php

Mmhh interesting … but what does it mean?

Page 27: Clean architecture with ddd layering in php

Make yourself comfortable

Page 28: Clean architecture with ddd layering in php

3. DDD Core

Page 29: Clean architecture with ddd layering in php

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")

Page 30: Clean architecture with ddd layering in php

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")

Page 31: Clean architecture with ddd layering in php

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")

Page 32: Clean architecture with ddd layering in php

Sounds familiar?

Page 33: Clean architecture with ddd layering in php

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")

Page 34: Clean architecture with ddd layering in php

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")

Page 35: Clean architecture with ddd layering in php

Next it’s maybe the most important thing in DDD

Page 36: Clean architecture with ddd layering in php

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")

Page 37: Clean architecture with ddd layering in php

Ubiquitous Language

It’s a shared jargon between domain experts and developers, based on Domain Model

Page 38: Clean architecture with ddd layering in php

Take care of Ubiquitous Language

Page 39: Clean architecture with ddd layering in php

What does “coffee” mean?

Alberto Brandolini AKA ziobrando

Page 40: Clean architecture with ddd layering in php

Ubiquitous Language

“changes to the language will be recognized as changes in the domain model”

(Eric Evans, "Domain Driven Design")

Page 41: Clean architecture with ddd layering in php

Context

“The setting in which a word or statement appears that determines its meaning.”

Page 42: Clean architecture with ddd layering in php

4. DDD Building Blocks

Page 43: Clean architecture with ddd layering in php

Entity

An object with “clear identity and a life-cycle with state transitions that we care about.”

(http://dddsample.sourceforge.net/characterization.html)

Page 44: Clean architecture with ddd layering in php

Are these entities?

Page 45: Clean architecture with ddd layering in php

It depends.

Page 46: Clean architecture with ddd layering in php

It depends.

“We don't assign seats on our flights, so feel free to sit in any available seat”

Page 47: Clean architecture with ddd layering in php

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)

Page 48: Clean architecture with ddd layering in php

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)

Page 49: Clean architecture with ddd layering in php

Are these value objects?

Page 50: Clean architecture with ddd layering in php

In most of the contexts, but ...

Page 51: Clean architecture with ddd layering in php
Page 52: Clean architecture with ddd layering in php

Beware about Anemic Domain Model

Page 53: Clean architecture with ddd layering in php

Beware about Anemic Domain Model

Both Entity and Value Object should have data and behaviours.

(http://www.martinfowler.com/bliki/AnemicDomainModel.html)

Page 54: Clean architecture with ddd layering in php

Few other concepts

RepositoryAggregate

Domain Event

Page 55: Clean architecture with ddd layering in php

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")

Page 56: Clean architecture with ddd layering in php

Repository

“All repositories provide methods that allow client to request objects matching some

criteria”

(Eric Evans, "Domain Driven Design")

Page 57: Clean architecture with ddd layering in php

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")

Page 58: Clean architecture with ddd layering in php

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)

Page 59: Clean architecture with ddd layering in php

Aggregate

“DDD aggregates are domain concepts (order, clinic visit, playlist), while collections are

generic.”

(http://martinfowler.com/bliki/DDD_Aggregate.html)

Page 60: Clean architecture with ddd layering in php

Domain Event

“Captures the memory of something interesting which affects the domain”

(http://martinfowler.com/eaaDev/DomainEvent.html)

Page 61: Clean architecture with ddd layering in php

How long does it take?

Page 62: Clean architecture with ddd layering in php

5. DDD Layering

Page 63: Clean architecture with ddd layering in php

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")

Page 64: Clean architecture with ddd layering in php

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")

Page 65: Clean architecture with ddd layering in php

Layering

(http://guptavikas.wordpress.com/2009/12/01/domain-driven-design-an-introduction/)

Page 66: Clean architecture with ddd layering in php

Layering

(http://dddsample.sourceforge.net/architecture.html)

Page 67: Clean architecture with ddd layering in php

Domain

“The domain layer is the heart of the software, and this is where the interesting stuff happens.”

(http://dddsample.sourceforge.net/architecture.html)

Page 68: Clean architecture with ddd layering in php

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)

Page 69: Clean architecture with ddd layering in php

Interface

“This layer holds everything that interacts with other systems”

(http://dddsample.sourceforge.net/architecture.html)

Page 70: Clean architecture with ddd layering in php

Interface

“This layer holds everything that interacts with other systems”

(http://dddsample.sourceforge.net/architecture.html)

Controller Form

View API

Page 71: Clean architecture with ddd layering in php

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)

Page 72: Clean architecture with ddd layering in php

Separation of concerns

Page 73: Clean architecture with ddd layering in php

Services

Page 74: Clean architecture with ddd layering in php

Services

“Sometimes, it just isn’t a thing.”

(Eric Evans, "Domain Driven Design")

Page 75: Clean architecture with ddd layering in php

Services

Domain ServicesApplication Services

Infrastructural Services

Page 76: Clean architecture with ddd layering in php

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")

Page 77: Clean architecture with ddd layering in php

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")

Page 78: Clean architecture with ddd layering in php

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")

Page 79: Clean architecture with ddd layering in php

6. Code First

Page 80: Clean architecture with ddd layering in php

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/)

Page 81: Clean architecture with ddd layering in php

YAGNI

You aren't gonna need it.You don’t need a Database or a Framework to

modelling the Domain

Page 82: Clean architecture with ddd layering in php

YAGNI

You aren't gonna need it.You don’t need a Database or a Framework to

modelling the Domain

Page 83: Clean architecture with ddd layering in php

Where should I start then?!?

Page 84: Clean architecture with ddd layering in php

Understanding the Domain

Page 85: Clean architecture with ddd layering in php

Talking with domain experts

Page 86: Clean architecture with ddd layering in php

DDD is Agile, we should be iterative

(http://dddsample.sourceforge.net/architecture.html)

Page 87: Clean architecture with ddd layering in php

7. Let’s code

Page 88: Clean architecture with ddd layering in php

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.

Page 89: Clean architecture with ddd layering in php

Code

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

You can follow the building steps, starting from the first commit.

Page 90: Clean architecture with ddd layering in php

Code

Let’s focus on some steps

Page 91: Clean architecture with ddd layering in php

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" }}

Page 92: Clean architecture with ddd layering in php

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

Page 93: Clean architecture with ddd layering in php

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

Page 94: Clean architecture with ddd layering in php

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

Page 95: Clean architecture with ddd layering in php

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

Page 96: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Contract;

use Doctrine\Common\Collections\Collection as DoctrineCollection;

interface Collection extends DoctrineCollection {}

Page 97: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Adapter;

use Doctrine\Common\Collections\ArrayCollection as DoctrineArrayCollection;use Leopro\TripPlanner\Domain\Contract\Collection;

class ArrayCollection extends DoctrineArrayCollection implements Collection {}

Page 98: Clean architecture with ddd layering in php

Domain

Page 99: Clean architecture with ddd layering in php

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.

Page 100: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Tests;

use Leopro\TripPlanner\Domain\Entity\Trip;

class TripTest extends \PHPUnit_Framework_TestCase{ public function testCreateTripReturnATripWithFirstRoute() { $trip = Trip::create('my first planning'); $this->assertInstanceOf('Leopro\TripPlanner\Domain\Entity\Trip', $trip); $this->assertEquals(1, $trip->getRoutes()->count()); }}

Page 101: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Entity;

use Leopro\TripPlanner\Domain\Adapter\ArrayCollection;

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; }}

Page 102: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Entity;

class Route{

}

Page 103: Clean architecture with ddd layering in php

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.

Page 104: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Tests;

use Leopro\TripPlanner\Domain\Entity\Route;

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()); }

Page 105: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Entity;

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); }

//...

Page 106: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Entity;

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?

Page 107: Clean architecture with ddd layering in php

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.

Page 108: Clean architecture with ddd layering in php

/** * @expectedException \...\DateAlreadyUsedException */public function testNoDuplicationDateForTheSameRoute(){ $route = Route::create('my first trip'); $route->addLeg('06-06-2014'); $route->addLeg('06-06-2014');}

Page 109: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Entity;

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);}

//...

Page 110: Clean architecture with ddd layering in php

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.

Page 111: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Tests;

use Leopro\TripPlanner\Domain\Entity\Leg;

class LegTest extends \PHPUnit_Framework_TestCase{ public function testCreateLegReturnsALegWithDateAndLocation() { $leg = Leg::create('01/01/2014', 'd/m/Y', -3.386665, 36.736908);

$this->assertInstanceOf('Leopro\TripPlanner\Domain\Entity\Leg', $leg);

$location = $leg->getLocation(); $this->assertInstanceOf('Leopro\TripPlanner\Domain\Entity\Location', $location);

$point = $location->getPoint(); $this->assertInstanceOf('Leopro\TripPlanner\Domain\ValueObject\Point', $point); $this->assertEquals(-3.386665, $point->getLatitude()); $this->assertEquals(36.736908, $point->getLongitude()); }}

Page 112: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Entity;

use Leopro\TripPlanner\Domain\ValueObject\Date;

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) ); }

//..

Page 113: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Entity;

use Leopro\TripPlanner\Domain\ValueObject\Point;

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; }}

Page 114: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Tests;

use Leopro\TripPlanner\Domain\ValueObject\Point;

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)); }}

Page 115: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Tests;

use Leopro\TripPlanner\Domain\ValueObject\Point;

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()

Page 116: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\ValueObject;

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)); }

Page 117: Clean architecture with ddd layering in php

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);}

Page 118: Clean architecture with ddd layering in php

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?

Page 119: Clean architecture with ddd layering in php

Application

Page 120: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Application\Command;

use Leopro\TripPlanner\Application\UseCase\UseCaseInterface;

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(‘...'); } } }

//...

Page 121: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Application\Command;

use Leopro\TripPlanner\Application\UseCase\UseCaseInterface;

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; } }}

Page 122: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Application\Command;

use Leopro\TripPlanner\Application\UseCase\UseCaseInterface;

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

Page 123: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Application\Command;

use Leopro\TripPlanner\Application\Contract\CommandInterface;use Leopro\TripPlanner\Domain\Adapter\ArrayCollection;

class CreateTripCommand implements CommandInterface{ private $name;

public function __construct($name) { $this->name = $name; }

public function getRequest() { return new ArrayCollection( array( 'name' => $this->name ) ); }}

Page 124: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Application\UseCase;

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; }}

Page 125: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Application\UseCase;

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

Page 126: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Contract;

use Leopro\TripPlanner\Domain\Entity\Trip;use Leopro\TripPlanner\Domain\ValueObject\TripIdentity;

interface TripRepository{ /** * @param TripIdentity $identity * @return \Leopro\TripPlanner\Domain\Entity\Trip */ public function get(TripIdentity $identity);

/** * @param Trip $trip * @return void */ public function add(Trip $trip);}

Page 127: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Domain\Contract;

use Leopro\TripPlanner\Domain\Entity\Trip;use Leopro\TripPlanner\Domain\ValueObject\TripIdentity;

interface TripRepository{ /** * @param TripIdentity $identity * @return \Leopro\TripPlanner\Domain\Entity\Trip */ public function get(TripIdentity $identity);

/** * @param Trip $trip * @return void */ public function add(Trip $trip);}

… and interfaces for Validator and Event Dispatcher

Page 128: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\Application\Contract;

interface Validator{ /** * @param $value * @return \Leopro\TripPlanner\Domain\Contract\Collection */ public function validate($value);}

interface EventDispatcher{ /** * @param array $listeners * @return EventListener[] */ public function registerListeners(array $listeners);

/** * @param $event */ public function notify($name, $event);}

Page 129: Clean architecture with ddd layering in php

About validation

In DDD, entities should be always valid.

Page 130: Clean architecture with ddd layering in php

About validation

But if you ask“where do I put validation?”you'll get different answers.

Page 131: Clean architecture with ddd layering in php

About validation

If you are using commands, validate the command itself, is a

good trade-off.

Page 132: Clean architecture with ddd layering in php

Infrastructure

Page 133: Clean architecture with ddd layering in php

Framework's revenge

Page 134: Clean architecture with ddd layering in php

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"},

Page 135: Clean architecture with ddd layering in php

Mapping entities

Page 136: Clean architecture with ddd layering in php

app/config/config.yml

orm: auto_generate_proxy_classes: "%kernel.debug%" auto_mapping: false mappings: TripPlannerDomain: type: yml prefix: Leopro\TripPlanner\Domain\Entity dir: %kernel.root_dir%/../src/Leopro/TripPlanner/InfrastructureBundle/Resources/config/doctrine/entity is_bundle: false TripPlannerDomainValueObjects: type: yml prefix: Leopro\TripPlanner\Domain\ValueObject dir: %kernel.root_dir%/../src/Leopro/TripPlanner/InfrastructureBundle/Resources/config/doctrine/value_object is_bundle: false

Page 137: Clean architecture with ddd layering in php

InfrastructureBundle/Resources/config/entity/Route.orm.yml

Leopro\TripPlanner\Domain\Entity\Trip: type: entity table: trip embedded: identity: class: Leopro\TripPlanner\Domain\ValueObject\TripIdentity fields: name: type: string length: 250 manyToMany: routes: targetEntity: Leopro\TripPlanner\Domain\Entity\Route joinTable: name: trip_routes joinColumns: link_id: referencedColumnName: identity_id inverseJoinColumns: report_id: referencedColumnName: internalIdentity cascade: ["persist"]

Page 138: Clean architecture with ddd layering in php

Validate commands

Page 139: Clean architecture with ddd layering in php

InfrastructureBundle/Resources/config/validation.yml

Leopro\TripPlanner\Application\Command\CreateTripCommand: properties: name: - NotBlank: ~

Leopro\TripPlanner\Application\Command\AddLegToRouteCommand: properties: tripIdentity: - NotBlank: ~ routeIdentity: - NotBlank: ~ date: - NotBlank: ~ dateFormat: - NotBlank: ~ latitude: - NotBlank: ~ longitude: - NotBlank: ~

Page 140: Clean architecture with ddd layering in php

Configuring services

Page 141: Clean architecture with ddd layering in php

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>

Page 142: Clean architecture with ddd layering in php

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="Leopro\TripPlanner\Application\UseCase\AddLegToRouteUseCase"> <argument type="service" id="trip_repository"/> <tag name="use_case"/> </service>

<service id="use_case.update_location" public="false" class="Leopro\TripPlanner\Application\UseCase\UpdateLocationUseCase"> <argument type="service" id="trip_repository"/> <tag name="use_case"/> </service>

</services>

Page 143: Clean architecture with ddd layering in php

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>

Page 144: Clean architecture with ddd layering in php

Adapter

Ops … some parts of the frameworks do not fit our

interfaces.

Page 145: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\InfrastructureBundle\Adapter;

use Leopro\TripPlanner\Application\Contract\Validator as ApplicationValidatorInterface;use Leopro\TripPlanner\Domain\Adapter\ArrayCollection;use Symfony\Component\Validator\Validator\ValidatorInterface;

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; }}

Page 146: Clean architecture with ddd layering in php

Repository

Page 147: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\InfrastructureBundle\Repository;

use Doctrine\ORM\EntityManager;use Leopro\TripPlanner\Domain\Contract\TripRepository 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(); }

Page 148: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\InfrastructureBundle\Repository;

use Doctrine\ORM\EntityManager;use Leopro\TripPlanner\Domain\Contract\TripRepository as TripRepositoryInterface;

class TripRepository implements TripRepositoryInterface{ public function add(Trip $trip) { $this->em->persist($trip); $this->em->flush(); }}

Page 149: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\InfrastructureBundle\Repository;

use Doctrine\ORM\EntityManager;use Leopro\TripPlanner\Domain\Contract\TripRepository 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?!?

Page 150: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\InfrastructureBundle\Repository;

use Doctrine\ORM\EntityManager;use Leopro\TripPlanner\Domain\Contract\TripRepository as TripRepositoryInterface;

class TripRepository implements TripRepositoryInterface{ public function add(Trip $trip) { $this->em->persist($trip); $this->em->flush(); }}

No, it’s quite different

Page 151: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\InfrastructureBundle\Repository;

use Leopro\TripPlanner\Domain\Contract\TripRepository 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); }

Page 152: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\InfrastructureBundle\Repository;

use Leopro\TripPlanner\Domain\Contract\TripRepository 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

Page 153: Clean architecture with ddd layering in php

Presentation

Page 154: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\PresentationBundle\Controller;

use Leopro\TripPlanner\PresentationBundle\Form\Type\CreateTripType;

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(), ); }}

Page 155: Clean architecture with ddd layering in php

<?php

namespace Leopro\TripPlanner\PresentationBundle\Form\Type;

use Leopro\TripPlanner\Application\Command\CreateTripCommand;

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' => 'Leopro\TripPlanner\Application\Command\CreateTripCommand', 'empty_data' => function (FormInterface $form) { $command = new CreateTripCommand( $form->get('name')->getData() );

return $command; }, )); }}

Page 156: Clean architecture with ddd layering in php

One step to the finish line

Page 157: Clean architecture with ddd layering in php

What have I learned?

Page 158: Clean architecture with ddd layering in php

A clean architecture helps in avoiding the Big Ball of Mud.

Page 159: Clean architecture with ddd layering in php

Also starting with a very simple domain

Page 160: Clean architecture with ddd layering in php

Iteration by iteration

Page 161: Clean architecture with ddd layering in php

Complexity could grow

Page 162: Clean architecture with ddd layering in php

If our system is tightly coupled

Page 163: Clean architecture with ddd layering in php

and the domain is scattered

Page 164: Clean architecture with ddd layering in php

we are losing the chance of responding to changes.

Page 165: Clean architecture with ddd layering in php

Talking about testing ...

Page 166: Clean architecture with ddd layering in php

… independence from frameworks, database, UI ...

Page 167: Clean architecture with ddd layering in php

… means talking about business.

Page 168: Clean architecture with ddd layering in php

Let the code speak the language of the business

Page 169: Clean architecture with ddd layering in php

First, taking care of the model

Page 170: Clean architecture with ddd layering in php

Then choosing the right tool

Page 171: Clean architecture with ddd layering in php

...

Page 172: Clean architecture with ddd layering in php

We reached the finish line, well done.

Page 173: Clean architecture with ddd layering in php

Thank you :-)

Page 174: Clean architecture with ddd layering in php

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

Page 175: Clean architecture with ddd layering in php

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/

Page 176: Clean architecture with ddd layering in php

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

Page 177: Clean architecture with ddd layering in php

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/