domain-driven design in php and symfony - drupal camp wroclaw!

86
Domain-Driven Design in PHP & Symfony

Upload: kacper-gunia

Post on 22-Nov-2014

1.406 views

Category:

Engineering


11 download

DESCRIPTION

Domain-driven Design in PHP and Symfony - Drupal Camp Wrocław 18/10/2014

TRANSCRIPT

Page 1: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Domain-Driven Designin PHP & Symfony

Page 2: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Kacper Gunia @cakper Software Engineer @SensioLabsUK

Symfony Certified Developer

PHPers Silesia @PHPersPL

Page 3: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!
Page 4: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Software is complex

Page 5: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!
Page 6: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

How to tackle it?

Page 7: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

–Eric Evans

Domain-Driven Design is !an approach to the development of complex software in which we:

Page 8: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

–Eric Evans

1. Focus on the core domain.

Page 9: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

–Eric Evans

2. Explore model in !a creative collaboration of domain practitioners and software practitioners.

Page 10: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

–Eric Evans

3. Speak an ubiquitous language within an explicitly bounded context.

Page 11: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

When to use DDD?

Page 12: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Complex domain

Page 13: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Domain Experts

Page 14: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Iterative process

Page 15: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Object-Oriented Design

Page 16: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Strategic DDD

Page 17: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Strategy!!

Stratos - army/resources!Ago - leading

Page 18: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Strategy!!

!

= What

Page 19: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Bounded Context

Page 20: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Bounded contexts of product

❖ Catalogue!❖ Sales!❖ Marketing!❖ Warehouse

Page 21: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Ubiquitous language

Page 22: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Core Domain

Page 23: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Supporting Domain

Page 24: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Model-Driven Design

Page 25: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Continuous Integration

Page 26: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

UI

SERVICE

UNIT

Page 27: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Refactoring Towards Deeper Insight

Page 28: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Tactical DDD

Page 29: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Tactics!!

Taktike - the art of organising an army, a maneuver

Page 30: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Tactics!!

!

= How

Page 31: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Building Blocks

Page 32: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Layered architecture

Page 33: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

DOMAIN

USER INTERFACE

APPLICATION

INFRASTRUCTURE

Page 34: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

.  └──  src          └──  Dcwroc                  ├──  Shopping                  ├──  Emejzon                  ├──  EmejzonBundle                  └──  DoctrineAdapter

Page 35: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Entities

Page 36: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  Sale  {  (…)          private  $id;  !

       function  __construct($name,  DateTimeInterface  $dueDate)          {                  $this-­‐>name  =  $name;                  $this-­‐>dueDate  =  $dueDate;          }  !

       public  function  getId()          {                  return  $this-­‐>id;          }  }  

Page 37: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  Sale  {  (…)          public  function  activate()          {                  if  (true  ===  $this-­‐>active)  {                          throw  new  LogicException('Sale  already  activated!');                  }  !

               $this-­‐>active  =  true;          }  }  

Page 38: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Value objects

Page 39: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  Email  {  (…)         public  function  __construct($email)          {                  if  (!filter_var($email,  FILTER_VALIDATE_EMAIL))  {                          throw  new  InvalidArgumentException();                  }  !

               $this-­‐>email  =  $email;          }  !

       function  asString()          {                  return  $this-­‐>email;          }  }

Page 40: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Aggregates

Page 41: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  Order  {(…)          function  __construct(                  Customer  $customer,                    ProductList  $productList          ){                  $this-­‐>customer  =  $customer;                  $this-­‐>productList  =  $productList;                  $this-­‐>status  =  Status::open();          }  !

       public  function  getTotal();  }  

Page 42: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Repositories

Page 43: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

interface  OrderRepository    {          public  function  save(Order  $order);  !

       public  function  remove(Order  $order);                    public  function  findById($orderId);  !

       public  function  findObsolete();  }

Page 44: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  DoctrineOrderRepository  implements  OrderRepository  {}  !

class  FileOrderRepository  implements  OrderRepository  {}  !

class  InMemoryOrderRepository  implements  OrderRepository  {}

Page 45: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Factories

Page 46: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  CustomerFactory  {          public  function  register(Email  $email,  Password  $password)          {                  (...)  !

               return  $customer;          }  }  

Page 47: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Services

Page 48: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

interface  Notifier    {          public  function  send(Message  $message);  }  

Page 49: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  EmailNotifier  implements  Notifier  {          private  $mailer;  !

       function  __construct(Swift_Mailer  $mailer)          {                  $this-­‐>mailer  =  $mailer;          }  !

       public  function  send(Message  $message)          {               (…)          }  }

Page 50: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Domain Events

Page 51: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  ResetPassword  {          function  request(Email  $email)          {                  $user  =  $this-­‐>userRepository-­‐>findOneByEmail($email);  !

               if  (!$user  instanceof  User)  {                          return  $this-­‐>raise(new  UserDoesNotExistEvent($email));                  }  !

               $request  =  Password::requestReset($user);                  $this-­‐>passwordResetRequestRepository-­‐>save($request);                  $this-­‐>raise(new  RequestIssuedEvent($request));          }  }

Page 52: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  ResetPassword  {          function  request(Email  $email)          {                  $user  =  $this-­‐>userRepository-­‐>findOneByEmail($email);  !

               if  (!$user  instanceof  User)  {                          return  $this-­‐>raise(new  UserDoesNotExistEvent($email));                  }  !

               $request  =  Password::requestReset($user);                  $this-­‐>passwordResetRequestRepository-­‐>save($request);                  $this-­‐>raise(new  RequestIssuedEvent($request));          }  }

Page 53: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  ResetPassword  {          function  request(Email  $email)          {                  $user  =  $this-­‐>userRepository-­‐>findOneByEmail($email);  !

               if  (!$user  instanceof  User)  {                          return  $this-­‐>raise(new  UserDoesNotExistEvent($email));                  }  !

               $request  =  Password::requestReset($user);                  $this-­‐>passwordResetRequestRepository-­‐>save($request);                  $this-­‐>raise(new  RequestIssuedEvent($request));          }  }

Page 54: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  ResetPassword  {          function  request(Email  $email)          {                  $user  =  $this-­‐>userRepository-­‐>findOneByEmail($email);  !

               if  (!$user  instanceof  User)  {                          return  $this-­‐>raise(new  UserDoesNotExistEvent($email));                  }  !

               $request  =  Password::requestReset($user);                  $this-­‐>passwordResetRequestRepository-­‐>save($request);                  $this-­‐>raise(new  RequestIssuedEvent($request));          }  }

Page 55: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Modules

Page 56: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

.  └──  Shopping          └──  Authentication                  ├──  InvalidPasswordResetRequest.php                  ├──  ResetPassword.php                  ├──  PasswordResetRequest.php                  └──  PasswordResetRequestRepository.php

Page 57: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Supple Design

Page 58: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Make behaviour obvious

Page 59: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Intention revealing interfaces

Page 60: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Design for change

Page 61: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Create software other developers want

to work with

Page 62: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Ignore persistance

Page 63: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Domain model is always in a valid state

Page 64: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Command Pattern

Page 65: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  ChangeEmailCommand    {          public  $email;          public  $customerId;  }  

Page 66: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  ChangeEmailCommandHandler    {          private  $customerRepository;  !

       function  __construct(CustomerRepository  $customerRepository)          {                  $this-­‐>customerRepository  =  $customerRepository;          }  !

       public  function  handle(ChangeEmailCommand  $command)          {                  $customer  =  $this-­‐>customerRepository                                                    -­‐>findById($command-­‐>customerId);                  $customer-­‐>changeEmail($command-­‐>email);          }  }

Page 67: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Specification Pattern

Page 68: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

interface  Specification  {          public  function  isSatisfiedBy($object);  }  

Page 69: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  PayingCustomerSpecification  implements  Specification  {          public  function  isSatisfiedBy($object)          {                  return  $object  instanceof  Customer                            &&  $object-­‐>getAmountSpent()  >  0.0;          }  }

Page 70: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  FreeDeliveryCalculator  {          private  $specification;  !

       function  __construct(Specification  $specification)          {                  $this-­‐>specification  =  $specification;          }  !

       public  function  applyDiscount(Order  $order)          {                  $customer  =  $this-­‐>$order-­‐>getCustomer();                  if  ($this-­‐>specification-­‐>isSatisfiedBy($customer))  {                          $order-­‐>deliverForFree();                  }          }  }

Page 71: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

class  FreeDeliveryCalculator  {          private  $specification;  !

       function  __construct(Specification  $specification)          {                  $this-­‐>specification  =  $specification;          }  !

       public  function  applyDiscount(Order  $order)          {                  $customer  =  $this-­‐>$order-­‐>getCustomer();                  if  ($this-­‐>specification-­‐>isSatisfiedBy($customer))  {                          $order-­‐>deliverForFree();                  }          }  }

Page 72: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Command-Query Responsibility Segregation

Page 73: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Commands

Page 74: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Use ORM to fetch/save aggregate roots

Page 75: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Queries

Page 76: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Use specialised queries and DTO’s to fetch presentation data

Page 77: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

DDD Summary

Page 78: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Explore Domain Knowledge

Page 79: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

With Domain Experts

Page 80: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

By speaking Ubiquitous Language

Page 81: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Make use of Building Blocks

Page 82: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

By applying Object-Oriented Design

Page 83: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Design for change

Page 84: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

That’s the only constant!

Page 85: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

–Eric Evans

“In order to create good software, you have to

know what that software is all about.”

Page 86: Domain-driven Design in PHP and Symfony - Drupal Camp Wroclaw!

Kacper Gunia Software Engineer

Symfony Certified Developer

PHPers Silesia

Thanks!