Основы доменной модели
Post on 14-Dec-2014
598 Views
Preview:
DESCRIPTION
TRANSCRIPT
DDDОсновы доменной модели
Основные понятия
Контекст - ограниченная зона ответственности.Сущность - модель объекта предметной области.Сводный корень - сущность, предоставляющая общий API.Объект-значение - свойство объектов предметной области.Службы - операции характерные для предметной области.
Знакомьтесь, доменный эксперт.
Доменный эксперт - человек или группа людей обладающие необходимыми знаниями и опытом в предметной области.
Hello world!
Система управления складом.Задачи:1) Упорядочить прием товаров на склад.2) Организовать систему заявок на выдачу.3) Оповещать менеджмент о нехватке товаров.
Первый взгляд
Как я представляю склад:
Как Geek&Poke видят склад:
ТОВАРЫ
Склад
МАГАЗИН
Единый язык
Система терминов, понятная как IT специалисту, так и эксперту в предметной области.
Goods?
Shipment? Item?
Product!
Доменный эксперт говорит что...
“Поставщики привозят на склад контейнеры с товарами.”
Склад
Поставщик
Поставщик
Контейнер
Контейнер
Доменный эксперт говорит что...
“В каждом контейнере могут быть разные товары и в разном количестве, кроме того у каждого товара есть срок годности.”
Продукт:- название;- срок год.;
Контейнер
Товар:- название;- срок год.
Товар:- название;- срок год.;
Товар:- название;- срок год.;
Товар:- название;- срок год.;
Товар:- название;- срок год.
Товар:- название;- срок год.
Доменный эксперт говорит что...
“Когда товар попадает на склад, мы записываем дату поступления.”
Склад
Товар:- название;- срок год.;- дата пос.
Товар:- название;- срок год.;- дата пос.
Товар:- название;- срок год.;- дата пос.
Промежуточный итог
СкладТовар
ТоварТовар
ПрКонтейнерТоварТовар
ТоварТовар
ТоварТовар
ПрКонтейнерТоварТовар
ТоварТовар
ТоварТовар
Да, да… очень похоже на то, что у нас происходит.
Пишим код ..! Product1. class Product2. {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. }
Пишим код ..! Container1. class Container2. {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. }
Пишим код ..! Supplier1. class Supplier2. {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. }
Пишим код ..! Warehouse1. class Warehouse2. {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. }
Доменный эксперт говорит что...
“Кажется мы забыли упомянуть что у нас два склада, и еще один строится рядом с кольцевой.”
Склад- номер- адрес- статус
Склад- номер- адрес- статус
Склад- номер- адрес- статус
Рефакторим ..! Warehouse.1. class Warehouse implements Entity2. {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. }
Объекты-значения1. class Status2. {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) Объекты значения могут хранить не только примитивы, но и другие объекты и даже сущности.
Сущности
1) Равенство сущностей основано на равенстве их идентификаторов.2) Основная единица бизнес-логики.3) Имена сущностей и их методы должны иметь смысл в контексте единого языка.
Кстати, неплохо было бы знать какой именно поставщик нам привез эти помидоры.
Рефакторим ..! Supplier1. class Supplier implements Entity2. {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. ...
Система управления складом
1) Упорядочить прием товаров на склад.2) Организовать систему заявок на выдачу.3) Оповещать менеджмент о нехватке товаров.
Доменный эксперт говорит что...
“Магазины подают заявки на получение товаров со склада.”
СкладМагазин
Заявка
Заявка
Доменный эксперт говорит что...
“В заявке указаны требуемые наименования товаров и их количество.”Заявка- номер;- товар => кол-во.;- товар => кол-во.;- товар => кол-во.;- товар => кол-во.;- ...
Ну и, естественно, мы не отгружаем товары с истекшим сроком годности.Их надо сразу списывать.
Доменный эксперт говорит что...
“Если на складе достаточно товаров, то заявке одобряется и машина с товарами отправляется в магазин.”
СкладМагазин
Машина
Заявка
Пишим код ..! Receipt1. class Receipt implements Entity2. {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. }
Пишим код ..! Shop1. class Shop implements Entity2. {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. }
Пишим код ..! Warehouse1. class Warehouse implements Entity2. {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. }
Сводный корень
Сущность Warehouse производит все бизнес-операции с сущностью Receipt и отвечает за изменение ее статуса: одобрено или отложено.В данном случае сущность Warehouse является сводным корнем.В различных контекстах возможны разные сводные корни, например Receipt может быть сводным корнем для Product.
А как изменять статус заявки?
Контекст
В данном примере мы имеем два распределенных контекста: поставщик-склад и склад-магазин.
СкладМагазин
Машина
ЗаявкаПоставщик
Поставщик
Контейнер
Контейнер
Система управления складом
1) Упорядочить прием товаров на склад.2) Организовать систему заявок на выдачу.3) Оповещать менеджмент о нехватке товаров.
Доменный эксперт говорит что...
“Знаете, мы понятия не имеем, что нужно знать менеджерам и как они работают с нашим складом. Давайте поговорим об этом завтра с Аланом.”
Код домена и задание от Алана можнопосмотреть на GitHub.
Вопросы?
Антон ШабовтаE-mail: zloyusr@gmail.com
Facebook: https://www.facebook.com/zloyusrVK: http://vk.com/shabouta
top related