Slide 1

Slide 1 text

Sergio Moya @soyelsergillo * &#$* *!# % !% $* ** 

Slide 2

Slide 2 text

$ *$# %& $ * Backend Engineer at Social Point ! DDD Fan ! Hip Hop Culture lover ! Badaloní (from Badalona) $#  *! (  ((($#  *$

Slide 3

Slide 3 text

(%(($ An example of a Business requested feature based on a TRUE STORY Project file distribution The Domain Model Use of Domain Events Adapter for our component ! Integrating in our Symfony Project

Slide 4

Slide 4 text

     %)% &"&% &$ &  )!#%  &  %)%  * &## 

Slide 5

Slide 5 text

&"&% &$ &  &
  %)%   A sphere of knowledge, influence, or activity. The subject area to which the user applies a program is the domain of the software. A description of a boundary (typically a subsystem, or the work of a particular team) within which a particular model is defined and applicable. A language structured around the domain model and used by all team members within a bounded context to connect all the activities of the team with the software. %$$

Slide 6

Slide 6 text

KNOW YOUR UBIQUITOUS LANGUAGE

Slide 7

Slide 7 text

The Lead of the Backend engineers team has decided to split the team into smaller two-member teams. ! Each team will develop a different component. ! Our team will develop the Payment Component

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

%#"&#%$ THERE IS NO BASKET OR CART ! ONE PRODUCT, ONE ORDER ! A PRODUCT WILL HAVE CONDITIONS TO BE PURCHASED ! THE SYSTEM SHOULD TRACK THE ORDERS

Slide 10

Slide 10 text

&$% # ## &#  !# &%  *

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

#%%&

Slide 13

Slide 13 text

#%%%$

Slide 14

Slide 14 text

%%%* ## /**! * CustomerOrder! *! * @ORM\Table()! * @ORM\Entity(repositoryClass="Tato\Bundle\PaymentBundle\Entity \CustomerOrderRepository")! */! class CustomerOrder! {! /**! * Set product! *! * @param \stdClass $product! * @return CustomerOrder! */! public function setProduct($product)! {! $this->product = $product;! ! return $this;! }! . . .! }! !

Slide 15

Slide 15 text

%%%*&$% # /**! * Customer! *! * @ORM\Table()! * @ORM\Entity(repositoryClass="Tato\Bundle\PaymentBundle\Entity \CustomerRepository")! */! class Customer! {! /**! * @var integer! *! * @ORM\Column(name="id", type="integer")! * @ORM\Id! * @ORM\GeneratedValue(strategy="AUTO")! */! private $id;! ! . . .! }! !

Slide 16

Slide 16 text

%#

Slide 17

Slide 17 text

% ### public function buyProduct(Product $product, Customer $customer, Money $moneyPayed)! {! $productPrice = $product->getPrice();! if ($moneyPayed->getCurrency() !== $productPrice->getCurrency()) {! throw new OrderException('The Currency of that payment is invalid');! }! ! if ($moneyPayed->getPrice() !== $productPrice->getPrice()) {! throw new OrderException('You must pay me!');! }! ! $conditions = $product->getConditions();! ! foreach ($conditions as $condition) {! if (!$condition->isValid($customer)) {! throw new OrderException('You must meet the product requirements!');! }! }! ! $goods = $product->getGoods();! foreach ($goods as $good) {! $good->give($customer);! }! }!

Slide 18

Slide 18 text

$#'   %# # #! $% #*  # $$ ( $#'

Slide 19

Slide 19 text

THIS IS WAR!

Slide 20

Slide 20 text

BUT YOU WANT THIS

Slide 21

Slide 21 text

BUT WE WANT THIS TODAY YOU WILL GET THIS

Slide 22

Slide 22 text

%-$ #%$* *

Slide 23

Slide 23 text

%-$ #%$* *  # %

Slide 24

Slide 24 text

, $# , , !*%& ,  ! % !*% & $%#&% 

Slide 25

Slide 25 text

&# &$% # A CUSTOMER CAN ORDER A PRODUCT ! A CUSTOMER CAN PAY AN ORDER

Slide 26

Slide 26 text

&# &$% # public function order(Product $product)! {! $order = new Order(rand(), $this, $product);! $this->recordThat(! new OrderCreated($order->getId(), $this->id, $product)! );! ! return $order;! }! We are recording Events!

Slide 27

Slide 27 text

&# &$% # public function pay(Order $order, Money $money)! {! $status = $order->pay($money);! $this->recordThat(new OrderPaid($this->id, $order->getId(), $money));! if ($status === OrderStatus::ORDER_FAILED) {! $this->recordThat(new OrderFailed($order->getId(), $this->id));! }! if ($status === OrderStatus::ORDER_PAID) {! $this->recordThat(! new OrderPaid($order->getId(), $this->id, $money)! );! }! return $status;! }

Slide 28

Slide 28 text

&#  ## AN ORDER MUST ENSURE THAT THEIR PRODUCT MEETS THEIR CONDITIONS ! AN ORDER CAN BE PAID

Slide 29

Slide 29 text

&#  ## public function __construct($id, Customer $customer, $product)! {! $this->id = $id;! $this->customer = $customer;! $this->status = new OrderStatus(OrderStatus::ORDER_STARTED);! $this->guardProductConditions($product);! $this->product = $product;! } Ensure that the product satisfies business conditions

Slide 30

Slide 30 text

public function pay(Money $money)! {! try {! if (!$this->product->getPrice()->isEqualTo($money)) {! throw new OrderException('The Order amount is not satisfied');! }! $this->product->deliver($this->customer);! $this->status = OrderStatus::create(OrderStatus::ORDER_PAID);! } catch (\Exception $e) {! $this->status = OrderStatus::create(OrderStatus::ORDER_FAILED);! }! return $this->status;! } &#  ## Return what we need

Slide 31

Slide 31 text

(% &%#! $% #$

Slide 32

Slide 32 text

(% &%#! $% #$  &#$(%

Slide 33

Slide 33 text

interface OrderRepository! {! public function orderExists($orderId);! ! public function find($id, $customerId);! ! public function findByCustomerId($customerId);! ! public function save($id, $productId, $customerId, $status = 1);! ! public function remove($id);! ! public function removeByCustomerId($customerId);! ! public function clearAll();! }! &#  ## We provide an Interface

Slide 34

Slide 34 text

&%(#

Slide 35

Slide 35 text

&%(# % ###

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

public function buyProductAction(Request $request)! {! $customer = new Customer($request->request->get('customerId'));! ! $productRepository = $this->get('tato.payment.product_respository');! $product = $productRepository->find($request->request->get('productId'));! ! $order = $customer->order($product);! ! $paid = new Money(! $request->request->get('paid'), ! $request->request->get(‘currency')! );! $status = $order->pay($paid);! ! $this->get(‘tato.payment.order_repository')->save(! ! ! $order->getId(), ! $product, ! $customer->getId(), ! $status->getStatus()! );! ! return new JsonResponse(array('status' => $status->getStatus()));! }! } $* * %# #

Slide 38

Slide 38 text

%-$ #%$* * 

Slide 39

Slide 39 text

TAKE A PIECE OF CQRS

Slide 40

Slide 40 text

public function handle(BuyProductCommand $command)! {! $customer = new Customer($command->customerId);! $product = $this->productRepository->find($command->productId);! ! $order = $customer->order($product);! $status = $order->pay(new Money($command->paid, $command->currency));! ! $events = $customer->getRecordedEvents();! ! // @todo do something with this events! ! $this->orderRepository->setOrder(! $command->gatewayName,! $order->getId(),! $command->customerId,! $status->getStatus()! );! ! return $order;! }  # We have Events!

Slide 41

Slide 41 text

class BuyProductCommand! {! public $customerId;! ! public $productId;! ! public $paid;! ! public $currency;! ! public function __construct($customerId, $productId, $paid, $currency)! {! $this->customerId = $customerId;! $this->productId = $productId;! $this->paid = $paid;! $this->currency = $currency;! } ! } %  A simple DTO

Slide 42

Slide 42 text

 ! %  

Slide 43

Slide 43 text

('$ An example of a Business requested feature based on a TRUE STORY Project file distribution The Domain Model Use of Domain Events Adapter for our component ! Integrating in our Symfony Project

Slide 44

Slide 44 text

%%('  ! %

Slide 45

Slide 45 text

$%#&%   $# , , !*%& ,  ! % !*% &

Slide 46

Slide 46 text

(!&%$$$ # %#!%% $

Slide 47

Slide 47 text

&# ###! $% #* ! class CustomOrderRepository extends EntityRepository implements OrderRepository! {! . . .! ! /**! * Checks if an order already exists.! *! * @param int $orderId! * @return bool! * @throws \Tato\Component\Payment\Exception\Order \OrderRepositoryException! */! public function orderExists($orderId)! {! ! ! ! $order = $this->entityManager->find('Order:Order', $orderId);! ! return $order instanceof Order;! }! ! . . . ! } DOCTRINE!

Slide 48

Slide 48 text

$#'$ services:! tato.payment.order_repository:! class: Tato\Component\Game\CustomOrderRepository! arguments:! - @doctrine.orm.entity_manager! ! tato.payment.product_repository:! class: Tato\Component\Game\Product\CustomProductRepository! arguments:! - @doctrine.orm.entity_manager! ! tato.payment.command.handler.buy_product:! class: Tato\Component\Payment\Command\BuyProductCommandHandler! arguments:! order_repository: @tato.payment.order_repository! product_repository: @tato.payment.product_repository!

Slide 49

Slide 49 text

 %#!! doctrine:! orm:! auto_generate_proxy_classes: "%kernel.debug%"! auto_mapping: false! mappings:! Order:! type: yml! is_bundle: false! dir: %kernel.root_dir%/../src/Tato/Bundle/PaymentBundle/ Resources/config/doctrine! prefix: Tato\Component\Payment\Order! alias: PaymentOrder %%!+$& (% $% # %#%%$ &%$ $* *&

Slide 50

Slide 50 text

%$$%!

Slide 51

Slide 51 text

%$$%! %%#*! %

Slide 52

Slide 52 text

!*% %# # public function buyProductAction(Request $request)! {! $customerId = $request->request->get('customerId');! $productId = $request->request->get('productId');! $paid = $request->request->get('paid');! $currency = $request->request->get('currency');! $command = new BuyProductCommand(! $customerId, ! $productId, ! $paid, ! $currency! );! $commandHandler = $this->get('tato.payment.command.handler.buy_product');! $order = $commandHandler->handle($command);! $events = $order->getRecordedEvents();! // @todo do something wit this events! return new JsonResponse(array('status' => $order->getStatus()));! }

Slide 53

Slide 53 text

public function buyProductAction(Request $request)! {! $customer = new Customer($request->request->get('customerId'));! ! $productRepository = $this->get('tato.payment.product_respository');! $product = $productRepository->find($request->request->get('productId'));! ! $order = $customer->order($product);! ! $paid = new Money(! $request->request->get('paid'), ! $request->request->get(‘currency')! );! $status = $order->pay($paid);! ! $this->get(‘tato.payment.order_repository')->save(! ! ! $order->getId(), ! $product, ! $customer->getId(), ! $status->getStatus()! );! ! return new JsonResponse(array('status' => $status->getStatus()));! }! } %#$%!!#  Business Specification in a Controller Coupled to Symfony DIC

Slide 54

Slide 54 text

&##%%&#      REPOSITORIES DIC & ADAPTERS   *%$ #( #

Slide 55

Slide 55 text

BE HEXAGONAL!

Slide 56

Slide 56 text

ENJOY DDD

Slide 57

Slide 57 text

$ %#$% http://buttercup-php.github.io/protects http://vaughnvernon.co http://dddinphp.org http://domainlanguage.com/ddd/patterns/DDD_Reference_2011-01-31.pdf http://ebookbrowsee.net/domain-driven-design-step-by-step-pdf-d322892202 http://martinfowler.com/bliki/CQRS.html http://www.amazon.es/Domain-Driven-Design-Tackling-Complexity-Software/dp/ 0321125215 http://www.amazon.com/Implementing-Domain-Driven-Design-Vaughn-Vernon/ dp/0321834577

Slide 58

Slide 58 text

"&$% $

Slide 59

Slide 59 text

Sergio Moya @soyelsergillo %$ -