From 40e41b7b26d2fca76042f96934f9f8ed7be0b298 Mon Sep 17 00:00:00 2001 From: eatae Date: Mon, 18 Mar 2024 14:49:40 +0300 Subject: [PATCH 1/9] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C=D1=88?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index c11236e..8db0874 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,31 @@ = [Книга](https://leanpub.com/ddd-in-php) -[Репозиторий с примерами](https://github.com/dddinphp) +[Репозиторий с примерами](https://github.com/dddshelf/ddd-in-php-book-examples) # Содержание ## Предисловие ## Предисловие от Авторов ### [DDD и PHP Сообщество](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/DDD-and-PHP-Community.md) -#### [Краткое содержание глав](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md) -##### [Глава 1. Начало работы с DDD](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#user-content-%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-1-%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%BE-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B-%D1%81-ddd) -##### [Глава 2. Архитектурные стили](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-2-%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%BD%D1%8B%D0%B5-%D1%81%D1%82%D0%B8%D0%BB%D0%B8) -##### [Глава 3. Объекты-Значения](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-3-%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F-value-objects) -##### [Глава 4. Сущности (Entities)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-4-%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8-entities) -##### [Глава 5. Доменные Сервисы (Domain Services)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-5-%D0%B4%D0%BE%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D1%8B-domain-services) -##### [Глава 6. Доменные события](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-6-%D0%B4%D0%BE%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D1%81%D0%BE%D0%B1%D1%8B%D1%82%D0%B8%D1%8F) -##### [Глава 7. Модули](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-7-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D0%B8) -##### [Глава 8. Агрегаты (Aggregates)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-8-%D0%B0%D0%B3%D1%80%D0%B5%D0%B3%D0%B0%D1%82%D1%8B-aggregates) -##### [Глава 9. Фабрики (Factories)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-9-%D1%84%D0%B0%D0%B1%D1%80%D0%B8%D0%BA%D0%B8-factories) -##### [Глава 10: Репозитории (Repositories)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-10-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B8-repositories) -##### [Глава 11: Приложение (Application)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-11-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-application) -##### [Глава 12: Интеграция Ограниченных Контекстов (Bounded Contexts)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-12-%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D1%8F-%D0%BE%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%BD%D1%8B%D1%85-%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82%D0%BE%D0%B2-bounded-contexts) -##### [Приложение: Гексогональная Архитектура (Hexagonal Architecture) в PHP](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B3%D0%B5%D0%BA%D1%81%D0%BE%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%81%D1%82%D1%83%D1%80%D0%B0-hexagonal-architecture-%D0%B2-php) -##### [Код и Примеры](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%BA%D0%BE%D0%B4-%D0%B8-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B) +
+ +### [Краткое содержание глав](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md) +#### [Глава 1. Начало работы с DDD](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#user-content-%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-1-%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%BE-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B-%D1%81-ddd) +#### [Глава 2. Архитектурные стили](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-2-%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%BD%D1%8B%D0%B5-%D1%81%D1%82%D0%B8%D0%BB%D0%B8) +#### [Глава 3. Объекты-Значения](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-3-%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F-value-objects) +#### [Глава 4. Сущности (Entities)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-4-%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8-entities) +#### [Глава 5. Доменные Сервисы (Domain Services)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-5-%D0%B4%D0%BE%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D1%8B-domain-services) +#### [Глава 6. Доменные события](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-6-%D0%B4%D0%BE%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D1%81%D0%BE%D0%B1%D1%8B%D1%82%D0%B8%D1%8F) +#### [Глава 7. Модули](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-7-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D0%B8) +#### [Глава 8. Агрегаты (Aggregates)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-8-%D0%B0%D0%B3%D1%80%D0%B5%D0%B3%D0%B0%D1%82%D1%8B-aggregates) +#### [Глава 9. Фабрики (Factories)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-9-%D1%84%D0%B0%D0%B1%D1%80%D0%B8%D0%BA%D0%B8-factories) +#### [Глава 10: Репозитории (Repositories)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-10-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B8-repositories) +#### [Глава 11: Приложение (Application)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-11-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-application) +#### [Глава 12: Интеграция Ограниченных Контекстов (Bounded Contexts)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-12-%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D1%8F-%D0%BE%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%BD%D1%8B%D1%85-%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82%D0%BE%D0%B2-bounded-contexts) +#### [Приложение: Гексагональная Архитектура (Hexagonal Architecture) в PHP](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B3%D0%B5%D0%BA%D1%81%D0%BE%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%81%D1%82%D1%83%D1%80%D0%B0-hexagonal-architecture-%D0%B2-php) +#### [Код и Примеры](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Preface/Summary-of-Chapters.md#%D0%BA%D0%BE%D0%B4-%D0%B8-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B) +
+ ## [Глава 1. Начало работы с DDD](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md) ### [Почему DDD так важен](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md#%D0%BF%D0%BE%D1%87%D0%B5%D0%BC%D1%83-ddd-%D1%82%D0%B0%D0%BA-%D0%B2%D0%B0%D0%B6%D0%B5%D0%BD) ### [Три столпа DDD](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md#%D1%82%D1%80%D0%B8-%D1%81%D1%82%D0%BE%D0%BB%D0%BF%D0%B0-ddd) @@ -33,13 +37,17 @@ ### [Стратегический обзор](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md#%D1%81%D1%82%D1%80%D0%B0%D1%82%D0%B5%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9-%D0%BE%D0%B1%D0%B7%D0%BE%D1%80) ### [Связанные механизмы: Микросервисы и автономные системы](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md#%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BC%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%B7%D0%BC%D1%8B-%D0%BC%D0%B8%D0%BA%D1%80%D0%BE%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D1%8B-%D0%B8-%D0%B0%D0%B2%D1%82%D0%BE%D0%BD%D0%BE%D0%BC%D0%BD%D1%8B%D0%B5-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B) ### [Резюмируем](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md#%D1%80%D0%B5%D0%B7%D1%8E%D0%BC%D0%B8%D1%80%D1%83%D0%B5%D0%BC) +
+ ## [Глава 2. Архитектурные стили](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter2/Architectural-Styles.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-2-%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%BD%D1%8B%D0%B5-%D1%81%D1%82%D0%B8%D0%BB%D0%B8) ### [Старые, добрые времена](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter2/Architectural-Styles.md#%D1%81%D1%82%D0%B0%D1%80%D1%8B%D0%B5-%D0%B4%D0%BE%D0%B1%D1%80%D1%8B%D0%B5-%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%B0) ### [Многоуровневая архитектура (Layered Architecture)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter2/Architectural-Styles.md#старые-добрые-времена) #### [Пример Многоуровневой Архитектуры](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter2/Architectural-Styles.md#%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D1%83%D1%80%D0%BE%D0%B2%D0%BD%D0%B5%D0%B2%D0%BE%D0%B9-%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D1%8B) -#### [Инверския зависемостей. Гексогональная архитектура.](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter2/Architectural-Styles.md#пример-многоуровневой-архитектуры) +#### [Инверсия зависимостей. Гексагональная архитектура.](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter2/Architectural-Styles.md#пример-многоуровневой-архитектуры) #### [Command Query Responsibility Segregation (CQRS)](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter2/Architectural-Styles.md#command-query-responsibility-segregation-cqrs) #### [Event Sourcing](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter2/Architectural-Styles.md#event-sourcing) +
+ ## [Глава 3. Объекты-Значения](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter3/Value-Objects.md#%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0-3-%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F-the-value-objects) ### [Определение](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter3/Value-Objects.md#%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5) ### [Объекты значения vs Сущности](https://github.com/TalismanFR/dddinphp/blob/master/ru-RU/Chapter3/Value-Objects.md#%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F-vs-%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8) From 70b69c82c8c81f044279a3fa203b7e69d4b7259c Mon Sep 17 00:00:00 2001 From: eatae Date: Mon, 18 Mar 2024 19:15:41 +0300 Subject: [PATCH 2/9] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C=D1=88?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=20Chapter1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tting-Started-with-Domain-Driven-Design.md | 247 ++++++++---------- 1 file changed, 104 insertions(+), 143 deletions(-) diff --git a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md index 0cc1659..d8d69b6 100644 --- a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md +++ b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md @@ -1,191 +1,152 @@ Глава 1. Начало работы с DDD. == -Так откуда весь шум? Если вы уже читали книги на эту тему за авторствами Vaughn Vernon и Eric Evans, -вы, вероятно, знакомы с тем, что мы собираемся рассказать, поскольку мы в значительной степени заимствуем их определения -и объяснения. DDD - это подход, который позволяет нам преуспеть в понимании и построении моделей программных продуктов. -Он предоставляет нам инструменты стратегического и тактического моделирования для разработки высококачественного программного -обеспечения, соотвествующего нашим бизнес-целям. ->Основная цель этой книги - показать примеры тактических шаблонов DDD используя PHP. Если вы хотите -больше узнать о стратегических шаблонах и о DDD впринцепе, вам следует прочитать книги за авторством -Vaughn Vernon и Eric Evans. - -Что очень важно, DDD не связан с технологиями. Вместо этого речь идет о развитии знаний о бизнесе и испльзовании технологий -для обеспечения ценности. Только когда вы сможете понять бизнесс, в котором работает ваша компания -, вы сможете участвовать в процессе создания модели программного обеспечения пораждая Единый Язык (Ubiquitous Language). +Так откуда весь шум? Если вы уже читали книги на эту тему за авторствами Vaughn Vernon и Eric Evans, вы, вероятно, знакомы с тем, что мы собираемся рассказать, поскольку мы в значительной степени заимствуем их определения и объяснения. DDD - это подход, который позволяет нам преуспеть в понимании и построении моделей программных продуктов. Он предоставляет нам инструменты стратегического и тактического моделирования для разработки высококачественного программного +обеспечения, соответcтвующего нашим бизнес-целям. + +> Основная цель этой книги - показать примеры тактических шаблонов DDD используя PHP. Если вы хотите больше узнать о стратегических шаблонах и о DDD в принципе, вам следует прочитать книги за авторством Vaughn Vernon и Eric Evans. + +Что очень важно, DDD не связан с технологиями. Вместо этого речь идет о развитии знаний о бизнесе и использовании технологий для обеспечения ценности. Только когда вы сможете понять бизнес, в котором работает ваша компания, вы сможете участвовать в процессе создания модели программного обеспечения порождая Единый Язык (Ubiquitous Language). ## Почему DDD так важен -Програмнное обеспечение это не только код. Код редко является конечной целью вашей работы. Код это только средства решения -бизнесс-задач. Так почему код должен быть на языке отличном от языка бизнесса? DDD подчеркивает что код и бизнесс должны -говорить на одном языке. Когда барьер преодален, нет необходимости в переводе или утомительной синхронизации, информация не -потеряется. Каждый участник влияет на Бизнесс-Домен, не только разработчики.Получающееся программное обеспечение - + +Программное обеспечение это не только код. Код редко является конечной целью вашей работы. Код это только средства решения бизнес задач. Так почему код должен быть на языке отличном от языка бизнеса? DDD подчеркивает что код и бизнес должны говорить на одном языке. Когда барьер преодолен, нет необходимости в переводе или утомительной синхронизации, информация не потеряется. Каждый участник влияет на Бизнесс-Домен, не только разработчики. Получающееся программное обеспечение - единственная правда для общего языка. -DDD также обеспечивает основу для стратегического и тактического моделирования. Стратегическое проектирование позволяет точно -определить наиболее важные области разрабоки на основе бизнес-ценностей. Тактическо -моделироване нужно для построения работающей Доменой Модели с использование проверянных в бою +DDD также обеспечивает основу для стратегического и тактического моделирования. Стратегическое проектирование позволяет точно определить наиболее важные области разработки на основе бизнес-ценностей. Тактическое моделирование нужно для построения работающей Доменной Модели с использование проверенных в бою строительных блоков и шаблонов. ## Три столпа DDD + Domain-Driven Design это подход к разработке программного обеспечения, который сфокусирован на трёх основных принципах: -1. **Единный Язык (Ubiquitous Language)**: Эксперты предметной области (Domain Experts) и разработчики программного обеспечения работают вместе, -чтобы создать общий язык для разрабатываемых бизнесс-сфер. Тут не может быть противопоставления, это единная команда. -Разработка программного обеспечения это инвестиция в бизнес, а не просто статья расходов. Усилия, прилогаемые к созданию -Единого Языка (Ubiquitous Language), помогают распространить глубокое понимание Домена (Domain) среди всех членов команды. -2. **Стратегическое моделирование (Strategic Design)**: DDD направлен на стратегию управления бизнесом, а не только на технические -аспекты его работы. Это помогает определить внутренние отношения и системы обратной связи раннего предупреждения. -С технической стороны, стратегический дизайн защищает каждую бизнес-услугу, обеспечивая мотивацию для достижения -сервис-ориентированной архитектуры. -3. **Тактическое моделирование (Tactical Design)**: DDD предоставляет инструменты и строительные блоки для итеративной разработки -программного обеспечения. Инструменты тактического моделирования позволяют создать программное -обеспечение, которое не только предельно корректно, но и является тестируемым и менее подверженым ошибкам. + +1. **Единный Язык (Ubiquitous Language):** + Эксперты предметной области (Domain Experts) и разработчики программного обеспечения работают вместе, чтобы создать общий язык для разрабатываемых бизнес-сфер. Тут не может быть противопоставления, это единая команда. Разработка программного обеспечения это инвестиция в бизнес, а не просто статья расходов. Усилия, прилагаемые к созданию Единого Языка (Ubiquitous Language), помогают распространить глубокое понимание Домена (Domain) среди всех членов команды. + +2. **Стратегическое моделирование (Strategic Design):** + DDD направлен на стратегию управления бизнесом, а не только на технические аспекты его работы. Это помогает определить внутренние отношения и системы обратной связи раннего предупреждения. С технической стороны, стратегический дизайн защищает каждую бизнес-услугу, обеспечивая мотивацию для достижения + сервис-ориентированной архитектуры. + +3. **Тактическое моделирование (Tactical Design):** + DDD предоставляет инструменты и строительные блоки для итеративной разработки + программного обеспечения. Инструменты тактического моделирования позволяют создать программное обеспечение, которое не только предельно корректно, но и является тестируемым и менее подверженным ошибкам. + ### Единый язык (Ubiquitous Language) Наряду с темой главы 12 "Интеграция Ограниченного Контекста (Bounded Contexts)", Единый Язык (Ubiquitous Language) является одной из главных сильных сторон DDD. ->Пока будем рассматривать Ограниченный Контекст как концептуальную границу вокруг системы. Единый язык внутри этой границы -имеет особое концептуальное значение. Концепции и определения вне этой границы могут иметь иные значения. +> Пока будем рассматривать Ограниченный Контекст как концептуальную границу вокруг системы. Единый язык внутри этой границы имеет особое концептуальное значение. Концепции и определения вне этой границы могут иметь иные значения. Итак, как найти, изучить и запечатлеть этот особый язык, следующие подсказки помогут вам в этом: -+ Определить ключевые бизнесс-процессы, их входы и выходы. -+ Создайть глосарий терминов и определений. ++ Определить ключевые бизнес-процессы, их входы и выходы. ++ Создайте глоссарий терминов и определений. + Определить важные концепции программного обеспечения с хорошей документацией. + Обменивайтесь этими знаний со всеми членами команды (Разработчиками и Экспертами Предметной Области) -С распростронением DDD, появились новые методы улучшающие и упрощающие процесс создания -Единого Языка (Ubiquitous Language). Самый важный и наболее часто используемый метод это Событийный штурм (Event Storming). + +С распространением DDD, появились новые методы улучшающие и упрощающие процесс создания Единого Языка (Ubiquitous Language). Самый важный и наиболее часто используемый метод это **Событийный штурм** (Event Storming). #### Событийный Штурм (Event Storming) -Alberto Brandolini рассказывает о Событийном Штурме и его преимущствах в своем блоги, и он делает это гораздо лаконичней -чем это могли бы сделать мы. Событийный штурм (Event Storming) - это формат семина для быстрого моделировани сложных бизнес-доменов: -+ Это мощно: подход позволил мне и многим практикующим его специалистам создать всеобъемлищую -модель полго бизнес-потока за часы, а не недели. -+ Это доступно: вся идея состоит в том, чтобы собрать людей у которых есть вопросы и людей у которых есть на них ответы, -в одно месте и совместно построить модель. -+ Это эффективно: результирующая модель идеально соотвествует стилю реализации DDD (особено использую подход Event Sourcing) - и позволяет быстро обределять Контексты и Агрегаты (Aggregate boundaries). -+ Это просто: утра-простые заметки. Без сложных UML диаграмм, которые могут отвлечь участников от сути дискуссии. +Alberto Brandolini рассказывает о Событийном Штурме и его преимуществах в своем блоги, и он делает это гораздо лаконичней чем это могли бы сделать мы. + +Событийный штурм (Event Storming) - это формат семинара для быстрого моделирования сложных бизнес-доменов: ++ Это мощно: подход позволил мне и многим практикующим его специалистам создать всеобъемлющую модель полного бизнес-потока за часы, а не недели. ++ Это доступно: вся идея состоит в том, чтобы собрать людей у которых есть вопросы и людей у которых есть на них ответы, в одно месте и совместно построить модель. ++ Это эффективно: результирующая модель идеально соотвествует стилю реализации DDD (особено использую подход Event Sourcing) и позволяет быстро обределять Контексты и Агрегаты (Aggregate boundaries). ++ Это просто: ультра-простые заметки. Без сложных UML диаграмм, которые могут отвлечь участников от сути дискуссии. + Это захватывающе: я всегда прекрасно проводил время организуя семинары. Люди заражаются энергией и делают больше, чем ожидали от себя. -В ходи работы возникают правильные вопросы, и формируется правильная атмосфера. +В ходи работы возникают правильные вопросы, и формируется правильная атмосфера. Если вы желаете больше узнать о Событийном Штурме, ознакомьтесь с книгой Brandolini, Introducing EventStorming. + ## Определение DDD -DDD это не серебрянная пуля; как и все в програмном обеспечении, всё зависит от контекста. -Старайтесь использовать этот подход чтобы упростить ваш Домен (Domain), а не добавляйте сложности. -Если разрабатываемое вами приложение ориентировано на работу с данными и ваши сценарии в основном подразумевают CRUD -операции (создание, чтение, обновление, удаление), то вам не нужен DDD. Вам всего лишь нужен интерфейс манипуляцией данными в -вашей хранилище. +DDD это не серебряная пуля, как и все в программном обеспечении, всё зависит от контекста. Старайтесь использовать этот подход чтобы упростить ваш Домен (Domain), а не добавляйте сложности. + +Если разрабатываемое вами приложение ориентировано на работу с данными и ваши сценарии в основном подразумевают CRUD операции (создание, чтение, обновление, удаление), то вам не нужен DDD. Вам всего лишь нужен интерфейс манипуляцией данными в вашей хранилище. -Если в вашем приложении реализует менее 30 сценариев использования (Use Cases), может вам проще использовать фраемворки Symfony или Laravel, для -управленией всей бизнес логикой. +Если в вашем приложении реализует менее 30 сценариев использования (Use Cases), может вам проще использовать фраймворки Symfony или Laravel, для управлениея всей бизнес логикой. -Однако, если ваше приложение имеет более 30 сценариев использования, ваша система подвержена движению в сторону Большого Комка Грязи - (Big Ball of Mud). Если вы точно знаете что ваша система будет достаточно сложной, то вам следует рассмореть возможность - использования DDD для борьбы с этой сложностью. - -Если вы знаете, что ваше приложение будет расти и, вероятно, часто изменяться, то DDD определенно -попожет вам в конетроле сложности и реализации рефакторинга вашей модели с течением времени. +Однако, если ваше приложение имеет более 30 сценариев использования, ваша система подвержена движению в сторону Большого Комка Грязи (Big Ball of Mud). Если вы точно знаете что ваша система будет достаточно сложной, то вам следует рассмотреть возможность использования DDD для борьбы с этой сложностью. -Если вам не понятен Домен (Domain), над которым вы работаете, потому что он новый и никто ранее не вкладывал средства -в это решение, это может означать, что он достаточно сложен для того чтобы с ходу начать применять DDD. -В этом случае вам стоит в первую очередь начать тесное взаимодействи с Экспертами Предметной Области Domain Experts для построения -правильной модели. +Если вы знаете, что ваше приложение будет расти и, вероятно, часто изменяться, то DDD определенно поможет вам в контроле сложности и реализации рефакторинга вашей модели с течением времени. + +Если вам не понятен Домен (Domain), над которым вы работаете, потому что он новый и никто ранее не вкладывал средства в это решение, это может означать, что он достаточно сложен для того чтобы с ходу начать применять DDD. +В этом случае вам стоит в первую очередь начать тесное взаимодействие с Экспертами Предметной Области Domain Experts для построения правильной модели. ## Некоторые нюансы -Применять DDD не просто. Необходимо время и усилия, чтобы построить Бизнес-Домен, создать терминалогию, провести иследования -и организовать сотрудничество с Экспертами Предметной Области используя Единый Язык, без профессиональных терминов программистов. -Вам потребуется участие Экспертов Предметной Области. Это в свою очередь потребует открытого, здорового и -непрерывного диалога, чтобы успешно перенести их терминалогию в модель програмного обеспечения. +Применять DDD не просто. Необходимо время и усилия, чтобы построить Бизнес-Домен, создать терминологию, провести исследования и организовать сотрудничество с Экспертами Предметной Области используя Единый Язык, без профессиональных терминов программистов. + +Вам потребуется участие Экспертов Предметной Области. Это в свою очередь потребует открытого, здорового и непрерывного диалога, чтобы успешно перенести их терминологию в модель программного обеспечения. -Вдобавок ко всему, вам придется приложить усилия, чтобы избежать технических моментов реализации на начальном этапе, а -сосредоточеться в первую очередь о поведени объектов и создани Единого Языка (Ubiquitous Language). +Вдобавок ко всему, вам придется приложить усилия, чтобы избежать технических моментов реализации на начальном этапе, а сосредоточиться в первую очередь на поведении объектов и создании Единого Языка (Ubiquitous Language). ## Стратегический обзор -Чтобы дать общий обзор стратегической стороны DDD, мы будем использовать подход из книги Джимми Нильссона «[Применение доменно-управляемого дизайна и шаблонов](http://www.williamspublishing.com/Books/978-5-8459-1296-1.html)» (Jimmy Nilsson "Applying Domain-Driven Design and Patterns"). Рассмотрим два разных пространства: пространство задач и пространство решений. - -В пространстве задач, DDD использует Домена (Domains) и Субдомены (Subdomains) для группировки и организации того, что -компания хочет решить (реализовать). В случае онлайн-агенства путешенствий (OTA), проблема заключается в том, чтобы иметь дело -с такими вещами, как авиабилеты и бронирование гостиниц. Такой Домен может бы огранизован в разные Субдомены, такие как: -ценообразование, инветаризация, управление пользователями и др. - -В пространстве решений, DDD предоставляет два патерна: Ограниченный Контекст (Bounded Context) и Карты Контекста (Context Map). -Целью является то, чтобы определить, как обеспечить реализацию для всех идентифицированных Поддоменов путем -определения их взаимодествия друг с другом и деталей этих самых взаимодествий. -Продолжая пример OTA каждый из Поддоменов будет решен с помощью реализации ограниченного контекста - например, -рассмотрим пользовательское веб-приложение, разработанное командой для Субдомена Управления Пользователями. -Карта контекста покажет, как каждый из Ограниченных контекстов связана с другими. Внутри Карты Контекста, мы можем видеть, какой тип -отношений имеют два Ограниченных Контекста (пример: клиент-поставщик (customer-supplier), партнеры (partners)). + +Чтобы дать общий обзор стратегической стороны DDD, мы будем использовать подход из книги Джимми Нильссона «*[Применение доменно-управляемого дизайна и шаблонов]*(http://www.williamspublishing.com/Books/978-5-8459-1296-1.html)» (Jimmy Nilsson "Applying Domain-Driven Design and Patterns"). Рассмотрим два разных пространства: пространство задач и пространство решений. + +В пространстве задач, DDD использует Домены (Domains) и Субдомены (Subdomains) для группировки и организации того, что компания хочет решить (реализовать). В случае онлайн-агенства путешествий (OTA), проблема заключается в том, чтобы иметь дело с такими вещами, как авиабилеты и бронирование гостиниц. Такой Домен может бы огранизован в разные Субдомены, такие как: ценообразование, инвентаризация, управление пользователями и др. + +В пространстве решений, DDD предоставляет два паттерна: Ограниченный Контекст (Bounded Context) и Карты Контекста (Context Map). Целью является то, чтобы определить, как обеспечить реализацию для всех идентифицированных Поддоменов путем определения их взаимодействия друг с другом и деталей этих самых взаимодествий. +Продолжая пример OTA каждый из Поддоменов будет решен с помощью реализации ограниченного контекста. Например, рассмотрим пользовательское веб-приложение, разработанное командой для Субдомена Управления Пользователями. +Карта контекста покажет, как каждый из Ограниченных контекстов связан с другими. Внутри Карты Контекста, мы можем видеть, какой тип отношений имеют два Ограниченных Контекста (пример: клиент-поставщик (customer-supplier), партнеры (partners)). Идеальный подход состоит в том, чтобы каждый Субдомен реализовывался одним ограниченным контекстом, но это не всегда возможно. -С точки зрения реализации, следуя DDD, вы получите распределенные архитектуры. Как вы, возможно, уже знаете, распределенные архитектуры -более сложны, чем монолитные, так почему такой подход интересен, особенно у больших и сложных компаний? Это действительно стоит того? +С точки зрения реализации, следуя DDD, вы получите распределенные архитектуры. Как вы, возможно, уже знаете, распределенные архитектуры более сложны, чем монолитные, так почему такой подход интересен, особенно у больших и сложных компаний? Это действительно стоит того? В общем, да, это того стоит. -Подтверждено, что распределенные архитектуры увеличивают общую производительность компании, поскольку они определяют границы вашего продукта, -которые будут разрабатывать целевые команды разработчиков. +Подтверждено, что распределенные архитектуры увеличивают общую производительность компании, поскольку они определяют границы вашего продукта, которые будут разрабатывать целевые команды разработчиков. -Еслива ваш Домен (проблема которую вам нужно решить) - не сложный, то применение стратегической части DDD может добавить ненужные -накладные расходы и замедлить скорость разработки. +Если ваш Домен (проблема которую вам нужно решить) - не является сложным, то применение стратегической части DDD может добавить ненужные накладные расходы и замедлить скорость разработки. -Если вы хотите узнать больше о стратегической части DDD, вам следует взглянуть на первые три главы книги Вона Вернона -[Реализация методов предметно-ориентированного проектирования](http://www.williamspublishing.com/Books/978-5-8459-1881-9.html) -или книгу Эрика Эвинса "[Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем](http://www.williamspublishing.com/Books/978-5-8459-1597-9.html)". +Если вы хотите узнать больше о стратегической части DDD, вам следует взглянуть на первые три главы книги Вона Вернона +*[Реализация методов предметно-ориентированного проектирования]*(http://www.williamspublishing.com/Books/978-5-8459-1881-9.html) +или книгу Эрика Эвинса "*[Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем]*(http://www.williamspublishing.com/Books/978-5-8459-1597-9.html)". ## Связанные механизмы: Микросервисы и автономные системы. -Существуют другие механизмы, продвигающие архитектуры,которые следуют тем же принципам что и DDD. Микросервисы и Автономные системы являются хорошими примерами этого. -Джеймс Льюис и Мартин ФАулер дают определение Микросервисам в [Microservices Resource Guide](https://martinfowler.com/microservices/): +Существуют другие механизмы, продвигающие архитектуры, которые следуют тем же принципам что и DDD. Микросервисы и Автономные системы являются хорошими примерами этого. +Джеймс Льюис и Мартин Фаулер дают определение Микросервисам в *[Microservices Resource Guide]*(https://martinfowler.com/microservices/): ->Архитектурный стиль микросервисов — это подход, при котором единое приложение строится как набор небольших сервисов, каждый из которых работает в собственном процессе и коммуницирует с остальными используя легковесные механизмы, как правило HTTP. Эти сервисы построены вокруг бизнес-потребностей и развертываются независимо с использованием полностью автоматизированной среды. Существует абсолютный минимум централизованного управления этими сервисами. Сами по себе эти сервисы могут быть написаны на разных языках и использовать разные технологии хранения данных. +> Архитектурный стиль микросервисов — это подход, при котором единое приложение строится как набор небольших сервисов, каждый из которых работает в собственном процессе и коммуницирует с остальными используя легковесные механизмы, как правило HTTP. Эти сервисы построены вокруг бизнес-потребностей и развертываются независимо с использованием полностью автоматизированной среды. Существует абсолютный минимум централизованного управления этими сервисами. Сами по себе эти сервисы могут быть написаны на разных языках и использовать разные технологии хранения данных. -Если вы хотите узнать больше о Микросервисах, начните с их руководства. Как это связанно с DDD? -Как объясняется в книге С.ма Ньюмана "[Создание Микросервисов](https://www.piter.com/product/sozdanie-mikroservisov)", -микросервисы являются реализацией Ограниченного контекста (Bounded Context) в DDD. +Если вы хотите узнать больше о Микросервисах, начните с их руководства. Как это связанно с DDD? +Как объясняется в книге Сэма Ньюмана "*[Создание Микросервисов]*(https://www.piter.com/product/sozdanie-mikroservisov)", микросервисы являются реализацией Ограниченного контекста (Bounded Context) в DDD. -Помимо Микросервисов, другим связанным механизмом является Автономные Системы (Self-Contained Systems). Согласно веб-сайту -[Self-Contained Systems](http://scs-architecture.org/) +Помимо Микросервисов, другим связанным механизмом является Автономные Системы (Self-Contained Systems). Согласно веб-сайту [Self-Contained Systems]*(http://scs-architecture.org/) ->Подход «Автономная система» - это архитектура, которая фокусируется на разделении функциональности на множество независимых систем, что делает полную логическую систему совместной работой множества небольших программных систем. ->Это позволяет избежать проблемы крупных монолитов, которые постоянно растут и в конечном итоге становятся непригодными для эксплуатации. ->За последние несколько лет мы видели преимущества этого подхода во многих средних и крупных проектах. Идея состоит в том, чтобы разбить большую систему на несколько более мелких автономных систем или (SCS), которые следуют определенным правилам. +> Подход «Автономная система» - это архитектура, которая фокусируется на разделении функциональности на множество независимых систем, что делает полную логическую систему совместной работой множества небольших программных систем. +> Это позволяет избежать проблемы крупных монолитов, которые постоянно растут и в конечном итоге становятся непригодными для эксплуатации. +> За последние несколько лет мы видели преимущества этого подхода во многих средних и крупных проектах. Идея состоит в том, чтобы разбить большую систему на несколько более мелких автономных систем или (SCS), которые следуют определенным правилам. На сайте также изложены семь характеристик SCS: - - Каждый SCS является автономным веб-приложением. - Для домена SCS все логические данные, логика обработки этих данных и весь код для визуализации веб-интерфейса содержатся в SCS. - SCS может выполнять свои основные сценарии использования самостоятельно, не полагаясь на другие доступные системы. - - Каждый SCS принадлежит одной команде. Это не обязательно означает, что только одна команда может изменить код, - но команда-владелец имеет решающее значение в отношении того, что входит в базу кода, например, путем объединения pull-requests. - - Связь с другими SCS или сторонними системами является асинхронной, где это возможно. - В частности, к другим SCS или внешним системам нельзя обращаться синхронно в пределах собственного цикла запроса/ответа SCS. - Это разъединяет системы, уменьшает последствия отказа и, таким образом, поддерживает автономию. - Цель состоит в том, чтобы развязать время выполнения задач: SCS должен работать, даже если другие SCS временно отключены. - Это может быть достигнуто, даже если связь на техническом уровне является синхронной, например, путем репликации данных или запросов буферизации. - - SCS может иметь дополнительный сервисный API. Поскольку у SCS есть собственный веб-интерфейс, он может взаимодействовать с пользователем, не обращаясь к службе UI. Однако API для мобильных клиентов или для других SCS может быть полезен. - - Каждая SCS должна включать данные и логику. Чтобы действительно реализовать какие-либо значимые функции, необходимы оба компонента. - - SCS должен сделать свои функции доступными для конечных пользователей с помощью собственного пользовательского интерфейса. - Поэтому у SCS не должно быть общего пользовательского интерфейса с другими SCS. SCS могут по-прежнему иметь ссылки друг на друга. - Однако асинхронная интеграция означает, что SCS все еще должен работать, если пользовательский интерфейс другого SCS недоступен. - Чтобы избежать тесной связи SCS, не следует делиться бизнес-кодом с другими SCS. - Может быть хорошо создать pull-request для SCS или использовать общие библиотеки, например: драйверы базы данных или oAuthclients. - - #### Упражнение - Обсудите плюсы и минусы распределенных архитектур с вашими коллегами по работе. Подумайте об использовании разных языков, - процессов развертывания, отвественности за инфраструктуру и так далее. - - ## Резюмируем - В этой главе вы узнали: - - - DDD - это не про технологию; на самом деле речь идет о предоставлении результатов в той области - в которой вы работаете, сосредоточившись на разработке модели. Каждый принимает участие в процессе проектирование Домена, и - разработчики и Эксперты Предметной Области объединяются для создания базы знаний, используя - один и тот же, Единый Язык (Ubiquitous Language). - - DDD предоставляет инструменты тактического и стратегического моделирования для разработки высококачественного ПО. - Стратегический дизайн нацелен на направление бизнеса, помогает определить внутрениие отношения и технически защищает каждую бизнес-службу, - создавая сильные границы. Тактическое проектирование предоставляет полезные строительные блоки для итеративного проектирования. - - DDD имеет смысл только в определенных задачах. Это не серебрянная пуля для любой проблемы в ПО, поэтому использовать подход или нет, во многом зависит от - сложности, с которой вы сталкнетесь. - - DDD - это долгосрочная инвестиция; это требует активных усилий. Эксперты Предметной Области должны тесно сотрудничать с разработчиками, - а разработчики должны думать с точки зрения бизнеса. В конечном итоге клиенты бизнеса - это те, кто должены быть довольны вашей работой. - - Реализация DDD требует усилий. Если бы это было легко, все писали бы качественный код. - Будьте готовы, потому что вы скоро узнаете, как писать код, который при прочтении отлично описывает бизнес вашей компании. - Наслаждайтесь этим приключением! \ No newline at end of file +- Каждый SCS является автономным веб-приложением. Для домена SCS все логические данные, логика обработки этих данных и весь код для визуализации веб-интерфейса содержатся в SCS. SCS может выполнять свои основные сценарии использования самостоятельно, не полагаясь на другие доступные системы. + +- Каждый SCS принадлежит одной команде. Это не обязательно означает, что только одна команда может изменить код, но команда-владелец имеет решающее значение в отношении того, что входит в базу кода, например, путем объединения pull-requests. + +- Связь с другими SCS или сторонними системами является асинхронной, где это возможно. В частности, к другим SCS или внешним системам нельзя обращаться синхронно в пределах собственного цикла запроса/ответа SCS. Это разъединяет системы, уменьшает последствия отказа и, таким образом, поддерживает автономию. Цель состоит в том, чтобы развязать время выполнения задач: SCS должен работать, даже если другие SCS временно отключены. Это может быть достигнуто, даже если связь на техническом уровне является синхронной, например, путем репликации данных или запросов буферизации. + +- SCS может иметь дополнительный сервисный API. Поскольку у SCS есть собственный веб-интерфейс, он может взаимодействовать с пользователем, не обращаясь к службе UI. Однако API для мобильных клиентов или для других SCS может быть полезен. + +- Каждая SCS должна включать данные и логику. Чтобы действительно реализовать какие-либо значимые функции, необходимы оба компонента. + +- SCS должен сделать свои функции доступными для конечных пользователей с помощью собственного пользовательского интерфейса. Поэтому у SCS не должно быть общего пользовательского интерфейса с другими SCS. SCS могут по-прежнему иметь ссылки друг на друга. Однако асинхронная интеграция означает, что SCS все еще должен работать, если пользовательский интерфейс другого SCS недоступен. + +Чтобы избежать тесной связи SCS, не следует делиться бизнес-кодом с другими SCS. Может быть хорошо создать pull-request для SCS или использовать общие библиотеки, например: драйверы базы данных или oAuthclients. + +#### Упражнение +Обсудите плюсы и минусы распределенных архитектур с вашими коллегами по работе. Подумайте об использовании разных языков, процессов развертывания, ответственности за инфраструктуру и так далее. + +## Резюмируем +В этой главе вы узнали: + +- DDD - это не про технологию. На самом деле речь идет о предоставлении результатов в той области в которой вы работаете, сосредоточившись на разработке модели. Каждый принимает участие в процессе проектирование Домена, и разработчики и Эксперты Предметной Области объединяются для создания базы знаний, используя один и тот же, Единый Язык (Ubiquitous Language). + +- DDD предоставляет инструменты тактического и стратегического моделирования для разработки высококачественного ПО. Стратегический дизайн нацелен на направление бизнеса, помогает определить внутренние отношения и технически защищает каждую бизнес-службу, создавая сильные границы. + Тактическое проектирование предоставляет полезные строительные блоки для итеративного проектирования. + +- DDD имеет смысл только в определенных задачах. Это не серебряная пуля для любой проблемы в ПО, поэтому использовать подход или нет, во многом зависит от + сложности, с которой вы столкнетесь. + +- DDD - это долгосрочная инвестиция. Это требует активных усилий. Эксперты Предметной Области должны тесно сотрудничать с разработчиками, а разработчики должны думать с точки зрения бизнеса. В конечном итоге клиенты бизнеса - это те, кто должны быть довольны вашей работой. + +Реализация DDD требует усилий. Если бы это было легко, все писали бы качественный код. Будьте готовы, потому что вы скоро узнаете, как писать код, который при прочтении отлично описывает бизнес вашей компании. +Наслаждайтесь этим приключением! \ No newline at end of file From 6518fac91ec296a0f80a01603440136b9da92cf9 Mon Sep 17 00:00:00 2001 From: eatae Date: Mon, 18 Mar 2024 19:41:48 +0300 Subject: [PATCH 3/9] =?UTF-8?q?Chapter1:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20tag=20
?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tting-Started-with-Domain-Driven-Design.md | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md index d8d69b6..644ea4f 100644 --- a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md +++ b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md @@ -3,10 +3,15 @@ Так откуда весь шум? Если вы уже читали книги на эту тему за авторствами Vaughn Vernon и Eric Evans, вы, вероятно, знакомы с тем, что мы собираемся рассказать, поскольку мы в значительной степени заимствуем их определения и объяснения. DDD - это подход, который позволяет нам преуспеть в понимании и построении моделей программных продуктов. Он предоставляет нам инструменты стратегического и тактического моделирования для разработки высококачественного программного обеспечения, соответcтвующего нашим бизнес-целям. +
+ > Основная цель этой книги - показать примеры тактических шаблонов DDD используя PHP. Если вы хотите больше узнать о стратегических шаблонах и о DDD в принципе, вам следует прочитать книги за авторством Vaughn Vernon и Eric Evans. +
Что очень важно, DDD не связан с технологиями. Вместо этого речь идет о развитии знаний о бизнесе и использовании технологий для обеспечения ценности. Только когда вы сможете понять бизнес, в котором работает ваша компания, вы сможете участвовать в процессе создания модели программного обеспечения порождая Единый Язык (Ubiquitous Language). +
+ ## Почему DDD так важен Программное обеспечение это не только код. Код редко является конечной целью вашей работы. Код это только средства решения бизнес задач. Так почему код должен быть на языке отличном от языка бизнеса? DDD подчеркивает что код и бизнес должны говорить на одном языке. Когда барьер преодолен, нет необходимости в переводе или утомительной синхронизации, информация не потеряется. Каждый участник влияет на Бизнесс-Домен, не только разработчики. Получающееся программное обеспечение - @@ -15,6 +20,8 @@ DDD также обеспечивает основу для стратегического и тактического моделирования. Стратегическое проектирование позволяет точно определить наиболее важные области разработки на основе бизнес-ценностей. Тактическое моделирование нужно для построения работающей Доменной Модели с использование проверенных в бою строительных блоков и шаблонов. +
+ ## Три столпа DDD Domain-Driven Design это подход к разработке программного обеспечения, который сфокусирован на трёх основных принципах: @@ -30,31 +37,48 @@ Domain-Driven Design это подход к разработке програм DDD предоставляет инструменты и строительные блоки для итеративной разработки программного обеспечения. Инструменты тактического моделирования позволяют создать программное обеспечение, которое не только предельно корректно, но и является тестируемым и менее подверженным ошибкам. +
+ ### Единый язык (Ubiquitous Language) Наряду с темой главы 12 "Интеграция Ограниченного Контекста (Bounded Contexts)", Единый Язык (Ubiquitous Language) является одной из главных сильных сторон DDD. + +
+ > Пока будем рассматривать Ограниченный Контекст как концептуальную границу вокруг системы. Единый язык внутри этой границы имеет особое концептуальное значение. Концепции и определения вне этой границы могут иметь иные значения. +> +
Итак, как найти, изучить и запечатлеть этот особый язык, следующие подсказки помогут вам в этом: + Определить ключевые бизнес-процессы, их входы и выходы. + + Создайте глоссарий терминов и определений. + + Определить важные концепции программного обеспечения с хорошей документацией. -+ Обменивайтесь этими знаний со всеми членами команды (Разработчиками и Экспертами Предметной Области) -С распространением DDD, появились новые методы улучшающие и упрощающие процесс создания Единого Языка (Ubiquitous Language). Самый важный и наиболее часто используемый метод это **Событийный штурм** (Event Storming). ++ Обменивайтесь этими знаниями со всеми членами команды (Разработчиками и Экспертами Предметной Области). + +С распространением DDD, появились новые методы улучшающие и упрощающие процесс создания Единого Языка (Ubiquitous Language). Самый важный и наиболее часто используемый метод это Событийный штурм (Event Storming). + +
#### Событийный Штурм (Event Storming) Alberto Brandolini рассказывает о Событийном Штурме и его преимуществах в своем блоги, и он делает это гораздо лаконичней чем это могли бы сделать мы. Событийный штурм (Event Storming) - это формат семинара для быстрого моделирования сложных бизнес-доменов: + Это мощно: подход позволил мне и многим практикующим его специалистам создать всеобъемлющую модель полного бизнес-потока за часы, а не недели. + + Это доступно: вся идея состоит в том, чтобы собрать людей у которых есть вопросы и людей у которых есть на них ответы, в одно месте и совместно построить модель. + + Это эффективно: результирующая модель идеально соотвествует стилю реализации DDD (особено использую подход Event Sourcing) и позволяет быстро обределять Контексты и Агрегаты (Aggregate boundaries). + + Это просто: ультра-простые заметки. Без сложных UML диаграмм, которые могут отвлечь участников от сути дискуссии. + + Это захватывающе: я всегда прекрасно проводил время организуя семинары. Люди заражаются энергией и делают больше, чем ожидали от себя. -В ходи работы возникают правильные вопросы, и формируется правильная атмосфера. +В ходе работы возникают правильные вопросы, и формируется правильная атмосфера. Если вы желаете больше узнать о Событийном Штурме, ознакомьтесь с книгой Brandolini, Introducing EventStorming. +
## Определение DDD @@ -71,6 +95,8 @@ DDD это не серебряная пуля, как и все в програ Если вам не понятен Домен (Domain), над которым вы работаете, потому что он новый и никто ранее не вкладывал средства в это решение, это может означать, что он достаточно сложен для того чтобы с ходу начать применять DDD. В этом случае вам стоит в первую очередь начать тесное взаимодействие с Экспертами Предметной Области Domain Experts для построения правильной модели. +
+ ## Некоторые нюансы Применять DDD не просто. Необходимо время и усилия, чтобы построить Бизнес-Домен, создать терминологию, провести исследования и организовать сотрудничество с Экспертами Предметной Области используя Единый Язык, без профессиональных терминов программистов. @@ -79,6 +105,8 @@ DDD это не серебряная пуля, как и все в програ Вдобавок ко всему, вам придется приложить усилия, чтобы избежать технических моментов реализации на начальном этапе, а сосредоточиться в первую очередь на поведении объектов и создании Единого Языка (Ubiquitous Language). +
+ ## Стратегический обзор Чтобы дать общий обзор стратегической стороны DDD, мы будем использовать подход из книги Джимми Нильссона «*[Применение доменно-управляемого дизайна и шаблонов]*(http://www.williamspublishing.com/Books/978-5-8459-1296-1.html)» (Jimmy Nilsson "Applying Domain-Driven Design and Patterns"). Рассмотрим два разных пространства: пространство задач и пространство решений. @@ -100,22 +128,32 @@ DDD это не серебряная пуля, как и все в програ *[Реализация методов предметно-ориентированного проектирования]*(http://www.williamspublishing.com/Books/978-5-8459-1881-9.html) или книгу Эрика Эвинса "*[Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем]*(http://www.williamspublishing.com/Books/978-5-8459-1597-9.html)". +
+ ## Связанные механизмы: Микросервисы и автономные системы. Существуют другие механизмы, продвигающие архитектуры, которые следуют тем же принципам что и DDD. Микросервисы и Автономные системы являются хорошими примерами этого. Джеймс Льюис и Мартин Фаулер дают определение Микросервисам в *[Microservices Resource Guide]*(https://martinfowler.com/microservices/): +
+ > Архитектурный стиль микросервисов — это подход, при котором единое приложение строится как набор небольших сервисов, каждый из которых работает в собственном процессе и коммуницирует с остальными используя легковесные механизмы, как правило HTTP. Эти сервисы построены вокруг бизнес-потребностей и развертываются независимо с использованием полностью автоматизированной среды. Существует абсолютный минимум централизованного управления этими сервисами. Сами по себе эти сервисы могут быть написаны на разных языках и использовать разные технологии хранения данных. +
+ Если вы хотите узнать больше о Микросервисах, начните с их руководства. Как это связанно с DDD? Как объясняется в книге Сэма Ньюмана "*[Создание Микросервисов]*(https://www.piter.com/product/sozdanie-mikroservisov)", микросервисы являются реализацией Ограниченного контекста (Bounded Context) в DDD. Помимо Микросервисов, другим связанным механизмом является Автономные Системы (Self-Contained Systems). Согласно веб-сайту [Self-Contained Systems]*(http://scs-architecture.org/) +
+ > Подход «Автономная система» - это архитектура, которая фокусируется на разделении функциональности на множество независимых систем, что делает полную логическую систему совместной работой множества небольших программных систем. > Это позволяет избежать проблемы крупных монолитов, которые постоянно растут и в конечном итоге становятся непригодными для эксплуатации. > За последние несколько лет мы видели преимущества этого подхода во многих средних и крупных проектах. Идея состоит в том, чтобы разбить большую систему на несколько более мелких автономных систем или (SCS), которые следуют определенным правилам. +
+ На сайте также изложены семь характеристик SCS: - Каждый SCS является автономным веб-приложением. Для домена SCS все логические данные, логика обработки этих данных и весь код для визуализации веб-интерфейса содержатся в SCS. SCS может выполнять свои основные сценарии использования самостоятельно, не полагаясь на другие доступные системы. @@ -135,6 +173,8 @@ DDD это не серебряная пуля, как и все в програ #### Упражнение Обсудите плюсы и минусы распределенных архитектур с вашими коллегами по работе. Подумайте об использовании разных языков, процессов развертывания, ответственности за инфраструктуру и так далее. +
+ ## Резюмируем В этой главе вы узнали: From 8edfa60dde89b4f7dc807d115b3f1590b5231f1e Mon Sep 17 00:00:00 2001 From: eatae Date: Tue, 19 Mar 2024 15:53:40 +0300 Subject: [PATCH 4/9] Chapter2: add tags and correction --- ru-RU/Chapter2/Architectural-Styles.md | 697 +++++++++++++------------ 1 file changed, 374 insertions(+), 323 deletions(-) diff --git a/ru-RU/Chapter2/Architectural-Styles.md b/ru-RU/Chapter2/Architectural-Styles.md index 9e167f7..1d103c1 100644 --- a/ru-RU/Chapter2/Architectural-Styles.md +++ b/ru-RU/Chapter2/Architectural-Styles.md @@ -1,28 +1,26 @@ Глава 2. Архитектурные стили == -Чтобы иметь возможность создавать сложные приложения, одним из ключевых требований является наличие архитектурного дизайна, -который соответствует потребностям приложения. Одно из преимущества DDD заключается в том, что он не привязан к какому-либо -конкретному стилю архитектуры. Вместо этого мы можем свободно выбирать архитектуру, которая наилучшим образом соответствует -потребностям каждого Ограниченного Контекста внутри основного Домена, который предлагает разнообразный набор архитектурных -решений для каждого конкретной проблемы Домена. +Чтобы иметь возможность создавать сложные приложения, одним из ключевых требований является наличие архитектурного дизайна, который соответствует потребностям приложения. Одно из преимущества DDD заключается в том, что он не привязан к какому-либо конкретному стилю архитектуры. Вместо этого мы можем свободно выбирать архитектуру, которая наилучшим образом соответствует потребностям каждого Ограниченного Контекста внутри основного Домена, который предлагает разнообразный набор архитектурных решений для каждого конкретной проблемы Домена. -Например, Система Обработки Заказов может использовать подход Event Sourcing для отслеживания всех различных операций с заказом; -Каталог продуктов может использовать CQRS для предоставления информации о продуктах различным клиентам; Система Управления Контентом -может использовать Гексогональную Архитектуру (Hexagonal Architecture) для представления требований, таких как блоги, статичные страницы и т.д. +Например: +- Система Обработки Заказов может использовать подход **Event Sourcing** для отслеживания всех различных операций с заказом. -В этой главе представленно введение во все соответствующие архитектурные стили в контексте PHP, следуя эволюции от традиционного -(старого) PHP-кода к более сложной архитектуре. Обратите внимание, что, хотя существует много дургих существующих архитектурных стилей, -таких как Data Fabric или SOA, некоторые из них оказались слишком сложными для представления средствами PHP. +- Каталог продуктов может использовать **CQRS** для предоставления информации о продуктах различным клиентам. + +- Система Управления Контентом может использовать **Гексогональную Архитектуру** (Hexagonal Architecture) для представления требований, таких как блоги, статичные страницы и т.д. + +В этой главе представлено введение во все соответствующие архитектурные стили в контексте PHP, следуя эволюции от традиционного (старого) PHP-кода к более сложной архитектуре. Обратите внимание, что, хотя существует много других существующих архитектурных стилей, таких как Data Fabric или SOA, некоторые из них оказались слишком сложными для представления средствами PHP. + +
## Старые, добрые времена -До релиза PHP версии 4, язык не охватывал Объектно-Ориентированную Парадигму. В то время обычным способом написания приложения -было использвоание процедур и глобального состояния. Такие понятия, как Разделение Ответственности (Separation of Concerns (SoC)) и -Модель-Представление-Контролер (Model-View-Controller (MVC)) были широко распространены среди сообщества PHP. +До релиза PHP версии 4, язык не охватывал Объектно-Ориентированную Парадигму. В то время обычным способом написания приложения было использование процедур и глобального состояния. Такие понятия, как **Разделение Ответственности** (Separation of Concerns (SoC)) и **Модель-Представление-Контролер** (Model-View-Controller (MVC)) были широко распространены среди сообщества PHP. -Пример ниже представляет собой приложение, написанное традиционным способом, где приложение состоят из множества фронтальных контроллеров, смешанных -с кодом HTML. В те времена слой Инфраструктуры, Представления, UI, и Доменные слои были перемешаны. +Пример ниже представляет собой приложение, написанное традиционным способом, где приложение состоят из множества фронтальных контроллеров, смешанных с кодом HTML. В те времена слой Инфраструктуры, Представления, UI, и Доменные слои были перемешаны. + +
```php ``` -![Cool](https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/cool.jpeg) +!*[Cool]*(https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/cool.jpeg) + +
-Этот стиль кода часто называют Большой Ком Грязи (Big Ball of Mud). -Однако в этом стиле улучшение стало то, что заголовок и нижний колонтитул веб-страницы были заключены в отдельные файлы. -Это позвонило избежать дублирования и способствовало повторному использованию: +Этот стиль кода часто называют **Большой Ком Грязи** (Big Ball of Mud). +Однако в этом стиле улучшение стало то, что заголовок и нижний колонтитул веб-страницы были заключены в отдельные файлы. Это позвонило избежать дублирования и способствовало повторному использованию: + +
```php ``` -В настоящее время, хотя это крайн нежелательно, все еще встречаются приложение которые используют подобный процедурный подход. (скорре всего legacy код) -Основным недостатком этого стиля архитектуры является то, что нет реального Разделения Ответственности (Separation of Concerns). Обслуживание и -стоимость разработки приложения, разработанного таким образом, резко возрастает по сравнению с другими известными и проверенными архитектурами. +
+ +В настоящее время, хотя это крайне нежелательно, все еще встречаются приложения которые используют подобный процедурный подход (скорее всего legacy код). Основным недостатком этого стиля архитектуры является то, что нет реального **Разделения Ответственности** (Separation of Concerns). Обслуживание и стоимость разработки приложения, разработанного таким образом, резко возрастает по сравнению с другими известными и проверенными архитектурами. + +
## Многоуровневая архитектура (Layered Architecture) -С точки зрения удобства поддержки и повторого использования кода, наилучший способ сделать код более простым в обслуживании - это разделить концепции, то есть создать слои для каждой -отдельной задачи. В нашем предыдущем примере легко сформировать разные уровни: первй для икапсуляции доступа и манипулирования данными, второй для решения проблем -инфраструктуры и третий для оркестрирования двух предыдущих. -Основное правило многоуровневой архитектуры - это то, что каждый слой должен иметь связь (возможно исопльзовать) с нижестоящими слоями, -как показано на рисунке ниже -![Layered Architecture](https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/image--004.jpg) +С точки зрения удобства поддержки и повторного использования кода, наилучший способ сделать код более простым в обслуживании - это разделить концепции, то есть создать слои для каждой отдельной задачи. +В нашем предыдущем примере легко сформировать разные уровни: +- первый для инкапсуляции доступа и манипулирования данными. +- второй для решения проблем инфраструктуры. +- третий для оркестрирования двух предыдущих. + +Основное правило многоуровневой архитектуры - это то, что каждый слой должен иметь связь (возможность использовать) с нижестоящими слоями, как показано на рисунке ниже: -Многоуровневая архитектура стремится к разделению различных компонентов приложения. Например, с точки зрения -предыдущего примера, Представление сообщения в блоге должно быть полностью незаисимым от сообщения в блоге как -концептуальной сущности. Сообщение в блоге как концептуальная сущность может может быть связано с одним или несколькими -представлениями, вместо того чтобы быть тесно связанным с каким либо определенным представлением. Это принято называть -Разделением Ответственности (Separation of Concerns). +!*[Layered Architecture]*(https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/image--004.jpg) -Другой парадигмой и шаблоном архитектуры, преследующей ту же цель, является шаблон Model-View-Controller. Изначально он -задумывался и широко использовался для создания настольных приложений с графическим интерфейсом, а теперь он в основном -использвуется в веб-приложениях благодаря популяризации веб-фраемворков, таких как Symfony, Zend, CodeIgniter. +**Многоуровневая Архитектура** стремится к разделению различных компонентов приложения. Например, с точки зрения предыдущего примера, **Представление** сообщения в блоге должно быть полностью независимым от сообщения в блоге как концептуальной сущности. +Сообщение в блоге как концептуальная сущность может быть отображено одним или несколькими **Представлениями**, вместо того чтобы быть тесно связанным с каким либо конкретным **Представлением**. Это принято называть **Разделением Ответственности** (Separation of Concerns). + +Другой парадигмой и шаблоном архитектуры, преследующей ту же цель, является шаблон **Model-View-Controller**. Изначально он задумывался и широко использовался для создания настольных приложений с графическим интерфейсом, а теперь он в основном использвуется в веб-приложениях благодаря популяризации веб-фреймворков, таких как Symfony, Zend, CodeIgniter. + +
### Model-View-Controller -MVC - это архитектурный шаблон и парадигма, которая делит приложение на три основных уровня, описанных в следующих пунктах: - - - Модель (The Model): Содержит все поведения Доменой Модели. Этот уровень управляет всеми данными, логикой, и бизнесс-правилами - назависимо от уровня представления данных. Уровень Модели являетяс сердцем и душой каждого приложения MVC. - - Контроллер (The Controller): Организует взаимодествия между другими уровнями приложения, запускает дествия в слое Модели - для обновления ее состояния и обновления слоя Представления связанного с этой моделью. - Кроме того, контроллер может отправлять сообщения на уровень Представления для изменения конкретного отображения Модели. - - Представление (The View): Отображает различные Представления слоя Модели и предоставляет способ вызывать изменения в - состоянии модели. - -![The MVC pattern](https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/image--006.jpg) - +**MVC** +Это архитектурный шаблон и парадигма, которая делит приложение на три основных уровня, описанных в следующих пунктах: + +**Модель (The Model)** +Содержит все поведения Доменой Модели. Этот уровень управляет всеми данными, логикой, и бизнесс-правилами назависимо от уровня представления данных. Уровень Модели является сердцем и душой каждого приложения MVC. + +**Контроллер (The Controller)** +Организует взаимодействия между другими уровнями приложения, запускает действия в слое Модели для обновления ее состояния и обновления слоя Представления связанного с этой моделью. +Кроме того, контроллер может отправлять сообщения на уровень Представления для изменения конкретного отображения Модели. + +**Представление (The View)** +Отображает различные Представления слоя Модели и предоставляет способ вызывать изменения в состоянии модели. + +!*[The MVC pattern]*(https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/image--006.jpg) + +
+ ### Пример Многоуровневой Архитектуры - + #### Модель -Продолжая предыдущий пример, мы упомянули, что различные задачи должны быть разделенны. -Для этого все слои должны быть идентифицированны в нашем оригинальном запутанном коде. -На протяжении всего этого процесса мы должны уделять особое внимание коду, соотвествующему уровню Модели, который будет ядром приложения: +Продолжая предыдущий пример, мы упомянули, что различные задачи должны быть разделены. Для этого все слои должны быть идентифицированы в нашем оригинальном запутанном коде. На протяжении всего этого процесса мы должны уделять особое внимание коду, соответствующему уровню **Модели**, который будет ядром приложения: + +
```php + Слой Модели теперь определяется классом `Post` и классом `PostRepository`. -Класс `Post` представляет сообщение в блоге, а класс `PostRepository` представляет всю коллеркцию доступных -сообщений в блоге. Кроме того, еще один слой - тот, который координирует поведения Домене Модели - необходим внутри модели. -Рассмотрим Прикладной слой (Application layer): +Класс `Post` представляет сообщение в блоге, а класс `PostRepository` представляет всю коллекцию доступных сообщений в блоге. Кроме того, есть еще один слой - тот, который координирует необходимое поведение Модели Домена. +Рассмотрим **Прикладной** слой (Application layer): + +
```php + +Класс `PostService` - это, так называемая Прикладная служба (Служба Приложения, Application Service), и её целью является организация поведения Домена. Ни один другой тип объекта не может напрямую взаимодействовать с внутренними слоями Модели. + +
#### Представление (View) -Представление - это слой, который может отправлять и получать сообщения со слоя модели и/или со слоя конетроллера. Его основная цель - -отобразить модель пользователю на уровне пользовательского интерфейса, а также обновлять это отображение при обновлении модели. -В общем случае, слой Представления получает объект (часто это Объект Передачи Данных (DataTransferObject)), тем самым собирая -всю необходимую информацию для успешного отображения. Для PHP есть несколько шаблонизаторов, которые могут помочь -отделить представление Модели от самой Модели и от Контроллера. Самым популярным, на текущий момент, является Twig. +Представление - это слой, который может отправлять и получать сообщения со слоя модели и/или со слоя контроллера. Его основная цель - отобразить модель пользователю на уровне пользовательского интерфейса, а также обновлять это отображение при обновлении модели. + +В общем случае, слой Представления получает объект (часто это Объект Передачи Данных (DataTransferObject)), тем самым собирая всю необходимую информацию для успешного отображения. Для PHP есть несколько шаблонизаторов, которые могут помочь +отделить Представление Модели от самой Модели и от Контроллера. Самым популярным, на текущий момент, является Twig. Посмотрим как будет выглядеть слой Представления с Twig. ->**DTO вместо экземпляров Модели?** +
+ +> **DTO вместо экземпляров Модели?** > > Это старый холивар. Зачем создавать DTO вместо того чтобы передать экземпляр класса Модели? -> Основная причина и самый локаничный ответ, знакомое нам Разделение Ответственности ->(Separation of Concerns). Возможность Представления просматривать и использовать поступающие объекты приводит к тесной ->связи между слоем Представления и слоем Модели. Фактически, изменение в уровне Модели может нарушить все представления, ->которые используют измененные экземпляры модели. +> Основная причина и самый лаконичный ответ, знакомое нам **Разделение Ответственности** (Separation of Concerns). Возможность Представления просматривать и использовать поступающие объекты приводит к тесной связи между слоем Представления и слоем Модели. Фактически, изменение в уровне Модели может нарушить все представления, которые используют измененные экземпляры модели. + +
+ ```twig {% extends "base.html.twig" %} {% block content %} @@ -330,27 +344,25 @@ class PostService {% endblock %} ``` -В большистве случаев, когда Модель инициирует изменение состояния, она также уведомляет связанные -Представления, чтобы обновить пользовательский интерфейс. В типичном веб-сценарии синхронизация между Моделью и -его представлениями может быть немного сложной из-за природы клиент-серверной архитектуры. -В таких средах, обчыно требуется использовать JavaScript, чтобы поддерживать эту синхронизацию. -По этой причени, JavaScript MVC фреймворки, о которых говорится ниже, стали широко популярными в последние годы: - - - AngularJS - - Ember.js - - Marionette.js - - React - - #### Контроллер (Controller) - -Слой Контроллера отвечает за организацию и взаимодествие слоёв Модели и Представления. -Он получает сообщения от слоя Представления и запускает поведения модели для выполнения -желаемого действия. Кроме того, он отправляет сообщения в Представления для отрисовки отображения Модели. -Обе операции выполняются благодаря прикладному уровню, который отвечает за организацию, взаимодействие и инкапсуляцию -поведения Домена. - -В терминах веб-приложения на PHP, Контроллер, обычно, охватывает набор классов, которые для достижения -своих целей "Общаются на HTTP". Другими словами они получают HTTP запрос и HTTP запросом дают ответ. +
+ +В большинстве случаев, когда Модель инициирует изменение состояния, она также уведомляет связанные Представления, чтобы обновить пользовательский интерфейс. В типичном веб-сценарии синхронизация между Моделью и её представлениями может быть немного сложной из-за природы клиент-серверной архитектуры. В таких средах, обычно требуется использовать JavaScript, чтобы поддерживать эту синхронизацию. +По этой причине, JavaScript MVC фреймворки, о которых говорится ниже, стали широко популярными в последние годы: +- AngularJS +- Ember.js +- Marionette.js +- React + +
+ +#### Контроллер (Controller) + +Слой Контроллера отвечает за организацию и взаимодействия слоёв Модели и Представления. Он получает сообщения от слоя Представления и запускает поведение Модели для выполнения желаемого действия. +Кроме того, он отправляет сообщения в Представления для отрисовки отображения Модели. Обе операции выполняются благодаря прикладному уровню, который отвечает за организацию, взаимодействие и инкапсуляцию поведения Домена. + +В терминах веб-приложения на PHP, Контроллер, обычно, охватывает набор классов, которые для достижения своих целей "Общаются на HTTP". Другими словами они получают HTTP Request и дают ответ в виде HTTP Response. + +
```php render('posts/update-result.html.twig'); } } ``` -### Инверсия зависемостей. Гексогональная архитектура. +
-Следую основному правилу Многоуровневой архитектуры, существует риск инкапсулирования инфраструктурных проблем в реализацию -Доменных интерфейсов. +### Инверсия зависимостей. Гексагональная архитектура. -Например, класс PostRepository из предыдущего MVC примера, должен быть помещен Домен предметной области. -Однако размещение инфраструктурных деталей прямо в теле нашего Домена нарушает Разделение Ответственности -(Domain Separation). Это создает проблемы; трудно избежать нарушения основных правил многоуровневой архитектуры, что приводит -к написанию кода которых тяжело поддается тестированию, т.к. уровень Домена осведомлен о технических реализациях. +Следую основному правилу Многоуровневой архитектуры, существует риск инкапсулирования инфраструктурных проблем в реализацию Доменных интерфейсов. +Например, класс PostRepository из предыдущего MVC примера, был помещен в Домен предметной области. Однако размещение инфраструктурных деталей прямо в теле нашего Домена нарушает Разделение Ответственности (Domain Separation). +Это создает проблемы. Трудно избежать нарушения основных правил многоуровневой архитектуры, что приводит к написанию кода который тяжело поддается тестированию, т.к. уровень Домена осведомлен о технических реализациях. + +
#### Принцип Инверсии Зависимостей (The Dependency Inversion Principle (DIP)) -Как мы можем это исправить? Поскольку уровень Доменной Модели связан с конкретной реализаций -инфраструктуры, можно применить принцип инверсии зависимости (DIP), переместив Инфраструктурный слой выше -всех остальных слоёв. - ->**Принцип Инверсии Зависимостей** ->Модули более выского уровня не должны зависеть от нижележащий уровней. Оба должны связываться через абстракции. ->Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. _Robert C. Martin_ - -При использовании принципа Инверсии Зависемостей, схема архитектуры меняется, и уровень инфраструктуры, который -можно назвать модулем низкого уровня, теперь зависит от пользовательского интерфейса, Прикладного слоя, и Доменного слоя, -которые являются высокоуровневыми модулями. Зависимость была инвертирована. - -Но что такое Гексагональная Архитектура и как она вписывается во все это? -Гексогональная Архитектура (так же именуемая как Порты и Адаптеры) была определена Алистером -Кокберном в его книге _"Hexagonal Architecture"_. Он изображает приложение как шестиугольник, -где каждая строна представляет Портс одним или неколькими Адаптерами. -Порт - это коннектор с подключаемым Адаптером, который преоброзует внешний вход во что-то, что -может понять внутренее приложение. С точки зрения DIP, Порт был бы модулем выского уровня, а Адаптер -был бы модулем низлежащего уровня. Кроме того, если приложению необходимо отправить сообщение во внешний сервис, оно -также будет использовать порт с адаптером для преобразования сообщения в язык понятный внешнему сервису и последующей отправкой сообщения. -По этой причине Гексагональная архитектура воспитывает концепцию симметрии в приложении, а также -является основной причиной, по которой меняется схема архитектуры. -Её часто представляют в виде шестиугольника, потому что больше не имеет смысла говорить о верхнем или нижнем слое. -Вместо этого, в Гексагональной Архитектуре говорится в внешнем слое и внутренем. +Как мы можем это исправить? Поскольку уровень Доменной Модели связан с конкретной реализаций инфраструктуры (PostRepository), можно применить принцип инверсии зависимости (DIP), переместив **Инфраструктурный** слой выше всех остальных слоёв. + +
+ +> **Принцип Инверсии Зависимостей** +> Модули более высокого уровня не должны зависеть от нижележащий уровней. Оба должны связываться через абстракции. +> Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. _Robert C. Martin_ + +
+ +При использовании принципа Инверсии Зависимостей, схема архитектуры меняется, и уровень инфраструктуры, который можно назвать модулем низкого уровня, теперь зависит от пользовательского интерфейса, Прикладного слоя, и Доменного слоя, которые являются высокоуровневыми модулями. Зависимость была инвертирована. + +Но что такое Гексагональная Архитектура и как она вписывается во все это? Гексагональная Архитектура (так же именуемая как Порты и Адаптеры) была определена Алистером Кокберном в его книге _"Hexagonal Architecture"_. +Он изображает приложение как шестиугольник, где каждая сторона представляет Порт с одним или несколькими Адаптерами. +**Порт** - это коннектор с подключаемым **Адаптером**, который преобразует внешний вход во что-то, что может понять внутреннее приложение. С точки зрения DIP, **Порт** был бы модулем высокого уровня, а **Адаптер** был бы модулем нижележащего уровня. Кроме того, если приложению необходимо отправить сообщение во внешний сервис, оно также будет использовать **Порт** с **Адаптером** для преобразования сообщения в язык понятный внешнему сервису и последующей отправкой сообщения. +По этой причине **Гексагональная** архитектура воспитывает концепцию симметрии в приложении, а также является основной причиной, по которой меняется схема архитектуры. +Её часто представляют в виде шестиугольника, потому что больше не имеет смысла говорить о верхнем или нижнем слое. Вместо этого, в Гексагональной Архитектуре говорится о внешнем слое и внутреннем. + +
#### Применение Гексогональной Архитектуры -Продолжим рассматривать приме с блогом. Первая концепция, которая нам нужна, это Порт, через который -внешний мир может общаться с приложением. Для этого случая мы будем использовать HTTP-порт и соответствующий ему Адаптер. -Внешним будет порт для отправки сообщений в приложение. В пример блога использовалась база данных для -хранения постов блога, поэтому нам так же необходим Порт для извлечения постов из базы данных: +Продолжим рассматривать пример с блогом. Первая концепция, которая нам нужна, это **Порт**, через который внешний мир может общаться с приложением. Для этого случая мы будем использовать HTTP-порт и соответствующий ему Адаптер. +Внешним будет порт для отправки сообщений в приложение. В примере блога использовалась база данных для хранения постов блога, поэтому нам так же необходим **Порт** для извлечения постов из базы данных: + +
+ ```php + +Этот **интерфейс** представляет собой **Порт**, через который приложение будет получать информацию о постах блога, и он будет расположен на уровне **Доменного** слоя. Теперь нужен **Адаптер** для этого **Порта**. +**Адаптер** отвечает за определение способа извлечения сообщений из блога с использованием определенной технологии. + +
+ ```php + +После того, как мы определили Порт и его Адаптер, последним шагом будет рефакторинг класса PostService, чтобы он использовал новый механизм. Это может быть легко достигнуто с помощью Иньекции Зависимостей (Dependency Injection): + +
+ ```php postRepository = $postRepository; } + public function createPost($title, $content) { $post = Post::writeNewFrom($title, $content); $this->postRepository->add($post); + return $post; } } ``` -Это простой пример Гегсогональной архитектуры. Это гибкая архитектура, которая способствует Разделению -Ответственности, как в Многоуровневой Архитектуре. Это также способствует симметрии, благодаря -наличию внутренней части приложения, которая связывается с внешним слоем через Порты. -Отныне, это будет основополгающая архитектура, используемая для построения и объяснения CQRS и -Event Sourcing. +
+ +Это простой пример Гегсагональной архитектуры. Это гибкая архитектура, которая способствует Разделению Ответственности, как в Многоуровневой Архитектуре. Это также способствует симметрии, благодаря наличию внутренней части приложения, которая связывается с внешним слоем через Порты. Отныне, это будет основополагающая архитектура, используемая для построения и объяснения CQRS и Event Sourcing. Более детальный разбор этой архитектуры вы можете найти в главе _"Приложение. Гексагональная архитектура в PHP"_. -Для более подробного примера вам следует перейти к _Главе 11 "Приложение"_, в которой объясняются такие сложные темы, -как транзакционность и другие сквозные вопросы. +Для более подробного примера вам следует перейти к _Главе 11 "Приложение"_, в которой объясняются такие сложные темы, как транзакционность и другие сквозные вопросы. + +
### Command Query Responsibility Segregation (CQRS) -Гексагональная архитектура - это хорошая основопологающая архитектура, но она имеет некоторые ограничения. -Например, сложные пользовательские интерфейсы могут требовать Агрегированной информации, отображаемой -в различных формах (Глава 8, Агрегаты), или они могут требовать данных, полученных из нескольких агрегатов. -И в этом сценарии мы могли бы реализовать множество методов поиска в Репозиториях (возможно, столько же, -сколько представлений пользовательского интерфейса есть в приложении). Или, может быть, мы решим -перенести эту сложность в Службу Приложений (Application Service), используя сложные структуры для -сбора и объедининя данных из различных Агрегатов. Пример: +Гексагональная архитектура - это хорошая основополагающая архитектура, но она имеет некоторые ограничения. Например, сложные пользовательские интерфейсы могут требовать Агрегированной информации, отображаемой в различных формах (Глава 8, Агрегаты), или они могут требовать данных, полученных из нескольких агрегатов. И в этом сценарии мы могли бы реализовать множество методов поиска в различных Репозиториях (возможно, столько же, сколько есть представлений пользовательского интерфейса в приложении). Или, может быть, мы решим перенести эту сложность в Службу Приложений (Application Service), используя сложные структуры для сбора и объединения данных из различных Агрегатов. Пример: + +
```php **Command Query Separation (CQS)** ->Задание вопроса не должно менять ответ - Бертран Мейер. ->Этот принцип разработки гласит, что каждый метод должен быть либо командой, выполняющей действие, либо ->запросом, возвращающим данные вызывающей стороне, но не обоими сразу. Wikipedia - -CQRS стремится к еще более агрессивному Разделению Проблем, деля модель на две части: - - Модель Записи (The Write Model): также известная как Командная модель (Command Model), она выполняет запись -и несет ответственность за истинное поведение домена. - - Модель Чтения (The Read Model): она берет на себя ответственность за чтение в приложении и расматривается как - нечто что должно выходить за пределы предметной области. - -Каждый раз, когда кто-то запускает команду для модели записи, выполняется запись в нужное хранилище данных. -Кроме того, она запускает обновление Модели Чтения, чтобы в ней отобразились последние изменения. - -Это строгое разделение вызывает еще одно проблему: Согласованность в Конечном Итоге (Eventual Consistency). -Согласованность модели чтения теперь зависит от команд, выполняемых Моделью Записи. Другими словами, модель чтения -в конечном итоге непротиворечива. То есть каждый раз, когда Модель Записи выполняет команду, -она запускает процесс, который будет отвечать за обновление модели чтения в соответствии с последними -обновлениями модели записи. Есть некоторый лаг во времени, когда пользовательский интерфейс может представить -устаревшую информацию пользователю. В веб-сценарии это происходит часто, поскольку мы несколько ограничены текущими -технологиями. - -Подумайте о системе кэширования стоящим перед веб-приложением. Каждый раз, когда база данных обновляется новой информацией, -данные на уровен кэша потенциально могут быть устаревшими, поэтому каждый раз, когда она обновляется, -должен быть процесс, который обновляет систему кэша. Системы кэширования в конечном итоге становятся согласованными. - -Такие процессы, в терминалогии CQRS, называются Проекции Модели Записи (Write Model Projections) -или просто Проекции. Мы проецируем Модели Записи на Модель Чтения. Этот процесс может быть синхронным или -асинхронным, в зависимости от ваших потребностей, и этом может быть сделанно благодаря полезному тактическому -шаблону проектирования - Глава "События Домена" - который будет подробно объяснен позже в книге. -Основной проекций Модели Записи является сбор всех опубликованных событий домена и обновление модели чтения всей информацией, -поступившей из событий. +
+ +Когда этими конструкциями злоупотребляют, построение пользовательских представлений может стать действительно болезненным. Мы должны принять компромисс между тем, чтобы заставлять Службу Приложений возвращать Доменную Модель или возвращать некие DTO`s. В последнем варианте (DTO), мы избегаем тесной связи между +Доменной Моделью и кодом Инфраструктуры (веб-контроллеры, cli контроллеры и т.д.). + +К счастью, есть другой подход. Если проблема заключается в множественных и разрозненных представлениях, то мы можем исключить их из Доменной Модели и начать рассматривать их как чисто инфраструктурную проблему. Эта опция базируется на принципе разработки, Разделение Команд и Запросов (CQS, Command Query Separation). Этот принцип был определен Бертраном Мейером (Bertrand Meyer), и, в свою очередь, он породил новый архитектурный паттерн под названием +"Разделение Ответственности на Запросы и Команды" (CQRS, Command Query Responsibility Segregation), как это определенно Грегом Янгом (Greg Young). + +
+ +> **Command Query Separation (CQS)** +> Задание вопроса не должно менять ответ - Бертран Мейер. +> Этот принцип разработки гласит, что каждый метод должен быть либо командой, выполняющей действие, либо запросом, возвращающим данные вызывающей стороне, но не обоими сразу. Wikipedia + +
+ +**CQRS** стремится к еще более агрессивному Разделению Проблем, деля модель на две части: +- Модель Записи (The Write Model): также известная как Командная модель (Command Model), она выполняет запись и несет ответственность за истинное поведение домена. +- Модель Чтения (The Read Model): она берет на себя ответственность за чтение в приложении и рассматривается как нечто что должно выходить за пределы предметной области. + +
+ +Каждый раз, когда кто-то запускает команду для модели записи, выполняется запись в нужное хранилище данных. Кроме того, она запускает обновление Модели Чтения, чтобы в ней отобразились последние изменения. + +Это строгое разделение вызывает еще одно проблему - Согласованность в Конечном Итоге (Eventual Consistency). +Согласованность модели чтения теперь зависит от команд, выполняемых Моделью Записи. Другими словами, модель чтения в конечном итоге непротиворечива. То есть каждый раз, когда Модель Записи выполняет команду, она запускает процесс, который будет отвечать за обновление модели чтения в соответствии с последними обновлениями модели записи. Есть некоторый лаг во времени, когда пользовательский интерфейс может представить устаревшую информацию пользователю. В веб-сценарии это происходит часто, поскольку мы несколько ограничены текущими технологиями. + +Подумайте о системе кэширования стоящим перед веб-приложением. Каждый раз, когда база данных обновляется новой информацией, данные на уровне кэша потенциально могут быть устаревшими, поэтому каждый раз, когда она обновляется, должен быть процесс, который обновляет систему кэша. Системы кэширования в конечном итоге становятся согласованными. + +Такие процессы, в терминалогии CQRS, называются Проекции Модели Записи (Write Model Projections) или просто Проекции. Мы проецируем Модели Записи на Модель Чтения. Этот процесс может быть синхронным или асинхронным, в зависимости от ваших потребностей, и этом может быть сделано благодаря полезному тактическому шаблону проектирования - Глава "События Домена" - который будет подробно объяснен позже в книге. + +Основной проекций Модели Записи является сбор всех опубликованных событий домена и обновление модели чтения всей информацией, поступившей из событий. + +
#### Модель записи -Модель записи является истиным владельцем поведения Домена. Продолжая -наш пример, интерфейс репозитория будет урощен до следующего: + +Модель записи является истинным владельцем поведения Домена. Продолжая +наш пример, интерфейс репозитория будет упрощен до следующего: + +
+ ```php + +Теперь `PostRepository` освобожден от всех задач чтения, кроме одной: функция byId, которая отвечает за загрузку Агрегата по его ID, для дальнейшей работы с ним. + +Так же будут удалены все методы запросов (query) из модели `Post`, оставив только методы команд. Это приводит к тому, что мы избавляемся от всех методов получения данных и любых других методов, предоставляющих информацию о Агрегате Post. Вместо этого будут опубликованы Доменные События, чтобы запустить проекцию Модели Записи использую подписку на них (события): + +
```php id = $id; @@ -681,10 +692,14 @@ class Post extends AggregateRoot } ``` +
+ Все действия, которые инициируют изменение состояния, реализуются -через события Домена. Для каждого опубликованного Доменого События существует +через события Домена. Для каждого опубликованного Доменного События существует соотвествующий метод apply, отвечающий за отражение изменения состояния: +
+ ```php + #### Модель чтения -Модель Чтения, так же известная как модель запросов (Query Model), является +Модель Чтения, так же известная как модель запросов (Query Model), является денормализованной, в интересах Домена, моделью данных. -Фактически, в CQRS все задачи чтения считаются процессами отчетности в -инфраструктурной задаче. Как правило, при использовании CQRS Модель Чтения зависит от -потребностей пользовательского интерфейса и сложности представлений, состовляющих -пользовательский интерфейс. В ситуации, когда модель чтения определяется в терминах реляционных баз данных, -простейшим подходом было бы установить взаимно-однозначные отношения между -таблицами базы данных и представлениями пользовательского интерфейса. Эти -таблицы базы данных и представления пользовательского интерфейса будут -обновлены с использованием проекций Модели Записи, инициированных событиями домена, - опубликованными стороной записи: - +Фактически, в CQRS все задачи чтения считаются процессами отчетности в инфраструктурной задаче. Как правило, при использовании CQRS Модель Чтения зависит от потребностей пользовательского интерфейса и сложности представлений, состовляющих пользовательский интерфейс. В ситуации, когда модель чтения определяется в терминах реляционных баз данных, простейшим подходом было бы установить взаимно-однозначные отношения между таблицами базы данных и представлениями пользовательского интерфейса. +Эти таблицы базы данных и представления пользовательского интерфейса будут +обновлены с использованием проекций Модели Записи, инициированных событиями домена, опубликованными стороной записи: + +
+ ```sql -- Определение представляния UI для поста с его комментариями CREATE TABLE single_post_with_comments ( @@ -762,12 +775,14 @@ INSERT INTO single_post_with_comments VALUES SELECT * FROM single_post_with_comments WHERE post_id = 1; ``` -Важной особенностью этого архитектурного стиля является то, что - Модель Чтения должна быть полностью одноразовой, поскольку истинное состояние приложения -определяется Моделью Записи. Это означает что Модель Чтения может быть удалено -и пересоздано при необходимости использую проекции Модели Записи. +
+ +Важной особенностью этого архитектурного стиля является то, что Модель Чтения должна быть полностью одноразовой, поскольку истинное состояние приложения определяется Моделью Записи. Это означает что Модель Чтения может быть удалена и пересоздана при необходимости используя проекцию Модели Записи. + +Ниже мы можем увидеть некоторые примеры возможных представлений в приложении блога: + +
-Ниже мы можеи увидеть некоторые примеры возможных представлений в преложении блога: ```sql SELECT * FROM posts_grouped_by_month_and_year @@ -782,13 +797,15 @@ posts_by_author WHERE author_id = 1; ``` -Важно отметить, что CQRS не ограничевается реализацией модели для реляционной +
+ +Важно отметить, что CQRS не ограничивается реализацией модели для реляционной базы данных. Это зависит исключительно от потребностей создаваемого приложения. -Это может быть реляционная база данных, документно-ориентированная база данных, хранилище типа ключ-значение, -или что-либо, что лучше всего соответствует потребностям вашего приложения. После приложения для публикации постов в блоге мы -будет использовать `Elasticsearch` (базу данных, ориентированную на документы) - - для реализации модели чтения. - +Это может быть реляционная база данных, документно-ориентированная база данных, хранилище типа ключ-значение, или что-либо, что лучше всего соответствует потребностям вашего приложения. +Далее в приложении для публикации постов в блоге мы будет использовать `Elasticsearch` (базу данных, ориентированную на документы) для реализации модели чтения. + +
+ ```php -Этот код показывает, что Модель Чтения на самом деле не нуждается в -объектно-реляционном преобразователе, посколько это может быть излишним. -Однако Модель Записи может выиграть от использования объектно-реляционого -преобразования, поскольку это позволит вам организовать и структурировать Модель Чтения -в соответствии с потребностями приложения. +Код Модели Чтения был существенно упрощен до одного запроса к индексу Elasticsearch. + +Этот код показывает, что Модель Чтения на самом деле не нуждается в объектно-реляционном преобразователе, посколько это может быть излишним. +Однако Модель Записи может выиграть от использования объектно-реляционного +преобразования, поскольку это позволит вам организовать и структурировать Модель Чтения в соответствии с потребностями приложения. + +
#### Синхронизация Модели Записи с Моделью Чтения -Здесь начинается сложная часть. Как мы синхронизируем Модель Чтения с +Здесь начинается сложная часть. Как мы синхронизируем Модель Чтения с Моделью Записи? Мы уже говорили, что сделаем это с помощью Событий Домена, -захваченных в тразакции Модели Записи. Для каждого типа захваченного События Домена -будет выполнена соответствующая проекция. Таким образом, будет установлено +захваченных в транзакции Модели Записи. Для каждого типа захваченного События Домена будет выполнена соответствующая проекция. Таким образом, будет установлено взаимно-однозначное отношение между Событиями Домена и проекциями. Давайте посмотрим на пример настройки проекций, для лучшего понимания идеи. Прежде всего, нам нужно определить каркас для проекций: +
+ ```php + Определение проекции `Elasticsearch` для события `PostWasCreated` достаточно просто: +
+ ```php + Реализация Проекции является своего рода специализированным слушателем -Событий Домена. Основное различие между этой Проекций и слушателем Доменых Событий по умолчанию в том, -что Проекция реагирует на группу Доменных Событий, а не только на одно. +Событий Домена. Основное различие между этой Проекций и слушателем Доменных Событий по умолчанию в том, что Проекция реагирует на группу Доменных Событий, а не только на одно. + +
```php projections[get_class($event)])) { @@ -905,6 +933,8 @@ class Projector } ``` +
+ Следующий код показывает, как будет выглядеть поток между проекцией и событиями: ```php @@ -936,11 +966,12 @@ $events = [ $projector->project($event); ``` -Этот код является своего рода синхронным, но процесс может быть и -асинхронным, если это необходимо. И вы могли бы информировать своих клиентов об этих -не синхроннизированных данных, разместив несколько предупреждений в слое представления. +
+ +Этот код является своего рода синхронным, но процесс может быть и асинхронным, если это необходимо. И вы могли бы информировать своих клиентов об этих +не синхронизированных данных, разместив несколько предупреждений в слое представления. -В следующем примере мы будем использовать PHP-расширение amqplib в сочетании с +В следующем примере мы будем использовать PHP-расширение amqplib в сочетании с ReactPHP: ```php @@ -975,6 +1006,8 @@ $events = [ $projector->project($event); ``` +
+ Чтобы это работало, нам нужен асинхронная проекция. Вот наивная реализация этого: ```php @@ -1006,7 +1039,9 @@ class AsyncProjector } ``` -И потребитель событий с использованием брокера RabbitMQ будет +
+ +И потребитель событий с использованием брокера RabbitMQ будет выглядеть примерно так: ```php @@ -1056,7 +1091,9 @@ $consumer->on( $loop->run(); ``` -Отсюда все становится проще. Мы заставляем все репозитории использовать +
+ +Отсюда все становится проще. Мы заставляем все Репозиторий использовать экземпляр проекции, а затем так же запускаем процесс проецирования: ```php @@ -1065,6 +1102,7 @@ class DoctrinePostRepository implements PostRepository { private $em; private $projector; + public function __construct(EntityManager $em, Projector $projector) { $this->em = $em; @@ -1092,49 +1130,50 @@ class DoctrinePostRepository implements PostRepository } ``` -Экземпляр `Post` и записанные события запускаются и сохраняются в одной транзакции. Это гарантирует, что никакие -события не будут потеряны, так как мы спроецируем их на модель чтения, если транзакция прошла успешно. +
+ +Экземпляр `Post` и записанные события запускаются и сохраняются в одной транзакции. Это гарантирует, что никакие события не будут потеряны, так как мы спроецируем их на модель чтения, если транзакция прошла успешно. В результате между Моделью Записи и Моделью чтения не будет никаких несоответствий. ->**ORM или без ORM** +
+ +> **ORM или без ORM** > ->Один из наиболее распространных вопросов при реализации CQRS - действительно ли нужен объектно-реляционный маппер -> (ORM)? Мы твердо верим, что использование ORM для модели записи прекрасно и дает все преимущества использования ->инструмента, который поможет нам сэкономить много работы в случае использования реляционной базы данных. ->Но мы не должны забывать, что нам все еще нужно сохранять и извлекать состояние Модели Записи используя ->реляционную базу данных +> Один из наиболее распространных вопросов при реализации CQRS - действительно ли нужен объектно-реляционный маппер (ORM)? +> Мы твердо верим, что использование ORM для модели записи прекрасно и дает все преимущества использования инструмента, который поможет нам сэкономить много работы в случае использования реляционной базы данных. +> Но мы не должны забывать, что нам все еще нужно сохранять и извлекать состояние Модели Записи используя реляционную базу данных. + +
### Event Sourcing -CQRS - это мощная и гибкая архитектура. Это дает дополнительное преимущство в отношении сбора и -сохранения Событий Домена (которые произошли во время выполнения операции Агрегата), -предоставляя вам высокую степень детализации того, что происходит в вашем Домене. События в Домене -являютяс одни из ключевых тактических паттернов из-за их значения в Домене, поскольку они описывают -прошлые события. ->**Будьте осторожны с записью слишком большого количества событий** ->Постоянно растущее число событий - это звоночек. Это может выявить пристрастие к записи событий ->Домена, что, скорее всего, стимулируется бизнесом. +CQRS - это мощная и гибкая архитектура. Это дает дополнительное преимущество в отношении сбора и сохранения Событий Домена (которые произошли во время выполнения операции Агрегата), предоставляя вам высокую степень детализации того, что происходит в вашем Домене. События в Домене являются одни из ключевых тактических паттернов из-за их значения в Домене, поскольку они описывают прошлые события. + +
+ +> **Будьте осторожны с записью слишком большого количества событий** +> +> Постоянно растущее число событий - это звоночек. Это может выявить пристрастие к записи событий Домена, что, скорее всего, стимулируется бизнесом. + +
Используя CQRS, мы смогли бы записать все соответствующие события, которые произошли на уровне Домена. -Состояние доменной модели может быть представлено путем воспроизведения событий домена, которые мы записали -ранее. Нам просто нужен инструмент для последовательного хранения всех этих событий. Нам нужно -хранилище событий. +Состояние доменной модели может быть представлено путем воспроизведения событий домена, которые мы записали ранее. Нам просто нужен инструмент для последовательного хранения всех этих событий. Нам нужно хранилище событий. + +
+ +> Основная идея, лежащая в основе Event Sourcing, заключается в отображении состояния Агрегатов в виде линейной последовательности событий. + +
+ +С помощью CQRS мы частично достигли следующего: сущность Post изменяет свое состояние с помощью событий Домена, но она сохраняется, как уже объяснялось, тем самым сопоставляя объект с строкой в таблицей базы данных. ->Основная идея, лежащая в основе Event Sourcing, заключается в отображении состояния Агрегатов в виде линейной ->последовательности событий. +`Event Sourcing` делает еще один шаг вперед. Сейчас мы использовали одну таблицу базы данных для хранения состояния всех постов блога, другую для хранения состояния всех комментариев постав блога и т.д., использование `Event Sourcing` мы можем использовать одну таблицу базы данных, в которой будут храниться все События Домена, опубликованные всеми агрегатами в Модели Домена. Да, вы прочитали это правильно. Единая таблица базы данных. -С помощью CQRS мы частично достигли следующего: сущность Post изменяет свое состояние с помощью -событий Домена, но она сохраняется, как уже объяснялось, тем самым сопоставляя объект с строкой в таблицей базы данных. +Следуя этому подходу, инструменты, подобные объектно-реляционному мапперу, больше не нужны. Единственным необходимым инструментом был бы простой уровень абстракции базы данных, к которому можно добавлять события: -`Event Sourcing` делает еще один шаг вперед. Сейчас мы использовали одну таблицу базы данных для -хранения состояния всех постов блога, другую для хранения состояния всех комментариев постав блога и т.д., -использование `Event Sourcing` мы можем использовать одну таблицу базы данных, в которой будут -храниться все События Домена, опубликованные всеми агрегатами в Модели Домена. Да, вы прочитали -это правильно. Единая таблица базы данных. +
-Следуя этому подходу, инструменты, подобные объектно-реляционному маперу, больше не нужны. -Единственным необходимым инструментом был бы простой уровень абстракции базы данных, к которому можно -добавлять события: ```php interface EventSourcedAggregateRoot { @@ -1149,15 +1188,18 @@ class Post extends AggregateRoot implements EventSourcedAggregateRoot foreach ($events as $event) { $post->applyThat($event); } + return $post; } } ``` -Теперь у Агрегата `Post` есть метод, который при заданном наборе событий (другими словами, потоке событий) -может пошагово воспроизводить состояние до тех пор, пока оно не достигнет текущего. -Следующим шагом будет создание адаптера порта для `PostRepository`, который будет извлекать все -опубликованные события из Агрегата `Post` и добавлять их в хранилище данных, куда добавляются -все событий. Это то что мы называем хранищем событий (event store). + +
+ +Теперь у Агрегата `Post` есть метод, который при заданном наборе событий (другими словами, потоке событий) может пошагово воспроизводить состояние до тех пор, пока оно не достигнет текущего. +Следующим шагом будет создание адаптера порта для `PostRepository`, который будет извлекать все опубликованные события из Агрегата `Post` и добавлять их в хранилище данных, куда добавляются все событий. Это то что мы называем хранилищем событий (event store). + +
```php class EventStorePostRepository implements PostRepository @@ -1184,10 +1226,13 @@ class EventStorePostRepository implements PostRepository } ``` -Так выглядит реализация `PostRepository`, когда мы используем хранилище событий для сохранения -всех событий, опубликованных Агрегатом `Post`. Теперь нам нужен способ восстановать Агрегат из его истории событий. -Метод востановления, реализуется Агрегатом `Post` и используется для восстановления состояния сообщения в блоге из +
+ +Так выглядит реализация `PostRepository`, когда мы используем хранилище событий для сохранения всех событий, опубликованных Агрегатом `Post`. Теперь нам нужен способ восстановить Агрегат из его истории событий. Метод восстановления, реализуется Агрегатом `Post` и используется для восстановления состояния сообщения в блоге из инициированных событий: + +
+ ```php class EventStorePostRepository implements PostRepository { @@ -1199,12 +1244,16 @@ class EventStorePostRepository implements PostRepository } } ``` -Хранилище событий это рабочая лошадка, которая несет на себе всю ответственность за сохранение -и восстановление потоков событий. Его публичный API состоит из двух простых методов: -`append` и `getEventsFrom`. Первый добавляет поток событий в хранилище событий, а второй получает потоки -событий, чтобы запусть построение Агрегата. -Мы могли бы использовать key-value хранилище для реализаци хранения всех событий: +
+ +Хранилище событий это рабочая лошадка, которая несет на себе всю ответственность за сохранение и восстановление потоков событий. Его публичный API состоит из двух простых методов: +`append` и `getEventsFrom`. Первый добавляет поток событий в хранилище событий, а второй получает потоки событий, чтобы запустить построение Агрегата. + +Мы могли бы использовать key-value хранилище для реализации хранения всех событий: + +
+ ```php class EventStore { @@ -1256,20 +1305,20 @@ class EventStore } } ``` + +
+ Эта реализация хранилища событий основана на Redis, широко используемом key-value хранилище. -События добавляются в список с использованием префиксных событий: помимо этого, перед сохранением событий -мы извлекаем некоторые метаданные, такие как класс события или дата создания, это может пригодиться позже. +События добавляются в список с использованием префиксных событий: помимо этого, перед сохранением событий мы извлекаем некоторые метаданные, такие как класс события или дата создания, это может пригодиться позже. + +Очевидно, что с точки зрения производительности, Агрегату очень затратно обходить всю историю событий, чтобы постоянно находиться в актуальном состоянии. Это особенно заметно, когда поток событий содержи сотни или тысячи событий. -Очевидно, что с точки зрения производительности, Агрегату очень затратно обходить всю историю событий, чтобы постоянно -находиться в актуальном состоянии. Это особенно заметно, когда поток событий содержи сотни или тысячи событий. +Лучший способ преодолеть эту ситуацию - это сделать снимок Агрегата (snapshot) и воспроизвести только те события в потоке событий, которые произошли после создания снимка. +Снимок - это просто сериализованная версия состояния Агрегата, преимуществено основанный на времени. При одном подходе, снимок делается каждые n запущенный событий. При другом подходе снимок делается каждые n секунд. -Лучший способ преодолеть эту ситуацию - это сделать снимок Агрегата (snapshot) и воспроизвести только -те события в потоке событий, которые произошли после создания снимка. -Снимок - это просто сериализованная версия состояния Агрегата, преимуществено основанный на времени. При одном подходе, снимок делается -каждые n запущенный событий. При другом подходе снимок делается каждые n секунд. +Следуя нашему примеру, мы будем использовать первый способ создания снимков. В метаданных события мы храним даполнительное поле, версию, с которой мы начнем воспроизводить историю Агрегата. -Следуя нашему примеру, мы будем использовать первый способ создания снимков. В метаданных события мы -храним даполнительное поле, версию, с которой мы начнем вопроизводить историю Агрегата. +
```php class SnapshotRepository @@ -1312,8 +1361,13 @@ class SnapshotRepository } } ``` -Теперь нам необходимо провести рефакторинг класса `EventStore`, чтобы он начал использовать -`SnapshotRepository` для загрузки Агрегата с допустимыми временными затратами. + +
+ +Теперь нам необходимо провести рефакторинг класса `EventStore`, чтобы он начал использовать `SnapshotRepository` для загрузки Агрегата с допустимыми временными затратами. + +
+ ```php class EventStorePostRepository implements PostRepository { @@ -1335,9 +1389,13 @@ class EventStorePostRepository implements PostRepository } } ``` -Нам просто нужно периодически делать снимки Агрегата. Мы можем делать это синхронно или асинхронно -с помощью процесса, отвечающего за мониторинг хранилища событий. Следующий код представляет собой простой пример, -демонстрирующий реализацию процесса создания снимка Агрегата: + +
+ +Нам просто нужно периодически делать снимки Агрегата. Мы можем делать это синхронно или асинхронно с помощью процесса, отвечающего за мониторинг хранилища событий. Следующий код представляет собой простой пример, демонстрирующий реализацию процесса создания снимка Агрегата: + +
+ ```php class EventStorePostRepository implements PostRepository { @@ -1363,32 +1421,25 @@ class EventStorePostRepository implements PostRepository } } ``` ->***ORM или без ORM*** ->Из представленного варианта использования архитектурного стиля ясно, что использование ORM только для ->сохранения/извлечения событий было бы излишним. Даже если мы используем реляционную базу данных для их хранения, ->нам нужно только сохранять/извлекать события из хранилища данных. + +
+ +> ***ORM или без ORM*** +> Из представленного варианта использования архитектурного стиля ясно, что использование ORM только для сохранения/извлечения событий было бы излишним. Даже если мы используем реляционную базу данных для их хранения, нам нужно только сохранять/извлекать события из хранилища данных. + +
## Резюмируем -Поскольку существует множество вариантов архитектурных стилей, возможно, вы немного запутались в этой главе. -Чтобы сделать выбор вы должны рассмотреть компромиссы для каждого из них. -Ясно одно: подход Большой Комок Грязи - не вариант, так как код будет + +Поскольку существует множество вариантов архитектурных стилей, возможно, вы немного запутались в этой главе. Чтобы сделать выбор вы должны рассмотреть компромиссы для каждого из них. Ясно одно: подход Большой Комок Грязи - не вариант, так как код будет очень быстро "портиться". Многоуровневая Архитектура является лучшим вариантом, но -она имеет некоторые недостатки,такие как тесная связь между слоями. -Можно утверждать, что наиболее сбалансированным вариантом будет Гексогональная -Архитектура, поскольку она может использоваться в качестве базовой архитектуры и обеспечивает -высокую степень развязки и симметрии между внетренней и внешней частью приложения. Это то, что мы рекомендуем -для большинства сценариев. +она имеет некоторые недостатки, такие как тесная связь между слоями. +Можно утверждать, что наиболее сбалансированным вариантом будет Гексагональная +Архитектура, поскольку она может использоваться в качестве базовой архитектуры и обеспечивает высокую степень развязки и симметрии между внутренней и внешней частью приложения. Это то, что мы рекомендуем для большинства сценариев. Мы также рассматриваем CQRS и EventSourcing как относительно гибкие архитектуры, которые помогут вам в борьбе с высокой сложностью проекта. -CQRS и EventSourcing являются мощными подходами, но не позволяйте фактору -крутости отвлекать вас от ценности, которую они предоставляют. -Поскольку они оба идут некоторыми накладными расходами, у вас должна быть техническая -причина для оправдания использования этих подходов. Эти архитектурыне стили -действительно очень полезны, и эвристически узнать необходимость применения можно посчитав количество -заявителей в репозиториев CQRS и количеству инициированных событий для -EventSourcing. Если число методов поиска начинает расти. а хранилища становятся -сложными в обслуживании, то пришло время рассмотреть вопрос об использовании CQRS, -чтобы разделить задачи чтения и записи. И после этого, если объем событий в каждом -Агрегате имеет тенденции к росту, и бизнес заинтересован в более детальной информации, -то можно подумать о том, может ли окупиться переход на EventSourcing. \ No newline at end of file +CQRS и EventSourcing являются мощными подходами, но не позволяйте фактору +крутости отвлекать вас от ценности, которую они предоставляют. Поскольку они оба идут некоторыми накладными расходами, у вас должна быть техническая +причина для оправдания использования этих подходов. Эти архитектурные стили +действительно очень полезны, и эвристически узнать необходимость применения можно посчитав количество заявителей в репозитории CQRS и количеству инициированных событий для EventSourcing. Если число методов поиска начинает расти. а хранилища становятся сложными в обслуживании, то пришло время рассмотреть вопрос об использовании CQRS, чтобы разделить задачи чтения и записи. И после этого, если объем событий в каждом Агрегате имеет тенденции к росту, и бизнес заинтересован в более детальной информации, то можно подумать о том, может ли окупиться переход на EventSourcing. From 753ac5fd3f85dfcc771a09cae5e77e92de8925a1 Mon Sep 17 00:00:00 2001 From: eatae Date: Tue, 19 Mar 2024 17:02:04 +0300 Subject: [PATCH 5/9] Chapter1: refs correction --- .../Getting-Started-with-Domain-Driven-Design.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md index 644ea4f..66a262f 100644 --- a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md +++ b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md @@ -109,7 +109,7 @@ DDD это не серебряная пуля, как и все в програ ## Стратегический обзор -Чтобы дать общий обзор стратегической стороны DDD, мы будем использовать подход из книги Джимми Нильссона «*[Применение доменно-управляемого дизайна и шаблонов]*(http://www.williamspublishing.com/Books/978-5-8459-1296-1.html)» (Jimmy Nilsson "Applying Domain-Driven Design and Patterns"). Рассмотрим два разных пространства: пространство задач и пространство решений. +Чтобы дать общий обзор стратегической стороны DDD, мы будем использовать подход из книги Джимми Нильссона [Применение доменно-управляемого дизайна и шаблонов](http://www.williamspublishing.com/Books/978-5-8459-1296-1.html) (Jimmy Nilsson "Applying Domain-Driven Design and Patterns"). Рассмотрим два разных пространства: пространство задач и пространство решений. В пространстве задач, DDD использует Домены (Domains) и Субдомены (Subdomains) для группировки и организации того, что компания хочет решить (реализовать). В случае онлайн-агенства путешествий (OTA), проблема заключается в том, чтобы иметь дело с такими вещами, как авиабилеты и бронирование гостиниц. Такой Домен может бы огранизован в разные Субдомены, такие как: ценообразование, инвентаризация, управление пользователями и др. @@ -125,8 +125,8 @@ DDD это не серебряная пуля, как и все в програ Если ваш Домен (проблема которую вам нужно решить) - не является сложным, то применение стратегической части DDD может добавить ненужные накладные расходы и замедлить скорость разработки. Если вы хотите узнать больше о стратегической части DDD, вам следует взглянуть на первые три главы книги Вона Вернона -*[Реализация методов предметно-ориентированного проектирования]*(http://www.williamspublishing.com/Books/978-5-8459-1881-9.html) -или книгу Эрика Эвинса "*[Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем]*(http://www.williamspublishing.com/Books/978-5-8459-1597-9.html)". +[Реализация методов предметно-ориентированного проектирования](http://www.williamspublishing.com/Books/978-5-8459-1881-9.html) +или книгу Эрика Эвинса [Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем](http://www.williamspublishing.com/Books/978-5-8459-1597-9.html).
@@ -142,9 +142,9 @@ DDD это не серебряная пуля, как и все в програ
Если вы хотите узнать больше о Микросервисах, начните с их руководства. Как это связанно с DDD? -Как объясняется в книге Сэма Ньюмана "*[Создание Микросервисов]*(https://www.piter.com/product/sozdanie-mikroservisov)", микросервисы являются реализацией Ограниченного контекста (Bounded Context) в DDD. +Как объясняется в книге Сэма Ньюмана [Создание Микросервисов](https://www.piter.com/product/sozdanie-mikroservisov), микросервисы являются реализацией Ограниченного контекста (Bounded Context) в DDD. -Помимо Микросервисов, другим связанным механизмом является Автономные Системы (Self-Contained Systems). Согласно веб-сайту [Self-Contained Systems]*(http://scs-architecture.org/) +Помимо Микросервисов, другим связанным механизмом является Автономные Системы (Self-Contained Systems). Согласно веб-сайту [Self-Contained Systems](http://scs-architecture.org/)
@@ -189,4 +189,4 @@ DDD это не серебряная пуля, как и все в програ - DDD - это долгосрочная инвестиция. Это требует активных усилий. Эксперты Предметной Области должны тесно сотрудничать с разработчиками, а разработчики должны думать с точки зрения бизнеса. В конечном итоге клиенты бизнеса - это те, кто должны быть довольны вашей работой. Реализация DDD требует усилий. Если бы это было легко, все писали бы качественный код. Будьте готовы, потому что вы скоро узнаете, как писать код, который при прочтении отлично описывает бизнес вашей компании. -Наслаждайтесь этим приключением! \ No newline at end of file +Наслаждайтесь этим приключением! From 6c8a7a7bdf16e04d4116d05bebad6360acfc211d Mon Sep 17 00:00:00 2001 From: eatae Date: Tue, 19 Mar 2024 17:07:22 +0300 Subject: [PATCH 6/9] Chapter2: refs correction --- ru-RU/Chapter2/Architectural-Styles.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ru-RU/Chapter2/Architectural-Styles.md b/ru-RU/Chapter2/Architectural-Styles.md index 1d103c1..d957fa5 100644 --- a/ru-RU/Chapter2/Architectural-Styles.md +++ b/ru-RU/Chapter2/Architectural-Styles.md @@ -75,7 +75,7 @@ $result = mysql_query('SELECT id, title, content FROM posts', $link);?> ``` -!*[Cool]*(https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/cool.jpeg) +![Cool](https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/cool.jpeg)
@@ -163,7 +163,7 @@ if (null !== $errormsg) : ?> Основное правило многоуровневой архитектуры - это то, что каждый слой должен иметь связь (возможность использовать) с нижестоящими слоями, как показано на рисунке ниже: -!*[Layered Architecture]*(https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/image--004.jpg) +![Layered Architecture](https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/image--004.jpg) **Многоуровневая Архитектура** стремится к разделению различных компонентов приложения. Например, с точки зрения предыдущего примера, **Представление** сообщения в блоге должно быть полностью независимым от сообщения в блоге как концептуальной сущности. Сообщение в блоге как концептуальная сущность может быть отображено одним или несколькими **Представлениями**, вместо того чтобы быть тесно связанным с каким либо конкретным **Представлением**. Это принято называть **Разделением Ответственности** (Separation of Concerns). @@ -187,7 +187,7 @@ if (null !== $errormsg) : ?> **Представление (The View)** Отображает различные Представления слоя Модели и предоставляет способ вызывать изменения в состоянии модели. -!*[The MVC pattern]*(https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/image--006.jpg) +![The MVC pattern](https://raw.githubusercontent.com/TalismanFR/dddinphp/master/share/image--006.jpg)
From c3c7ae2685405bfbdf9b7fee3dd04c55bf47af52 Mon Sep 17 00:00:00 2001 From: eatae Date: Tue, 19 Mar 2024 17:09:55 +0300 Subject: [PATCH 7/9] Chapter2: refs correction --- ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md index 0cc1659..1e83906 100644 --- a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md +++ b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md @@ -188,4 +188,4 @@ DDD это не серебрянная пуля; как и все в прогр Реализация DDD требует усилий. Если бы это было легко, все писали бы качественный код. Будьте готовы, потому что вы скоро узнаете, как писать код, который при прочтении отлично описывает бизнес вашей компании. - Наслаждайтесь этим приключением! \ No newline at end of file + Наслаждайтесь этим приключением! From cca1e9c35f8e531bf1b61dc765fec012e50dbfac Mon Sep 17 00:00:00 2001 From: eatae Date: Tue, 19 Mar 2024 17:15:09 +0300 Subject: [PATCH 8/9] Chapter1: refs correction --- ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md index 66a262f..0c02f42 100644 --- a/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md +++ b/ru-RU/Chapter1/Getting-Started-with-Domain-Driven-Design.md @@ -8,6 +8,7 @@ > Основная цель этой книги - показать примеры тактических шаблонов DDD используя PHP. Если вы хотите больше узнать о стратегических шаблонах и о DDD в принципе, вам следует прочитать книги за авторством Vaughn Vernon и Eric Evans.
+ Что очень важно, DDD не связан с технологиями. Вместо этого речь идет о развитии знаний о бизнесе и использовании технологий для обеспечения ценности. Только когда вы сможете понять бизнес, в котором работает ваша компания, вы сможете участвовать в процессе создания модели программного обеспечения порождая Единый Язык (Ubiquitous Language).
From 817e4ed1cc46d751e72722bc9eb3bd5c5d48f635 Mon Sep 17 00:00:00 2001 From: eatae Date: Tue, 19 Mar 2024 22:21:04 +0300 Subject: [PATCH 9/9] =?UTF-8?q?Chapter3:=20=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D1=88=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= =?UTF-8?q?=20=D1=82=D0=B5=D0=BA=D1=81=D1=82=D0=B0=20=D0=B8=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D0=B4=D0=B5=D0=BB=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=20=D0=B2?= =?UTF-8?q?=20=D0=B2=D0=B8=D0=B4=D0=B5=20=D1=82=D0=B5=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ru-RU/Chapter3/Value-Objects.md | 815 +++++++++++++++++++------------- 1 file changed, 478 insertions(+), 337 deletions(-) diff --git a/ru-RU/Chapter3/Value-Objects.md b/ru-RU/Chapter3/Value-Objects.md index 207bd6a..0bbe950 100644 --- a/ru-RU/Chapter3/Value-Objects.md +++ b/ru-RU/Chapter3/Value-Objects.md @@ -1,80 +1,81 @@ Глава 3. Объекты-Значения (The Value Objects) == -Объекты-Значения являются фундаментальным строительным блоком DDD, и они используются для моделирования концепции вашего -Единого Языка в коде. Объект Значения - это не просто вещь в вашем домене - -он измеряет, определяем количество или описывает что-то. Объекты Значения могут рассматриваться -как небольшие простые объекты, такие как деньги или диапозон дат, равенство -которых основано не на идентификаторе, а на содержании. - -Например, цена продукта может быть смоделирована с использованием Объекта Значения. -В данном случае это не вещь, а ценность, которая позволяет нам измерить, сколько -денег стоит продукт. Объем памяти для таких объектов легко определяется (рассчитывается -по составным частям), а накладных расходов очень мало. В результате создание нового экземпляра -предпочтительнее повторного использования ссылок, даже если Объект-Значения использоуется для -предоставления одного и того же значения. Равенство проверяется на основе сопоставимости -всех полей экземпляров. +Объекты-Значения являются фундаментальным строительным блоком DDD, и они используются для моделирования концепции вашего Единого Языка в коде. Объект Значения - это не просто вещь в вашем домене - он измеряет, определяет количество или описывает что-то. +Объекты Значения могут рассматриваться как небольшие простые объекты, такие как деньги или диапазон дат, равенство которых основано не на идентификаторе, а на содержании. + +Например, цена продукта может быть смоделирована с использованием Объекта Значения. В данном случае это не вещь, а ценность, которая позволяет нам измерить, сколько денег стоит продукт. Объем памяти для таких объектов легко определяется (рассчитывается по составным частям), а накладных расходов очень мало. В результате создание нового экземпляра предпочтительнее повторного использования ссылок, даже если Объект-Значения используется для предоставления одного и того же значения. Равенство проверяется на основе сопоставимости всех полей экземпляров. + +
## Определение -Уорд Каннингем (Ward Cunningham) дает следующее [определение](http://c2.com/cgi/wiki?ValueObject) Объекту Значения: ->Это мера или описание чего-либо. Примерами Объектов Значения являются такие вещи, как числа, даты, денежные суммы и строки. ->Обычно это не большие Объекты, которые используются довольно широко. ->Их идентичность основана на их состояни, а не на идентификаторе Объекта. ->Таким образом, вы можете иметь несколько копий одного концептуального Объекта Значения. ->Каждая банкнота достоинством 5 долларов имеет свою собственную идентификацию (благодаря ->своему серийному номеру), но экономия денежных средств зависит от того, что ->каждая банкнота достоинством в 5 долларов имеет ту же стоимость, что и любая другая банкнота в 5 долларов. - -Мартин Фаулер дает следующее [определение](http://martinfowler.com/bliki/ValueObject.html) Объекту Значения: ->Это маленький Объект, такой как деньги или диапозон дат. Их ключевое свойство ->заключается в том, что они следуют семантике значений, а не ссылочной семантике. ->Мы можем так утверждать, потому что их понятие равенства не основано на идентификаторе, ->вместо этого два Объекта Значения равны, если все их поля равны. ->Хотя все поля равны, вам не нужно сравнивать все поля, если подмножество уникально - ->например, кодов валюты для объектов валюты достаточно чтобы проверить равенство. ->Общая эвристика заключается в том, что Объекты Значения должны быть полностью неизменными. ->Если вы хотите изменить объект значения, вы должны заменить объект новым и не иметь ->возможности обновлять значения самого Объекта Значения. Обновляемые Объекты Значения приводят ->к проблемам с наложением имен. - -Примерами Объектов Значения являются числа, текстовые строки, даты, время, полное имя человека ->***Упражнение*** ->Попробуйте найти дополнительные Объекты Значения в вашем текущем Домене. + +**Уорд Каннингем (Ward Cunningham) дает следующее [определение](http://c2.com/cgi/wiki?ValueObject) Объекту Значения:** + +
+ +> Это мера или описание чего-либо. Примерами Объектов Значения являются такие вещи, как числа, даты, денежные суммы и строки. +> Обычно это не большие Объекты, которые используются довольно широко. Их идентичность основана на их состоянии, а не на идентификаторе Объекта. +> Таким образом, вы можете иметь несколько копий одного концептуального Объекта Значения. +> Каждая банкнота достоинством 5 долларов имеет свою собственную идентификацию (благодаря своему серийному номеру), но экономия денежных средств зависит от того, что каждая банкнота достоинством в 5 долларов имеет ту же стоимость, что и любая другая банкнота в 5 долларов. + +
+ +**Мартин Фаулер дает следующее [определение](http://martinfowler.com/bliki/ValueObject.html) Объекту Значения:** + +
+ +> Это маленький Объект, такой как деньги или диапазон дат. Их ключевое свойство заключается в том, что они следуют семантике значений, а не ссылочной семантике. Мы можем так утверждать, потому что их понятие равенства не основано на идентификаторе, вместо этого два Объекта Значения равны, если все их поля равны. Хотя все поля равны, вам не нужно сравнивать все поля, если подмножество уникально - например, кодов валюты для объектов валюты достаточно чтобы проверить равенство. +> Общая эвристика заключается в том, что Объекты Значения должны быть полностью неизменными. Если вы хотите изменить объект значения, вы должны заменить объект новым и не иметь возможности обновлять значения самого Объекта Значения. Обновляемые Объекты Значения приводят к проблемам с наложением имен. + +
+ +Примерами Объектов Значения являются числа, текстовые строки, даты, время, полное имя человека. + +
+ +> ***Упражнение*** +> Попробуйте найти дополнительные Объекты Значения в вашем текущем Домене. + +
## Объекты значения vs Сущности Рассмотрим следующие примеры из [Википедии](http://en.wikipedia.org/wiki/Domain-driven_design#Building_blocks_of_DDD), чтобы лучше понять разница между Объектами Значениями и Сущностями: -- Объект Значения. Когда люди обменивают долларовые банкноты, они обычно не -различают каждый уникальную купюру; они обеспокоены только номинальной стоимостью -долларовой банкноты. В этом контексте далларовые банкноты ялвяются Объектами Значения. -Тем не менее, Федеральный Резерв может быть обеспокоен каждым уникальным счетом; в этом контексте -каждый счет был бы сущностью. -- Сущность. Большинство авиакомпаний различают каждое место уникально на каждом рейсе. -Каждое место в данном контексте является сущностью. Однако Southwest Airlines, EasyJet и -Ryanair не различают каждое место; все места одинаковые. В этом контексте место на самом деле является -Объектом Значения. - ->***Упражнение*** ->Подумайте о понятии адреса (индекс, улица, номер дома и т.д.). Каков возможный ->контекст, в котором адрес может быть смоделирован как сущность, а не как объект значения? ->Обсудите ваши выводы с коллегами. + +
+ +- **Объект Значения** + Когда люди обменивают долларовые банкноты, они обычно не различают каждую уникальную купюру, они обеспокоены только номинальной стоимостью долларовой банкноты. В этом контексте долларовые банкноты являются Объектами Значения. Тем не менее, Федеральный Резерв может быть обеспокоен каждым уникальным счетом, в этом контексте каждый счет был бы сущностью. + +- **Сущность** + Большинство авиакомпаний различают каждое место уникально на каждом рейсе. Каждое место в данном контексте является сущностью. Однако Southwest Airlines, EasyJet и Ryanair не различают каждое место; все места одинаковые. В этом контексте место на самом деле является Объектом Значения. + +
+ +> ***Упражнение*** +> +> Подумайте о понятии адреса (индекс, улица, номер дома и т.д.). Каков возможный +> контекст, в котором адрес может быть смоделирован как сущность, а не как объект значения? Обсудите ваши выводы с коллегами. + +
## Пример "Валюта" и "Стоимость" -Объекты Значения "Валюта" и "Денежная Стоимость", вероятно, являются наиболее -часто используемыми примерами для объяснения Объектов Значения, благодаря -[патерну "Деньги" (Money)](http://martinfowler.com/eaaCatalog/money.html). -Этот шаблон проектирования предоставляет решение для моделирования задачи, которая позволяет -избежать проблемы округления с плавающей запятой, что, в свою очередь, позволяет выполнять -детерминированные вычисления. +Объекты Значения "Валюта" и "Денежная Стоимость", вероятно, являются наиболее часто используемыми примерами для объяснения Объектов Значения, благодаря [патерну "Деньги" (Money)](http://martinfowler.com/eaaCatalog/money.html). +Этот шаблон проектирования предоставляет решение для моделирования задачи, которая позволяет избежать проблемы округления с плавающей запятой, что, в свою очередь, позволяет выполнять детерминированные вычисления. В реальном мире валюта описывает денежные единицы так же, как метры и ярды описывают единицы расстояния. Каждая валюта представлена трехбуквенным ISO кодом в верхнем регистре: + +
+ ```php class Currency { private $isoCode; + public function __construct($anIsoCode) { $this->setIsoCode($anIsoCode); @@ -94,25 +95,30 @@ class Currency } } ``` -Одна из основных целей Объектов Значения так же являются святым Граалем ООП: инкапсуляция. -Следуя этому шаблону, вы получите выделенное место для размещения всей проверки, -логики сравнения и поведения для данной концепции. - ->***Дополнительные Проверки Валюты*** ->В предыдущем примере кода мы можем построить валюту с кодом ISO равным ААА. ->Это не валидно, валюты с такой кодировкой не существует. Напишите более конкретное правило, ->которое проверит, действителен ли код ISO. Полный список действительный валютных кодов ISO ->можно найти ->[здесь](https://www.xe.com/iso4217.php). Если вам нужна помощь, взгляните на библиотеку ->[Money Packagist](https://github.com/moneyphp/money) - -Деньги используются для измерения определенной суммы валюты. Они моделируются с + +
+ +Одна из основных целей Объектов Значения так же являются святым Граалем ООП - **инкапсуляция**. Следуя этому шаблону, вы получите выделенное место для размещения всей проверки, логики сравнения и поведения для данной концепции. + +
+ +> ***Дополнительные Проверки Валюты*** +> +> В предыдущем примере кода мы можем построить валюту с кодом ISO равным ААА. +> Это невалидно, валюты с такой кодировкой не существует. Напишите более конкретное правило, которое проверит, действителен ли код ISO. Полный список действительный валютных кодов ISO можно найти [здесь](https://www.xe.com/iso4217.php). +> Если вам нужна помощь, взгляните на библиотеку [Money Packagist](https://github.com/moneyphp/money) + +
+ +Деньги используются для измерения определенной суммы валюты. Они моделируются с помощью суммы и валюты. Сумма, в случае паттерна Money, реализуется с использованием -целочисленного представления наименее значимой доли валюты - например, в центах долларов США или -копеек в рублях России. +целочисленного представления наименее значимой доли валюты - например, в центах долларов США или копеек в рублях России. В качестве бонуса вы также можете заметить, что мы используем самоинкапсуляцию -для установки кода ISO, которая центарлизует изменения в самом Объекте Значения: +для установки кода ISO, которая централизует изменения в самом Объекте Значения: + +
+ ```php class Money { @@ -146,59 +152,57 @@ class Money } } ``` -Теперь, когда вы знаете формальное определение Объектов Значений, давайте -углубимся в тот мощной функционал который они предоставляют. + +
+ +Теперь, когда вы знаете формальное определение Объектов Значений, давайте углубимся в тот мощной функционал который они предоставляют. + +
## Характеристики -При моделированние концепции Единого Языка вашего Домена, вы всегда должны отдавать -предпочтение Объектам Значения, а не Сущностям. Объекты Значения проще создавать, тестировать, -использовать и поддерживать. +При моделировании концепции Единого Языка вашего Домена, вы всегда должны отдавать предпочтение Объектам Значения, а не Сущностям. Объекты Значения проще создавать, тестировать, использовать и поддерживать. -Исходя из этого, вы можете определить, может ли рассматриваемая концепция моделироваться как -Объект Значения, если: +Исходя из этого, вы можете определить, может ли рассматриваемая концепция моделироваться как Объект Значения, если: - Он измеряет, оценивает или описывает объект предметной области. -- Он может быть имутабельным (неизменяемым). +- Он может быть иммутабельным (неизменяемым). - Он моделирует концептуальное целое, объединяя связанные атрибуты в единое целое. - Он может быть сравнен с другим таким же объектом через равенство всех его аттрибутов. - Он полностью заменяется при изменении способа измерения или описания. - Он предоставляет связанным с ним объектам функцию без побочных эффектов. +
+ ### Измерение, Оценивание, или Описание -Как говорилось выше, Объект Значения не должен рассматриваться как вещь в вашем Домене. -В качестве значения Объект Значения измеряет, оценивает или описывает концепцию в Домене. - -В нашем примере объект `Currency` описывает тип объекта `Money`. Объект `Money` измеряет -или количественно оценивает единицы данной валюты. - -### Имутабельность -Это один из важнейших аспектов для усвоения. Объекты Значения не могут быть измнены в -течении всего периода их использования. Благодаря этой имутабельности Объект Значения -легко создавать и тестировать, они не содержат нежелательных/неожиданных побочных эффектов. -Таким образом, Объекты Значения должны создаваться через их конструкторы. -Чтобы создать его, вы обычно передаете необходимые типы притивов или другие Объект Значения -через конструктор. - -Объекты Знечения всегда находятся в валидном состоянии; Вот почему мы создаем их за один -шаг. Пустые конструкторы с несколькими сеттерами или геттерами переносят -ответственность за создание на клиента что приводит к Анемичность Доменой Модели, что считается антипатерном. - -Также важно отметить, что не рекомендуется хранить ссылки на Сущности внутри ваших Объектов Значения. -Сущности являются изменяемыми, и хранение ссылок на них может привести к -нежелательным побочным эффектам в Объекте Значения. - -В языках с `перегрузкой методов` таких как Java, вы можете создавать несколько -конструкторов с одинаковыми именами. Каждому из этих конструкторов предоставляются + +Как говорилось выше, Объект Значения не должен рассматриваться как вещь в вашем Домене. В качестве значения Объект Значения измеряет, оценивает или описывает концепцию в Домене. + +В нашем примере объект `Currency` описывает тип объекта `Money`. Объект `Money` измеряет или количественно оценивает единицы данной валюты. + +
+ +### Иммутабельность + +Это один из важнейших аспектов для усвоения. Объекты Значения не могут быть изменены в течении всего периода их использования. Благодаря этой иммутабельности Объект Значения легко создавать и тестировать, они не содержат нежелательных/неожиданных побочных эффектов. +Таким образом, Объекты Значения должны создаваться через их конструкторы. Чтобы создать его, вы обычно передаете необходимые типы примитивов или другие Объекты Значения через конструктор. + +Объекты Значения всегда находятся в валидном состоянии. Вот почему мы создаем их за один шаг. Пустые конструкторы с несколькими сеттерами или геттерами переносят ответственность за создание на клиента что приводит к Анемичность Доменой Модели, что считается антипаттерном. + +Также важно отметить, что не рекомендуется хранить ссылки на Сущности внутри ваших Объектов Значения. Сущности являются изменяемыми, и хранение ссылок на них может привести к нежелательным побочным эффектам в Объекте Значения. + +В языках с `перегрузкой методов` таких как Java, вы можете создавать несколько конструкторов с одинаковыми именами. Каждому из этих конструкторов предоставляются различные входные параметры для создания одного и того же типа результирующего объекта. -В PHP мы можем реализовать аналогичную возможность с помощью фабричных методов. -Эти специфичные фабричные методы также известны как семантические конструкторы. -Основная цель `fromMoney` метода предоставить больше контекстуального значения, чем +В PHP мы можем реализовать аналогичную возможность с помощью фабричных методов. Эти специфичные фабричные методы также известны как семантические конструкторы. +Основная цель `fromMoney` метода предоставить больше контекстуального значения, чем простой конструктор. Более радикальные подходы предлагают сделать метод `__construct` приватным и для создания объекта используют семантический конструктор. -В нашем объекте `Money` мы могли бы реализовать несколько полезных -фабричных методово, какие как: +В нашем объекте `Money` мы могли бы реализовать несколько полезных +фабричных методов, какие как: + +
+ ```php class Money { @@ -216,21 +220,24 @@ class Money } } ``` -Использую ключевое слово `self`, мы не связываем код с именем класса. -Таким образом, изменение имени класса или пространства имен не повлияет на работоспособность -фабричных методов. Эта маленькая деталь реализации поможет нам при рефакторинге на более -познем этапе. ->***`static` или `self`*** ->Использование `static` вместо `self` может привести к нежелательным проблемам, когда Объект Значения ->наследуется от другого Объекта Значения. +
+ +Использую ключевое слово `self`, мы не связываем код с именем класса. Таким образом, изменение имени класса или пространства имен не повлияет на работоспособность фабричных методов. Эта маленькая деталь реализации поможет нам при рефакторинге на более позднем этапе. + +
+ +> ***`static` или `self`*** +> Использование `static` вместо `self` может привести к нежелательным проблемам, когда Объект Значения наследуется от другого Объекта Значения. -Учитывая имутабельность, нам нужно понять как обрабатывать действия изменения объекта, которые -обычно встречаются в контексте состояния. Если нам требуется измненеия состояния, мы должны вернуть -новой экземпляр Объекта Значения в котором учтено это измненение. Например если мы хотим увеличить -сумму Объекта Значения `Money`, мы вместо того чтобы этому объекту присвоить новое значени должны создать и вернуть новый объект. +
+ +Учитывая иммутабельность, нам нужно понять как обрабатывать действия изменения объекта, которые обычно встречаются в контексте состояния. Если нам требуется изменения состояния, мы должны вернуть новый экземпляр Объекта Значения в котором учтено это изменение. Например если мы хотим увеличить сумму Объекта Значения `Money`, мы вместо того чтобы этому объекту присвоить новое значение должны создать и вернуть новый объект. К счастью, следовать этому правилу достаточно просто: + +
+ ```php class Money { @@ -244,8 +251,13 @@ class Money } } ``` -Объект `Money` возращаемый функцией `increaseAmountBy`, отличается от объекта `Money` в которым был -иницирован вызов функции. Это можно наблюдать в приведенных ниж примерах проверов соппоставимости: + +
+ +Объект `Money` возвращаемый функцией `increaseAmountBy`, отличается от объекта `Money` в которым был инициирован вызов функции. Это можно наблюдать в приведенных ниже примерах проверок сопоставимости: + +
+ ```php increaseAmountBy(100); var_dump($aMoney === $otherMoney); // bool(false) ``` + +
+ ### Концептуальное Целое -Может возникнуть вопрос: "Почему бы просто не реализовать что-то похожее как на примере ниже, полностью -избегая необходимости в Объекте Значения?": + +Может возникнуть вопрос: "Почему бы просто не реализовать что-то похожее как на примере ниже, полностью избегая необходимости в Объекте Значения?": + +
+ ```php class Product { @@ -275,42 +293,53 @@ class Product // ... } ``` -У этого подхода есть некоторые значительные недостатки, например, если вы хотите проверить ISO. -В действительности нет необходимости в том чтобы `Product` отвечал за валидацию ISO валюты `Currency` -(это нарушает первое правило SOLID, Единство Ответственности). Это еще более заметно, если вы -хотите повторно использовать сопутствующую логику в других частях вашего Домена (придерживаясь принципа DRY). -Учитивая эти факторы, описанный пример является идеальным каждидатом для абстрагирования в Объект Значения. -Использование этой бастракции не только дает вам возможность группировать связанные свойства, -но также позволяет создавать концепции более высокого порядка и более конкретный Единый Язык. +
+ +У этого подхода есть некоторые значительные недостатки, например, если вы хотите проверить ISO. В действительности нет необходимости в том чтобы `Product` отвечал за валидацию ISO валюты `Currency` (это нарушает первое правило SOLID, Единство Ответственности). Это еще более заметно, если вы хотите повторно использовать сопутствующую логику в других частях вашего Домена (придерживаясь принципа DRY). + +Учитывая эти факторы, описанный пример является идеальным кандидатом для абстрагирования в Объект Значения. Использование этой абстракции не только дает вам возможность группировать связанные свойства, но также позволяет создавать концепции более высокого порядка и более конкретный Единый Язык. ->***Упражениние*** -> Обсудите с коллегами, может ли email считаться Объектом Значения. Имеет ли значение контект в котором ->используется объект? -### Равенство (эквиваленство) Значения +
+ +> ***Упражениние*** +> +> Обсудите с коллегами, может ли email считаться Объектом Значения. Имеет ли значение контекст в котором используется объект? + +
+ +### Равенство (эквивалентность) Значения Как обсуждалось ранее, два Объекта-Значения равны, если содержимое, которое они измеряют, оценивают, или описывают, одинаково. -Например представьте два объекта `Money` наминалом 1 USD. Можем мы их счетать равными? -В реальном мире две купюры в 1 USD оцениваются одинаково? Скорее всего да. -Возращаясь к коду, расматриваемые два Объекта-Значения являются двумя разными -экземплярами объекта, однако они представляют одниковое значение что делает их подобными. +Например представьте два объекта `Money` номиналом 1 USD. Можем мы их считать равными? В реальном мире две купюры в 1 USD оцениваются одинаково? Скорее всего да. Возвращаясь к коду, рассматриваемые два Объекта-Значения являются двумя разными экземплярами объекта, однако они представляют одинаковое значение что делает их подобными. Касаемо PHP, обычно сравнивают два Объекта-Значения оператором `==`. Если мы изучим документацию PHP касаемо оператора то увидем интересное поведение: ->При использовании оператора сравнения `==` переменные объекта сравниваются простым способом ->а именно: два экземпляра объекта равно, если они имеют одинаковые атрибуты ->и значения, и являются экземплярами одного и того же класса. - Данное поведение соответствует нашему формальному описанию Объекта-Значения. ->Однако, поскольку присутствует предикат точного совпадения имени класса, вам следует ->быть осторожными при проверке подтипов Объектов-Значения. + +> При использовании оператора сравнения `==` переменные объекта сравниваются простым способом +> а именно: два экземпляра объекта равно, если они имеют одинаковые атрибуты +> и значения, и являются экземплярами одного и того же класса. + +
+ +Данное поведение соответствует нашему формальному описанию Объекта-Значения. + +> Однако, поскольку присутствует предикат точного совпадения имени класса, вам следует быть осторожными при проверке подтипов Объектов-Значения. + +
К сожалению, даже более строгий оператор `===` нам не поможет: ->При использовании оператора сравнения `===` переменные объекта равны только тогда, когда они ->ссылаются на один и тот же экземпляр одного и того же класса. + +> При использовании оператора сравнения `===` переменные объекта равны только тогда, когда они ссылаются на один и тот же экземпляр одного и того же класса. + +
Следующий пример должен помочь понять эти тонкие различия: + +
+ ```php + +Решение состоит в том чтобы реализовать метод сравнения в каждом классе Объектов Значения. Этот метод выполняет проверку типа и равенства его составных атрибутов. +Сравнение абстрактных типов данных легко реализовать с помощью встроенной подсказки типов в PHP. Вы также можете использовать функцию `get_class()` для помощи в проверке сопоставимости при необходимости. + +
+ +### Заменяемость + +Рассмотрим сущность `Product`, которая использует Объект Значения `Money` для хранения цены продукта. Дополнительно рассмотрим две сущности `Product` с одинаковой ценой например 100 USD. Этот сценарий можно смоделировать используя два отдельных объекта `Money` или две ссылки указывающих на один Объект Значения. + +Совместное использование одного Объекта Значения может быть рискованным решением, измнения затрагивают стоимость сразу двух объектов `Product`. Такое поведение можно считать неожиданным побочным эффектом. +Например, если мы знаем что Карлос был принят на работу 20 февраля, и мы знаем, что Кристиан был принят в тот же день, мы можем установить дату приема на работу Кристиана такой же, как и у Карлоса. Если Карлос затем меняет месяц своей даты найма на май, меняется и дата найма Кристиана. Правильно это или нет, но это не то, чего ожидают люди. + +Из-за проблем, выделенных в этом примере, при удерживании ссылки на Объект Значения рекомендуется заменять объект целиком, а не изменять его значение: + +
+ ```php $this−>price = new Money(100, new Currency('USD')); //... $this->price = $this->price->increaseAmountBy(200); ``` -Такое подход повторяет поведения базовых типов PHP (string например). Рассмотрим функцию -`strtolower`. Он возращает новую строку, а не изменяет исходную. Ссылка не используется; вместо этого -возвращается новое значение. +
+ +Такое подход повторяет поведения базовых типов PHP (string например). Рассмотрим функцию `strtolower`. Он возвращает новую строку, а не изменяет исходную. Ссылка не используется, вместо этого возвращается новое значение. + +
### Поведение без побочных эффектов. -Если мы хотим включить некоторое дополнительнео поведение - например, метод `add` - в наш класс -`Money`, вполне естественно проверить, что входные данные соответствуют любым предварительным условиям -и поддерживаю любую инвариантность. В нашем случае мы хотим добавлять деньги только с одинаковой валютой.: + +Если мы хотим включить некоторое дополнительное поведение - например, метод `add` - в наш класс `Money`, вполне естественно проверить, что входные данные соответствуют любым предварительным условиям и поддерживаю любую инвариантность. В нашем случае мы хотим добавлять деньги только с одинаковой валютой: + +
+ ```php class Money { @@ -369,8 +401,12 @@ class Money } ``` -Если две валюты не совпадают, бросается исключение. В противном случае суммы добавляются. -Однако этот код некоторые подводные камни. Теперь представьте, что в нашем коде имеется загадочный методы `otherMethod`: +
+ +Если две валюты не совпадают, бросается исключение. В противном случае суммы добавляются. Однако этот код имеет некоторые подводные камни. Теперь представьте, что в нашем коде имеется загадочный методы `otherMethod`: + +
+ ```php class Banking { @@ -383,16 +419,16 @@ class Banking } ``` -Всё будет впорядке, пока по какой-то причине мы не начнем видеть неожиданные рузельтаты в результате выполнения метода -`otherMethod`. Внезапно переменная а `$aMoney` более не является 100 USD. Что произошло? +
+ +Всё будет в порядке, пока по какой-то причине мы не начнем видеть неожиданные результаты при выполнении метода `otherMethod`. Внезапно переменная `$aMoney` более не является 100 USD. Что произошло? И что произойдет, если `otherMethod` внутренне использует наш ранее определенный метод `add`? -Возможно, вы не знаете, что метод `add` изменяет состояние экземпляра `Money`. Это то, что мы называем побочным эффектом. -Вы должны избегать побочных эффектов. Вы не должны изменять свои аргументы. Если вы это сделаете, -разработчик, использующий ваши объекты, может столкнуться с непредвиденным поведением. Он будет жаловаться и по делу. +Возможно, вы не знаете, что метод `add` изменяет состояние экземпляра `Money`. Это то, что мы называем побочным эффектом. Вы должны избегать побочных эффектов. Вы не должны изменять свои аргументы. Если вы это сделаете, разработчик, использующий ваши объекты, может столкнуться с непредвиденным поведением. Он будет жаловаться и по делу. + +Итак, как мы можем это исправить? Просто - убедившись, что Объект Значения является иммутабельным, мы избежим подобных проблем. Простым решением может быть возвращение нового экземпляра для каждой потенциально изменяемой операции, таких как метод `add`. + +
-Итак, как мы можем это исправить? Просто - убедившись, что Объект Значения является имутабельным, мы избежим -подобных проблем. Простым решением может быть возвращение нового экземпляра для каждой потенциально изменяемой -операции, таких как метод `add`. ```php class Money { @@ -409,13 +445,20 @@ class Money } } ``` -С таким простым решением, неизменяемость гарантируется. Каждый раз, когда два экземпляра `Money` используют -метод `add`, возращается новый результирующий экземпляр. Другие классы могут выполнить любое количество изменений, -не затрагивая оригинальную копию. Код без побочных эффектов просто для понимания, легко тестируется и менее + +
+ +С таким простым решением, неизменяемость гарантируется. Каждый раз, когда два экземпляра `Money` используют метод `add`, возращается новый результирующий экземпляр. Другие классы могут выполнить любое количество изменений, не затрагивая оригинальную копию. Код без побочных эффектов просто для понимания, легко тестируется и менее подвержен ошибкам. +
+ ## Базовые Типы + Рассмотрим следующий фрагмент кода: + +
+ ```php $a = 10; $b = 10; @@ -431,27 +474,22 @@ var_dump($a); // integer(50); ``` -Хотя `$a` и `$b` это разные переменные, хранящиеся в разные местах памяти, при сравнении они одинаковы. -Они имеют одинаковое значение, поэтому мы считаем их равными. Вы можете изменить значение `$a` -с 10 на 20 в любое время, выбрав новое значение 20 и подменив им 10. Вы можете заменять целочисленыне значения столько, -сколько хотите, без учета предыдущего значения потому что, вы не модифицируете его, вы его заменяете. -Если вы применяете какую либо операцию к ним - например сложение (`$a + $b`) - вы получите другое -новое значение, которое может быть присвоено другой переменной или ранее определенной. -Когда вы передаете `$a` какой либо функции, вы передаете значение, а не ссылку (за исключением тех случае -когда вы намеренно передаете ссылку). Не имеет значение, будет ли `$a` изменено в этой функции, потому что в -той области видимости откуда был вызван метод у вас все еще будет оригинальная копия. -Объекты Значения ведут себя как базовые типы. +
+ +Хотя `$a` и `$b` это разные переменные, хранящиеся в разные местах памяти, при сравнении они одинаковы. Они имеют одинаковое значение, поэтому мы считаем их равными. Вы можете изменить значение `$a` с 10 на 20 в любое время, выбрав новое значение 20 и подменив им 10. +Вы можете заменять целочисленные значения столько, сколько хотите, без учета предыдущего значения потому что, вы не модифицируете его, вы его заменяете. Если вы применяете какую либо операцию к ним - например сложение (`$a + $b`) - вы получите другое новое значение, которое может быть присвоено другой переменной или ранее определенной. +Когда вы передаете `$a` какой либо функции, вы передаете значение, а не ссылку (за исключением тех случае когда вы намеренно передаете ссылку). Не имеет значение, будет ли `$a` изменено в этой функции, потому что в той области видимости откуда был вызван метод у вас все еще будет оригинальная копия. Объекты Значения ведут себя как базовые типы. + +
## Тестирование Объектов Значения -Объекты Значения тестируются так же, как и иные объекты. Тем не менее, неизменяемость и поведение без побочных -эффектов так же должны быть проверены. Решение состоит в том, чтобы создать копию Объекта Значения, который вы тестируете, -перед выполнением каких-либо изменений. Реализацию метод `equal` используйте для проверки подобности. -Выполните действия, которые вы хотите проверить, и подтвердите результаты. Наконец, проверьте, -что исходны объект и копия по-прежнему равны. +Объекты Значения тестируются так же, как и иные объекты. Тем не менее, неизменяемость и поведение без побочных эффектов так же должны быть проверены. Решение состоит в том, чтобы создать копию Объекта Значения, который вы тестируете, перед выполнением каких-либо изменений. Реализацию метод `equal` используйте для проверки равенства. Выполните действия, которые вы хотите проверить, и подтвердите результаты. Наконец, проверьте, что исходный объект и копия по-прежнему равны. + +Давайте применим наши знания на практике и протестируем реализацию нашего метода `add` на отсутствие побочных эффектов. + +
-Давайте примененим наши знания на практике и протестируем реализацию нашего метода `add` - на отсутствие побочных эффектов. ```php class MoneyTest extends FrameworkTestCase { @@ -487,19 +525,18 @@ class MoneyTest extends FrameworkTestCase ``` + +
+ ## Сохранение Объекта Значения -Объекты Значения никогда не хранятся сами по себе; они, как правило, сохраняются как часть Агрегата. -Объекты Значения не должны сохраняться как полные записи, хотя в некоторых случаях это возможно. -Вместо этого лучше использовать шаблоны **Embedded Value** или **Serialize LOB**. -Оба шаблона могут быть использованы при сохранении ваших объектов с использованием Open Source ORM, например -Doctrine, или с поприетарным ORM. Так как Объект Значений являются малыми по размеру, Embedded Value -является хорошим выбором, поскольку этот подход обеспечивает простой способ запроса Сущностей по любому из аттрибутов -Объектов Значений. Однако, если для вас не важны запросы по этим полям, реализовать сериализованные стратегии будет проще. +Объекты Значения никогда не хранятся сами по себе, они, как правило, сохраняются как часть Агрегата. Объекты Значения не должны сохраняться как полные записи, хотя в некоторых случаях это возможно. Вместо этого лучше использовать шаблоны **Embedded Value** или **Serialize LOB**. Оба шаблона могут быть использованы при сохранении ваших объектов с использованием Open Source ORM, например Doctrine, или с поприетарным ORM. Так как Объект Значений являются малыми по размеру, Embedded Value является хорошим выбором, поскольку этот подход обеспечивает простой способ запроса Сущностей по любому из аттрибутов Объектов Значений. Однако, если для вас не важны запросы по этим полям, реализовать сериализованные стратегии будет проще. -Рассмотрим следующую Сущность `Product` с атрибутами типа `string`: `id`, `name` и `price` (`Money` Объект Значение). +Рассмотрим следующую Сущность `Product` с атрибутами типа `string`: `id`, `name` и `price` (`Money` Объект Значения). Мы намерено упростили пример сделав `id` строкой, а не Объектом Значения. +
+ ```php class Product { @@ -518,8 +555,13 @@ class Product // ... } ``` + +
+ Забегая вперед в Главу 10, реализация репозитория для сохранения Сущности может выглядеть так: +
+ ```php $product = new Product( $productRepository->nextIdentity(), @@ -529,31 +571,32 @@ $product = new Product( $productRepository−>persist(product); ``` -Теперь рассмотрим как специальные ORM и Doctrine реализуют сохранение Сущности `Product` содержащим -Объекты Значений. Мы остановимся на применени шаблонов `Embedded Value` и `Serialize LOB`, а -также на различиях между сохранением одного экземпляров Объекта Значения и их коллекций. +
+ +Теперь рассмотрим как специальные ORM и Doctrine реализуют сохранение Сущности `Product` содержащим Объекты Значений. Мы остановимся на применении шаблонов `Embedded Value` и `Serialize LOB`, а также на различиях между сохранением одного экземпляров Объекта Значения и их коллекций. + +
> **Почему Doctrine?** -> Doctrine - это великая ORM. Она покрывает 80% задач и требований, которые ставят приложения на PHP. ->У этой ORM отличное сообщество. При правильной настройке она может работать так же или даже лучше, чем ->сделаныне на заказ ORM (без потери удобства обслуживания). Мы рекомендуем использовать Doctrine в ->большинстве случаев при работе с Сущностями и безнес-логикой. Это сэкономит вам много времени и нервов. +> +> Doctrine - это великая ORM. Она покрывает 80% задач и требований, которые ставят приложения на PHP. У этой ORM отличное сообщество. При правильной настройке она может работать так же или даже лучше, чем сделанные на заказ ORM (без потери удобства обслуживания). Мы рекомендуем использовать Doctrine в большинстве случаев при работе с Сущностями и безнес-логики. Это сэкономит вам много времени и нервов. + +
## Сохранение Простого Объекта Значения. -Для сохранения одного Объекта Значения доступно множество опций. -Они варьируются от использования Serialize LOB или Embedded Value в качестве стратегии сопоставления, до использования -специального ORM или альтернативы с открытым исходных кодом, такой как Doctrine. -Под специальной ORM мы подразумеваем пользовательскую ORM, разработанная вашей компанией для сохранения Сущностей -в базу данных. В нашем сценарии специальный ORM код будет реализован с использованием библиотеки -[DBAL](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/). -Согласно [официальной документации](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/introduction.html) The Doctrine Database Abstraction & Access Layer (DBAL) предлагает -легкий и тонкий runtime слой вокруг PDO-подобного API и множество дополнительных горизонтальный функций, -самоанализ схемы базы данных и манипулирование через OO API. - -### Embedded Value (Встроеное Значение) с помощью специальной ORM -Если мы имеем дело со специальной ORM, использущей шаблон Embedded Value, нам нужно создать поле -в таблице Entity для каждого атрибута Объекта Значения. В этом случае для сохранения Сущности -`Product` требуется два дополнительных столбца - один для стоимости, второй для кода валюты. + +Для сохранения одного Объекта Значения доступно множество опций. Они варьируются от использования Serialize LOB или Embedded Value в качестве стратегии сопоставления, до использования специального ORM или альтернативы с открытым исходных кодом, такой как Doctrine. +Под специальной ORM мы подразумеваем пользовательскую ORM, разработанная вашей компанией для сохранения Сущностей в базу данных. В нашем сценарии специальный ORM код будет реализован с использованием библиотеки [DBAL](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/). + +Согласно [официальной документации](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/introduction.html) The Doctrine Database Abstraction & Access Layer (DBAL) предлагает легкий и тонкий runtime слой вокруг PDO-подобного API и множество дополнительных горизонтальный функций, самоанализ схемы базы данных и манипулирование через API. + +
+ +### Embedded Value (Встроенное Значение) с помощью специальной ORM + +Если мы имеем дело со специальной ORM, использущей шаблон Embedded Value, нам нужно создать поле в таблице Entity для каждого атрибута Объекта Значения. В этом случае для сохранения Сущности `Product` требуется два дополнительных столбца - один для стоимости, второй для кода валюты. + +
```sql CREATE TABLE `products` ( @@ -563,8 +606,12 @@ CREATE TABLE `products` ( price_currency VARCHAR( 3) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` -Если вы используется специальный репозиторий ORM на основе DBAL, назовем его `DbalProductRepository` - -вы должны позаботиться о создании оператора `INSERT`, привязки параметров и выполнении этого метода. + +
+ +Если вы используете специальный репозиторий ORM на основе DBAL, назовем его `DbalProductRepository` - вы должны позаботиться о создании оператора `INSERT`, привязки параметров и выполнении вставки. + +
```php class DbalProductRepository extends DbalRepository implements ProductRepository @@ -584,8 +631,13 @@ class DbalProductRepository extends DbalRepository implements ProductRepository } ``` -После выполнения этого фрагмента кода для создания Сущностей `Product` и сохранения их, каждый столбец -заполняется необходимой информацией: + +
+ +После выполнения этого фрагмента кода для создания Сущностей `Product` и сохранения их, каждый столбец заполняется необходимой информацией: + +
+ ```sql mysql> select * from products \G *************************** 1. row *************************** @@ -595,10 +647,15 @@ price_amount: 999 price_currency: USD 1 row in set (0.00 sec) ``` -Как видите, вы можете сопоставить свои Объекты Значений с параметрами запроса, чтобы сохранить ваши -Объекты Значения в базе данных. Однако, не все так просто, как кажется. Попробуем извлечь сохраненный продукт с -с соотвествующим Объектом Значения `Money`. Обычно подход заключается в выполнении оператора `SELECT` и -возврате новой Сущности: + +
+ +Как видите, вы можете сопоставить свои Объекты Значений с параметрами запроса, чтобы сохранить ваши Объекты Значения в базе данных. Однако, не все так просто, как кажется. Попробуем извлечь сохраненный продукт с соответствующим Объектом Значения `Money`. + +Обычно подход заключается в выполнении оператора `SELECT` и возврате новой Сущности: + +
+ ```php class DbalProductRepository extends DbalRepository implements ProductRepository { @@ -621,40 +678,39 @@ class DbalProductRepository extends DbalRepository implements ProductRepository } ``` -У этого подхода есть некоторые преимущества. Во-первых, вы можете легко прочитать, шаг за шагом, -каждый необходимый элемент. Во-вторых, вы можете выполнять запросы на основе любых атрибутов Объекта Значения. -Наконец, ваша Сущности будет занимать в базе ровно столько места, сколько ей требуется - не больше и не меньше. -Однако использование собственно-написанной ORM имеет свои недостатки. Как объясняется в главе 6 "События домена", -сущности (в представлении агрегата) должны вызывать событие в конструкторе, если ваш домен занят созданием агрегата. -Если использовать оператор `new`, то событие будет вызываться столько раз, сколько раз агрегат будет получен -из базы данных. +
-Это одна из причин почему Doctrine использует внутренние прокси, а так же сериализуещие и -десериализуещие методы для востановления объекта с его атрибутами в определенном состоянии без использования его -конструктора. Сущность должна быть создана с новым оператором только один раз за время своего существования: +У этого подхода есть некоторые преимущества. Во-первых, вы можете легко прочитать, шаг за шагом, каждый необходимый элемент. Во-вторых, вы можете выполнять запросы на основе любых атрибутов Объекта Значения. Наконец, ваша Сущности будет занимать в базе ровно столько места, сколько ей требуется - не больше и не меньше. + +Однако использование собственно-написанной ORM имеет свои недостатки. Как объясняется в главе 6 "События домена", Сущности (в представлении Агрегата) должны вызывать событие в конструкторе, если ваш домен занят созданием агрегата. +Если использовать оператор `new`, то событие будет вызываться столько раз, сколько раз агрегат будет получен из базы данных. + +Это одна из причин почему Doctrine использует внутренние прокси, а так же сериализуещие и десериализуещие методы для востановления объекта с его атрибутами в определенном состоянии без использования его конструктора. Сущность должна быть создана с новым оператором только один раз за время своего существования: + +
> **Конструкторы** -> В конструкторы не обязательно помещать параметры для каждого атрибута объекта. Подумайте о посте в блоге. -> Конструктор может нуждаться в идентификаторе и заголовке; однако, внутри он может так же установить свой атрибут `status` -> значение _Черновик_. При публикации поста необходимо вызвать метод публикации, чтобы -> соответствующим образом изменить его статус и установить дату публикации. - -Если вы все еще хотите создать свой ORM, будьте готовы решить некоторые фундаментальные проблемы, такие как -События, различные конструкторы, Объекты Значений, отложенная загрузка и другие. Вот почему мы рекомендуем -попробовать Doctrine для приложений реализующих DDD. - -Кроме того, в этом случае вам необходимо создать Сущность `DbalProduct`, -которая наследуется от Сущности `Product` и может востанавливать Сущность из базы данных без использования -оператора `new`, а вместо этого использовать статические методы фабрики. - -### Embedded Value (Embeddables) с использованием Doctrine >= 2.5.* -Последним стабильной версией Doctrine на момент написания книги является 2.5, и она поставляется -с поддержкой сопоставления Объектов Значения, что устраняет необходимость делать это -самостоятельно, как в Doctrine 2.4. С декабря 2015, Doctrine также поддерживает сложенные встраиваемые объекты. - -Поскольку классы `Product`, `Money` и `Currency` уже показаны, остается только показать файл настройки -сопоставления Doctrine: +> +> В конструкторы необязательно помещать параметры для каждого атрибута объекта. Подумайте о посте в блоге. Конструктор может нуждаться в идентификаторе и заголовке, однако, внутри он может так же установить свой атрибут `status` +> значение _Черновик_. При публикации поста необходимо вызвать метод публикации, чтобы соответствующим образом изменить его статус и установить дату публикации. + +
+ +Если вы все еще хотите создать свой ORM, будьте готовы решить некоторые фундаментальные проблемы, такие как События, различные конструкторы, Объекты Значений, отложенная загрузка и другие. Вот почему мы рекомендуем попробовать Doctrine для приложений реализующих DDD. + +Кроме того, в этом случае вам необходимо создать Сущность `DbalProduct`, которая наследуется от Сущности `Product` и может восстанавливать Сущность из базы данных без использования оператора `new`, а вместо этого использовать статические методы фабрики. + +
+ +### Embedded Value (Embeddables) с использованием Doctrine >= 2.5.* + +Последним стабильной версией Doctrine на момент написания книги является 2.5, и она поставляется с поддержкой сопоставления Объектов Значения, что устраняет необходимость делать это самостоятельно, как в Doctrine 2.4. С декабря 2015, Doctrine также поддерживает сложенные встраиваемые объекты. + +Поскольку классы `Product`, `Money` и `Currency` уже показаны, остается только показать файл настройки сопоставления Doctrine: + +
+ ```xml ``` + +
+ В сопоставлении продукта мы определяем цену как переменную экземпляра класса `Money`. В то же время, `Money` спроектирован с реализацией класса `Currency`: +
+ ```xml ``` + +
+ Наконец, пришло время показать конфигурацию сопоставления для нашего Объекта Значения `Currency`: + +
+ ```xml ``` -Как видите, приведенный выше код имеет стандартное встраиваемое определение с одним строковым полем, -содержащим код ISO. Этот подход является самым простым способом использования встраиваемых объектов и -гораздо эффективнее. По умолчанию, Doctrine присваиваем имена вашим столбцам, добавляя к ним префикс, используя имя -Объекта Значения. Вы можете изменить это поведение в соответствии с вашими потребностями, отредактировав -атрибут `column-prefix` в xml нотации. + +
+ +Как видите, приведенный выше код имеет стандартное встраиваемое определение с одним строковым полем, содержащим код ISO. Этот подход является самым простым способом использования встраиваемых объектов и гораздо эффективнее. По умолчанию, Doctrine присваивает имена вашим столбцам, добавляя к ним префикс, используя имя Объекта Значения. Вы можете изменить это поведение в соответствии с вашими потребностями, отредактировав атрибут `column-prefix` в xml нотации. + +
## Serialized LOB и специальная ORM -Если поиск по атрибутам Объекта Значения не нужно, можно рассмотреть -еще один шаблон: the Serialize LOB (Сериализованный Большой Объект). + +Если поиск по атрибутам Объекта Значения не нужен, можно рассмотреть +еще один шаблон: the **Serialize LOB** (Сериализованный Большой Объект). Этот шаблон работает путем сериализации всего Объекта Значения в строковый формат, который можно легко сохранить и извлечь. Наиболее существенное различие между этим решением и альтернативой встраивания объекта заключается в том, -что в последнем варианте весь объект занимает лиш одну колонку в таблице. +что в последнем варианте весь объект занимает лишь одну колонку в таблице. + +
```sql -CREATE TABLE ` products` ( +CREATE TABLE `products` ( id INT NOT NULL, name VARCHAR( 255) NOT NULL, price TEXT NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` + +
+ Для сохранения Сущности `Product` при использовании этого подхода, -требуется изменение в `DbalProductRepository`. Объект Значение -`Money` должно быть сереализовано в строку перед сохранением Сущности. +требуется изменение в `DbalProductRepository`. Объект Значения +`Money` должно быть сериализовано в строку перед сохранением Сущности. + +
```php class DbalProductRepository extends DbalRepository implements ProductRepository @@ -770,9 +846,13 @@ class DbalProductRepository extends DbalRepository implements ProductRepository } } ``` -Теперь посмотрим, как наш `Product` представлен в базе данных. -Тип столбца `price` TEXT, в ней хранится сериализованный объект `Money`, -представлющий 9.99USD + +
+ +Теперь посмотрим, как наш `Product` представлен в базе данных. Тип столбца `price` TEXT, в ней хранится сериализованный объект `Money`, представлющий 9.99USD + +
+ ```sql mysql > select * from products \G *************************** 1.row*************************** @@ -785,68 +865,83 @@ Currency":1:{\ s:34:" Ddd\Domain\Model\Currency isoCode";s:3:"USD";}}1 row in set(\ 0.00 sec) ``` -Этот подход работает. Однако он не рекомендуется из-за проблем, -возникающих при рефакторинге классов в вашем коде. Можете ли вы -представить проблемы с которыми столкнемся, если решим переименовать + +
+ +**Этот подход работает. Однако он не рекомендуется из-за проблем,** +**возникающих при рефакторинге классов в вашем коде.** Можете ли вы +представить проблемы с которыми мы столкнемся, если решим переименовать наш класс `Money`? А какие изменения потребуются в базе данных при перемещении класс `Money` в другое пространство имен? Другой сложностью, как говорилось ранее, -является отсутствие возможности запроса по атрибутам Объекта Значения. -Неважно, используете вы Doctrine или нет; написать запрос, чтобы -получить продукты дешевле, скажем, 200 долларов, практически невозможно. +является отсутствие возможности запроса по атрибутам Объекта Значения. Неважно, используете вы Doctrine или нет; написать запрос, чтобы получить продукты дешевле, скажем, 200 долларов, практически невозможно. -Проблема запроса по атрибутам Объекта Значения может быть решена только -при подходе Embedded Values. Однако проблемы рефакторинга могут быть +Проблема запроса по атрибутам Объекта Значения может быть решена только при подходе Embedded Values. Однако проблемы рефакторинга могут быть решены с помощью специальной библиотеки для процессов сериализации. +
+ ### Улучшенная сериализация с помощью JMS Serialize -При сериализации / десериализации нативных PHP стратегий, возникает проблема, -связанная с рефакторингом классов и пространств имен. Один из альтернативных вариантов использовать собственный -механизм сериализации. Однако есть еще один предпочтительный подход: использованием библиотеки -сериализатора с открытым исходным кодом, такую как `JMS Serializer`. Давайте рассмотрим пример -его применения при сериализации объекта `Money`: + +При сериализации/десериализации нативных PHP стратегий, возникает проблема, связанная с рефакторингом классов и пространств имен. Один из альтернативных вариантов использовать собственный механизм сериализации. Однако есть еще один предпочтительный подход: использованием библиотеки сериализатора с открытым исходным кодом, такую как `JMS Serializer`. Давайте рассмотрим пример его применения при сериализации объекта `Money`: + +
+ ```php $myMoney = new Money(999, new Currency('USD')); $serializer = JMS\Serializer\SerializerBuilder::create()->build(); $jsonData = $serializer−>serialize($myMoney, 'json'); ``` + +
+ Процес десериализации объекта так же прост: + +
+ ```php $serializer = JMS\Serializer\SerializerBuilder::create()->build(); // ... $myMoney = $serializer−>deserialize(jsonData, 'Ddd', 'json'); ``` -В этом примере вы можете проводить рефакторинг своего класса `Money` без необходимости обновления базы данных. -`JMS Serializer` может быть использован в множестве других сценариев - например, при работе с REST API. -Важной особенностью является возможность указать, какие атрибуты объекта должн ыбыть пропущены в процессе сериализации - например, пароль. + +
+ +В этом примере вы можете проводить рефакторинг своего класса `Money` без необходимости обновления базы данных. `JMS Serializer` может быть использован в множестве других сценариев - например, при работе с REST API. +Важной особенностью является возможность указать, какие атрибуты объекта должны быть пропущены в процессе сериализации - например, пароль. + +
## Serialized LOB с использвоанием Doctrine + В `Doctrine` существуют различные способы сериализации объектов, для их дальнейшего сохранения. +
+ ### Doctrine Object Mapping Type -`Doctrine` поддерживает шаблон `Serialize LOB`. Существует множество предопределенных типов сопоставления, -которые можно использовать для сопоставления атрибутов Сущности со столбцами базы данных и таблиц. Одним -из таких сопоставлений является тип объекта, который сопоставляет SQL CLOB с PHP объектом при использоании -`serialize()` и `unserialize()`. +`Doctrine` поддерживает шаблон `Serialize LOB`. Существует множество предопределенных типов сопоставления, которые можно использовать для сопоставления атрибутов Сущности со столбцами базы данных и таблиц. Одним +из таких сопоставлений является тип объекта, который сопоставляет SQL CLOB с PHP объектом при использоании `serialize()` и `unserialize()`. + +
Согласно документации [`Doctrine DBAL 2`](https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/basic-mapping.html), тип `object`: ->Отображает и преобразует данные объекта на основе сериализации PHP. Если вам необходимо сохранить точное ->представление данных вашего объекта, вам следует рассмотреть возможность использования этого типа, ->поскольку он использует сериализации для представления точной копии вашего объекта в виде строки в базе данных. ->Значения, извлеченные из базы данных, всегда преобразуются в тип объекта PHP с использованием десериализации ->или null-значение, если данные отсутствуют. + +
+ +> Отображает и преобразует данные объекта на основе сериализации PHP. Если вам необходимо сохранить точное представление данных вашего объекта, вам следует рассмотреть возможность использования этого типа, поскольку он использует сериализации для представления точной копии вашего объекта в виде строки в базе данных. +> Значения, извлеченные из базы данных, всегда преобразуются в тип объекта PHP с использованием десериализации или null-значение, если данные отсутствуют. > ->Этот тип всегда будет привязан к текстовому типу используемой базы данных, поскольку нет способа сохранить ->представление объекта PHP в базе данных в его исходном виде. Кроме того, для этого типа требуется подсказка в виде ->комментария к столбцу SQL, чтобы его можно было корректно восстановить. Doctrine не может правильно востановить объект, ->используя решения, которые не поддерживают комментарии к столбцам, и вместо этого будет ->возвращать текстовое значение. +> Этот тип всегда будет привязан к текстовому типу используемой базы данных, поскольку нет способа сохранить представление объекта PHP в базе данных в его исходном виде. Кроме того, для этого типа требуется подсказка в виде комментария к столбцу SQL, чтобы его можно было корректно восстановить. Doctrine не может правильно востановить объект, используя решения, которые не поддерживают комментарии к столбцам, и вместо этого будет возвращать текстовое значение. > ->Поскольу встроенный тип `text` в PostgreSql не поддерживает NULL байт, при десирализации будет возникать ошибка. ->Решением может служить использование `serialize()/unserialize()` и `base64_encode()/base64_decode()` ->с дальнейшим хранением результата в текстовом поле. +> Поскольку встроенный тип `text` в PostgreSql не поддерживает NULL байт, при десериализации будет возникать ошибка. +> Решением может служить использование `serialize()/unserialize()` и `base64_encode()/base64_decode()` с дальнейшим хранением результата в текстовом поле. + +
Рассмотрим возможное XML сопоставление для Сущности `Product` с использованием типа `object` + +
+ ```xml deserialize(jsonData, 'Ddd', 'json'); ``` + +
+ Ключевым дополнением является `type="object`, которое сообщает Doctrine, что мы собираемся использовать сопоставление объектов. Рассмотрим, как мы можем создать и сохранить Сущность `Product`, используя Doctrine: + +
+ ```php // ... $em−>persist($product); $em−>flush($product); ``` -Проверим, если мы теперь запросим нашу Сущность `Product` из базы данных, вернется ли он нам в корректном -состоянии: + +
+ +Проверим, если мы теперь запросим нашу Сущность `Product` из базы данных, вернется ли она нам в корректном состоянии: + +
+ ```php // ... $repository = $em->getRepository('Ddd\\Domain\\Model\\Product'); @@ -905,28 +1011,44 @@ class Ddd\Domain\Model\Product#177 (3) { } * / ``` -И последнее, но неменее важно, документация гласит: ->Типы `object` сравниваются по ссылке, а не по значению. Doctrine обновляет это значение, если ссылка изменилась, и, ->следовательно, ведет себя так, как будто объекты являются неизменяемыми. -Этот подход страдает от тех же проблем рефаторинга, что и рассмотренная ранее специальная ORM. Сопоставление типа -`object` использует `serialize/unserialize`. Как насчет использования собственной сериализации? +
+ +И последнее, но не менее важно, документация гласит: + +
+ +> Типы `object` сравниваются по ссылке, а не по значению. Doctrine обновляет это значение, если ссылка изменилась, и, следовательно, ведет себя так, как будто объекты являются неизменяемыми. + +
+ +Этот подход страдает от тех же проблем рефаторинга, что и рассмотренная ранее специальная ORM. Сопоставление типа `object` использует `serialize/unserialize`. Как насчет использования собственной сериализации? + +
### Doctrine Пользовательские Типы + Другим вариантом является сохранение Объекта Значения с помощью пользовательского типа Doctrine. -Пользовательский Тип добавляет новый тип сопоставления в Doctrine - токой, который описывает пользовательское -преобразование между полем Сущности и его представлением в базе данных. +Пользовательский Тип добавляет новый тип сопоставления в Doctrine - такой, который описывает пользовательское преобразование между полем Сущности и его представлением в базе данных. В [документации](https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/cookbook/custom-mapping-types.html#custom-mapping-types) написано: ->Простое преобразование всех типов для хранение в базе данных используемое в Doctrine вовсе не универсально. ->Вы можете определить свои собственные Doctrine Mapping Types унаследовавшись от `Doctrine\DBAL\Types\Type`. ->Вы должны реализовать 4 метода, чтобы все заработало. + +
+ +> Простое преобразование всех типов для хранение в базе данных используемое в Doctrine вовсе не универсально. +> Вы можете определить свои собственные Doctrine Mapping Types унаследовавшись от `Doctrine\DBAL\Types\Type`. +> Вы должны реализовать 4 метода, чтобы все заработало. + +
Тип `object` при сериализации используется такая информация как класс объекта, что затрудняет безопасный рефакторинг. Давайте попробуем улучшить это решение. Подумаем о собственном процессе сериализации, который может устранить проблему. Одним из таких способов может быть сохранение Объекта Значения `Money` в базе данных в виде строки, закодированной в форматы amount|isoCode. + +
+ ```php use Ddd\Domain\Model\Currency; use Ddd\Domain\Model\Money; @@ -936,6 +1058,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; class MoneyType extends TextType { const MONEY = 'money'; + public function convertToPHPValue( $value, AbstractPlatform $platform @@ -960,20 +1083,27 @@ class MoneyType extends TextType ] ); } + public function getName() { return self::MONEY; } } ``` -Используя Doctrine, вам необходимо зарегистрироваться все пользовательские типы. Обычно для этого -используется фабрика `EntityManagerFactory` которая пораждает `EntityManager`. + +
+ +Используя Doctrine, вам необходимо зарегистрировать все пользовательские типы. Обычно для этого используется фабрика `EntityManagerFactory` которая пораждает `EntityManager`. Кроме того, вы можете выполнить этот шаг на этапе загрузки приложения: + +
+ ```php use Doctrine\DBAL\Types\Type; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Tools\Setup; + class EntityManagerFactory { public function build() @@ -998,7 +1128,13 @@ class EntityManagerFactory } } ``` + +
+ Теперь нам нужно указать в настройках сопоставления, что мы хотим использовать наш пользовательский тип: + +
+ ```xml @@ -1014,7 +1150,13 @@ class EntityManagerFactory ``` + +
+ Давайте проверим базу данных чтобы увидеть как сохраняется `Money` в этом случае. + +
+ ```mysql mysql> select * from products \G *************************** 1. row*************************** @@ -1023,9 +1165,8 @@ name: Domain-Driven Design in PHP price: 999|USD 1 row in set (0.00 sec) ``` -Этот подход явно лучше про сравнению с предыдущим с точки зрения будущего рефаторинга. -Однако возможности поиска по значения остаются ограниченными из-за формата столбца. -С помощью пользовательских типов Doctrine вы можете немного улучшить ситуацию, но это -все еще не лучший вариантов для построения DQL запросов. С дополнительной информацией можете -ознакомиться на странице [Custom Mapping Types](https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/cookbook/custom-mapping-types.html#custom-mapping-types) + +
+ +Этот подход явно лучше про сравнению с предыдущим с точки зрения будущего рефаторинга. Однако возможности поиска по значения остаются ограниченными из-за формата столбца. С помощью пользовательских типов Doctrine вы можете немного улучшить ситуацию, но это все еще не лучший вариантов для построения DQL запросов. С дополнительной информацией можете ознакомиться на странице [Custom Mapping Types](https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/cookbook/custom-mapping-types.html#custom-mapping-types)