Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DDD: Основы доменной модели

Avatar for zloyuser zloyuser
February 05, 2014

DDD: Основы доменной модели

Avatar for zloyuser

zloyuser

February 05, 2014
Tweet

More Decks by zloyuser

Other Decks in Programming

Transcript

  1. Основные понятия Контекст - ограниченная зона ответственности. Сущность - модель

    объекта предметной области. Сводный корень - сущность, предоставляющая общий API. Объект-значение - свойство объектов предметной области. Службы - операции характерные для предметной области.
  2. Знакомьтесь, доменный эксперт. Доменный эксперт - человек или группа людей

    обладающие необходимыми знаниями и опытом в предметной области.
  3. Hello world! Система управления складом. Задачи: 1) Упорядочить прием товаров

    на склад. 2) Организовать систему заявок на выдачу. 3) Оповещать менеджмент о нехватке товаров.
  4. Единый язык Система терминов, понятная как IT специалисту, так и

    эксперту в предметной области. Goods? Shipment? Item? Product!
  5. Доменный эксперт говорит что... “Поставщики привозят на склад контейнеры с

    товарами.” Склад Поставщик Поставщик Контейнер Контейнер
  6. Доменный эксперт говорит что... “В каждом контейнере могут быть разные

    товары и в разном количестве, кроме того у каждого товара есть срок годности.” Продукт: - название; - срок год.; Контейнер Товар: - название; - срок год. Товар: - название; - срок год.; Товар: - название; - срок год.; Товар: - название; - срок год.; Товар: - название; - срок год. Товар: - название; - срок год.
  7. Доменный эксперт говорит что... “Когда товар попадает на склад, мы

    записываем дату поступления.” Склад Товар: - название; - срок год.; - дата пос. Товар: - название; - срок год.; - дата пос. Товар: - название; - срок год.; - дата пос.
  8. Промежуточный итог Склад Товар Товар Товар Пр Контейнер Товар Товар

    Товар Товар Товар Товар Пр Контейнер Товар Товар Товар Товар Товар Товар Да, да… очень похоже на то, что у нас происходит.
  9. Пишим код ..! Product 1. class Product 2. { 3.

    public function __construct(Name $name, \DateTime $expirationDate ) 4. { 5. $this->name = $name; 6. $this->expirationDate = $exipationDate ; 7. } 8. 9. public function accept(\DateTime $deliveryDate) 10. { 11. $this->deliveryDate = $deliveryDate; 12. return $this; 13. } 14. 15. public function isExpired() 16. { 17. return $this->expirationDate > new \DateTime(); 18. } 19. }
  10. Пишим код ..! Container 1. class Container 2. { 3.

    private $products; 4. 5. public function __construct(array $products) 6. { 7. Assertion ::allIsInstanceOf ($products, 'Product'); 8. 9. $this->products = $products; 10. } 11. 12. public function getProducts() 13. { 14. return $this->products; 15. } 16. }
  11. Пишим код ..! Supplier 1. class Supplier 2. { 3.

    public function sendProducts(Warehouse $receiver, array $products) 4. { 5. $this->sendContainer($receiver, new Container($products)); 6. } 7. 8. public function sendContainer (Warehouse $receiver, Container $container) 9. { 10. $receiver->acceptContainer ($container); 11. } 12. }
  12. Пишим код ..! Warehouse 1. class Warehouse 2. { 3.

    private $products; 4. 5. public function acceptContainer (Container $container) 6. { 7. $now = new \DateTime(); 8. $products = $container->getProducts(); 9. 10. foreach ($products as $product) { 11. $name = $product->getName(); 12. $this->products[$name][] = $product->accept($now); 13. } 14. } 15. }
  13. Доменный эксперт говорит что... “Кажется мы забыли упомянуть что у

    нас два склада, и еще один строится рядом с кольцевой.” Склад - номер - адрес - статус Склад - номер - адрес - статус Склад - номер - адрес - статус
  14. Рефакторим ..! Warehouse. 1. class Warehouse implements Entity 2. {

    3. private $id; 4. private $address; 5. private $status; 6. 7. public function __construct(WarehouseId $id, Address $address, Status $status = null) 8. { 9. $this->id = $id; 10. $this->address = $address; 11. $this->status = $status ?: new Status(Status::CLOSED); 12. } 13. 14. public function acceptContainer (Container $container) 15. { 16. if ($this->status->isClosed()) { 17. \throw new Exception("Warehouse closed" ); 18. } 19. 20. ... 21. }
  15. Объекты-значения 1. class Status 2. { 3. const CLOSED =

    0; 4. const OPENED = 1; 5. 6. private $value; 7. 8. public function __construct($value) 9. { 10. $this->value = $value; 11. } 12. 13. public function isClosed() 14. { 15. return self::CLOSED === $this->value; 16. } 17. } 1) Равенство объектов-значений основано на равенстве их полей. 2) Объекты-значения неизменны. 3) Строковое представление объектов-значений должно быть однозначно. 4) Объекты значения могут хранить не только примитивы, но и другие объекты и даже сущности.
  16. Сущности 1) Равенство сущностей основано на равенстве их идентификаторов. 2)

    Основная единица бизнес- логики. 3) Имена сущностей и их методы должны иметь смысл в контексте единого языка. Кстати, неплохо было бы знать какой именно поставщик нам привез эти помидоры.
  17. Рефакторим ..! Supplier 1. class Supplier implements Entity 2. {

    3. private $id; 4. 5. public function __construct(SupplierId $id) 6. { 7. $this->id = $id; 8. } 9. 10. public function getId() 11. { 12. return $this->id; 13. } 14. 15. public function sendProducts(Warehouse $receiver, array $products) 16. { 17. $this->sendContainer(new Container($products, $this )); 18. } 19. 20. ...
  18. Система управления складом 1) Упорядочить прием товаров на склад. 2)

    Организовать систему заявок на выдачу. 3) Оповещать менеджмент о нехватке товаров.
  19. Доменный эксперт говорит что... “В заявке указаны требуемые наименования товаров

    и их количество.” Заявка - номер; - товар => кол-во.; - товар => кол-во.; - товар => кол-во.; - товар => кол-во.; - ... Ну и, естественно, мы не отгружаем товары с истекшим сроком годности. Их надо сразу списывать.
  20. Доменный эксперт говорит что... “Если на складе достаточно товаров, то

    заявке одобряется и машина с товарами отправляется в магазин.” Склад Магазин Машина Заявка
  21. Пишим код ..! Receipt 1. class Receipt implements Entity 2.

    { 3. public function __construct(ReceiptId $id, Shop $receiver) 4. { 5. $this->id = $id; 6. $this->receiver = $receiver; 7. } 8. 9. public function addRequire(Name $name, $count) 10. { 11. Assertion ::integer($count); 12. Assertion ::greater($count, 0); 13. 14. $this->requires[$name] = $count; 15. } 16. 17. public function getList() 18. { 19. return $this->requires; 20. } 21. }
  22. Пишим код ..! Shop 1. class Shop implements Entity 2.

    { 3. private $id; 4. private $address; 5. public function __construct(ShopId $id, Address $address) 6. { 7. $this->id = $id; 8. $this->address = $address; 9. } 10. 11. public function createReceipt (array $products) 12. { 13. $receipt = new Receipt(new ReceiptId(new \DateTime), $this); 14. 15. foreach ($products as $name => $count) { 16. $receipt->addRequire(new Name($name), $count); 17. } 18. 19. return $receipt; 20. } 21. }
  23. Пишим код ..! Warehouse 1. class Warehouse implements Entity 2.

    { 3. public function resolveReceipt (Receipt $receipt) 4. { 5. $toSend = []; 6. foreach ($receipt->getList() as $name => $count) { 7. $available = $this->getAvailable($name); 8. if ($count > count($available)) { 9. throw new \AvailableException ($name, $available, $count); 10. } 11. 12. $needed = array_slice($available, 0, $count); 13. $toSend = array_merge($toSend, $needed); 14. $this->writeOff($needed, new Reason('Resolved for receipt №' .$receipt->getId())); 15. } 16. 17. $receipt->resolve(new \DateTime()); 18. 19. return new Car($toSend); 20. } 21. }
  24. Сводный корень Сущность Warehouse производит все бизнес-операции с сущностью Receipt

    и отвечает за изменение ее статуса: одобрено или отложено. В данном случае сущность Warehouse является сводным корнем. В различных контекстах возможны разные сводные корни, например Receipt может быть сводным корнем для Product. А как изменять статус заявки?
  25. Контекст В данном примере мы имеем два распределенных контекста: поставщик-

    склад и склад-магазин. Склад Магазин Машина Заявка Поставщик Поставщик Контейнер Контейнер
  26. Система управления складом 1) Упорядочить прием товаров на склад. 2)

    Организовать систему заявок на выдачу. 3) Оповещать менеджмент о нехватке товаров.
  27. Доменный эксперт говорит что... “Знаете, мы понятия не имеем, что

    нужно знать менеджерам и как они работают с нашим складом. Давайте поговорим об этом завтра с Аланом.” Код домена и задание от Алана можно посмотреть на GitHub.