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

DDD в D, где D это Django

Avatar for MaxST MaxST
December 04, 2023

DDD в D, где D это Django

Каждый раз, начиная новый проект, мы хотим сделать все хорошо. Но через некоторое время проект превращается в легаси, которое тяжело изменять.
- Можно ли с этим что-то сделать?
- Что такое DDD и как он может вам помочь?
- С чего начать?
На эти вопросы ответим в докладе.

Avatar for MaxST

MaxST

December 04, 2023

Other Decks in Programming

Transcript

  1. Обо мне Столпасов Максим Teamlead Backend МП Чижик С 2006

    автоматизирую предприятия С 2012 ❤ Python C 2019 в Х5 Мое хобби моя работа Telegram: @Max_S_T E-mail: [email protected]
  2. 4 ` Я каждый раз собираюсь сделать хорошо И каждый

    раз происходит одно и тоже Новый проект
  3. Что такое DDD? Domain-Driven Design (DDD) – это методология разработки

    программного обеспечения, которая ставит акцент на моделировании предметной области 9
  4. Что такое DDD? Domain-Driven Design (DDD) – это методология разработки

    программного обеспечения, которая ставит акцент на моделировании предметной области 10 DDD помогает победить сложность системы Множество путей реализации DDD – каждая команда понимает его по своему
  5. ` Преимущества DDD Преимущества использования подхода DDD 11 Когда читаешь

    о DDD Более легкое понимание кода Легче вносить изменения Легче находить общий язык Больше гибкости и масштабируемости Улучшение коммуникации между разработчиками и бизнесом
  6. ` Преимущества DDD Когда читаешь о DDD Когда дело доходит

    до практики Преимущества использования подхода DDD 12 Высокий порог входа Мало примеров на python Еще меньше инфы на русском
  7. Основные принципы DDD Стратегическая часть Тактическая часть 13 Универсальный язык

    Ограниченный контекст Карта контекстов Антикоррупционные слои Агрегаты Объекты значения Репозитории Службы … …
  8. Модульный монолит Модульный монолит - это подход, при котором приложение

    разбивается на модули, каждый из которых отвечает за определенную функциональность 15
  9. Вызовы При использовании подхода DDD в модульном монолите могут возникать

    вызовы, связанные с : 16 Организацией кода Коммуникацией между модулями Общей архитектурой приложения
  10. 18 Пример применения DDD в python Начнем со структуры контекста

    и затем будем погружаться ниже в реализацию каждого слоя
  11. Пример применения DDD в python 19 Структура: интерфейсный слой name_context

    ├── admin # содержит модули, связанные с административными функциями приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении
  12. name_context ├── admin # содержит модули, связанные с административными функциями

    приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. Пример применения DDD в python 20 Структура: инфраструктурный слой
  13. name_context ├── admin # содержит модули, связанные с административными функциями

    приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. Пример применения DDD в python 21 Структура: сервисный слой (предохранительный, интеграционный) name_context ├── admin # содержит модули, связанные с административными функциями приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. ├── services # содержит модули, связанные с интеграцией с внешними сервисами. │ ├── other_api_client # содержат модули, связанные с интеграцией с внешними API. │ ├── goods_api # -/- │ ├── handlers # содержит обработчики событий других контекстов и транслирует # события текущего контекста во внешнии.
  14. name_context ├── admin # содержит модули, связанные с административными функциями

    приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. Пример применения DDD в python 22 Структура: прикладной слой name_context ├── admin # содержит модули, связанные с административными функциями приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. ├── services # содержит модули, связанные с интеграцией с внешними сервисами. │ ├── other_api_client # содержат модули, связанные с интеграцией с внешними API. │ ├── goods_api # -/- │ ├── handlers # содержит обработчики событий других контекстов и транслирует # события текущего контекста во внешнии. name_context ├── admin # содержит модули, связанные с административными функциями приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. ├── services # содержит модули, связанные с интеграцией с внешними сервисами. │ ├── other_api_client # содержат модули, связанные с интеграцией с внешними API. │ ├── goods_api # -/- │ ├── handlers # содержит обработчики событий других контекстов и транслирует │ │ # события текущего контекста во внешнии. ├── application # содержит модули, связанные с бизнес-логикой приложения. │ └── stories # содержит бизнес-логику в виде историй, представляющих сценарии # использования в приложении.
  15. name_context ├── admin # содержит модули, связанные с административными функциями

    приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. Пример применения DDD в python 23 Структура: Доменный слой name_context ├── admin # содержит модули, связанные с административными функциями приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. ├── services # содержит модули, связанные с интеграцией с внешними сервисами. │ ├── other_api_client # содержат модули, связанные с интеграцией с внешними API. │ ├── goods_api # -/- │ ├── handlers # содержит обработчики событий других контекстов и транслирует # события текущего контекста во внешнии. name_context ├── admin # содержит модули, связанные с административными функциями приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. ├── services # содержит модули, связанные с интеграцией с внешними сервисами. │ ├── other_api_client # содержат модули, связанные с интеграцией с внешними API. │ ├── goods_api # -/- │ ├── handlers # содержит обработчики событий других контекстов и транслирует │ │ # события текущего контекста во внешнии. ├── application # содержит модули, связанные с бизнес-логикой приложения. │ └── stories # содержит бизнес-логику в виде историй, представляющих сценарии # использования в приложении. name_context ├── admin # содержит модули, связанные с административными функциями приложения. ├── api # содержит модули, связанные с обработкой API интерфейса. ├── tasks # содержит определения задач, выполняемых асинхронно в приложении. ├── infrastructure # содержит модули, связанные с реализацией инфраструктурных механизмов. │ ├── mappers # содержит реализацию мапперов. │ ├── repositories # содержит классы-репозитории, отвечающие за работу с хранилищем данных. │ ├── handlers # содержит обработчики событий. ├── services # содержит модули, связанные с интеграцией с внешними сервисами. │ ├── other_api_client # содержат модули, связанные с интеграцией с внешними API. │ ├── goods_api # -/- │ ├── handlers # содержит обработчики событий других контекстов и транслирует │ # события текущего контекста во внешнии. ├── application # содержит модули, связанные с бизнес-логикой приложения. │ └── stories # содержит бизнес-логику в виде историй, представляющих сценарии │ # использования в приложении. ├── domain # содержит модули, относящиеся к бизнес-логике приложения, а также │ │ # объединяющие сущности, сервисы и репозитории. │ ├── aggregates # содержит агрегаты, отображающие комплексные бизнес-сущности. │ ├── events # содержит классы событий, позволяющих прослеживать изменения │ │ # состояния бизнес-сущностей. │ ├── handlers # содержит обработчики событий и изменения состояния бизнес-сущностей. │ ├── mappers # содержит мапперы, отображающие данные из одного │ │ # представления в другое. │ ├── typings| # содержит интерфейсы(протоколы), используемые в бизнес-логике. │ │ interfaces| │ │ repositories │ └── value_objects # содержит классы, представляющие простые бизнес-сущности (например, деньги, адреса).
  16. Инструменты для работы с DDD в python Для работы с

    DDD в python можно использовать различные инструменты и библиотеки: 25 Django - Web Django-ninja - API Pydantic - aggregates, events, schemas Dependencies - DI Stories - User stories, use cases Classes - mappers А также линтеры, форматеры и т.д.
  17. 26 Легко переделать Реализация слоев Интерфейсный слой DI Инфраструктурный слой

    Мапперы Прикладной слой @router.get( '/basket_info/', response={ **default_responses, HTTPStatus.OK: InfoBasketSchemaOut, HTTPStatus.NOT_FOUND: BasketNotFoundErrorMessage, }, url_name='basket_info', ) def get_basket_info(request: HttpRequest): """Информация об активной корзине. """ return GetBasketContainer.run(user_id=request.user.id)
  18. 27 Легко переделать @router.get( '/basket_info/', response={ **default_responses, HTTPStatus.OK: InfoBasketSchemaOut, HTTPStatus.NOT_FOUND:

    BasketNotFoundErrorMessage, }, url_name='basket_info', ) def get_basket_info(request: HttpRequest): """Информация об активной корзине. """ return GetBasketContainer.run(user_id=request.user.id) Обращаются к контейнерам Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной слой Не должны знать о внутренней логике
  19. 28 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой class GetBasketContainer(DefaultInjector): story = GetBasketStory return_attribute = 'basket_info' basket_repo = BasketRepo basket_creator = CreateBasketContainer.run Мостик в логику Пользовательская история, которая запускается контейнером
  20. 29 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой class GetBasketContainer(DefaultInjector): story = GetBasketStory return_attribute = 'basket_info' basket_repo = BasketRepo basket_creator = CreateBasketContainer.run Мостик в логику Пользовательская история, которая запускается контейнером Атрибут из состояния пользовательской истории
  21. 30 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой class GetBasketContainer(DefaultInjector): story = GetBasketStory return_attribute = 'basket_info' basket_repo = BasketRepo basket_creator = CreateBasketContainer.run Зависимости нашей пользовательской истории backet_creator сам является контейнером Объединяет компоненты: Репозиторий
  22. 31 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой class BasketRepo(IBasketRepo): def get(self, basket_id: BasketId) -> Basket | None: obj = BasketModel.objects.filter(id=basket_id).first() return to_basket(obj) def get_by_user(self, user_id: UserId) -> Basket | None: obj = BasketModel.objects.filter(user_id=user_id).first() return to_basket(obj) Репозитории наследуются от интерфейсов
  23. 32 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой class BasketRepo(IBasketRepo): def get(self, basket_id: BasketId) -> Basket | None: obj = BasketModel.objects.filter(id=basket_id).first() return to_basket(obj) def get_by_user(self, user_id: UserId) -> Basket | None: obj = BasketModel.objects.filter(user_id=user_id).first() return to_basket(obj) Способы получения наших агрегатов из системы хранения Методов не должно быть много
  24. 33 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой class BasketRepo(IBasketRepo): def get(self, basket_id: BasketId) -> Basket | None: obj = BasketModel.objects.filter(id=basket_id).first() return to_basket(obj) def get_by_user(self, user_id: UserId) -> Basket | None: obj = BasketModel.objects.filter(user_id=user_id).first() return to_basket(obj) Репозиторий всегда возвращает агрегат to_basket – маппер, который используют для приведения к агрегату
  25. 34 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой # basket/domain/mappers.py from classes import typeclass @typeclass def to_basket(_) -> Basket: """Приведение к агрегату Basket.""" @to_basket.instance(dict) def _(d: dict) -> Basket: return Basket.parse_obj(d) # basket/infrastructure/mappers.py @to_basket.instance(BasketModel) def _to_basket(basket: BasketModel) -> Basket: return Basket( basket_id=getattr(basket, 'basket_id', getattr(basket, 'id', None)), user_id=getattr(basket, 'customer_id', None), … ) Маппинг простых типов тоже реализуем в домене В домене объявляем точку входа в маппер
  26. 35 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой # basket/domain/mappers.py from classes import typeclass @typeclass def to_basket(_) -> Basket: """Приведение к агрегату Basket.""" @to_basket.instance(dict) def _(d: dict) -> Basket: return Basket.parse_obj(d) # basket/infrastructure/mappers.py @to_basket.instance(BasketModel) def _to_basket(basket: BasketModel) -> Basket: return Basket( basket_id=getattr(basket, 'basket_id', getattr(basket, 'id', None)), user_id=getattr(basket, 'customer_id', None), … ) Вызываем to_basket(dict(…)) Используем to_basket из basket.domain.mappers Маппиг простых типов тоже реализуем в домене В домене объявляем точку входа в маппер
  27. 36 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой # basket/domain/mappers.py from classes import typeclass @typeclass def to_basket(_) -> Basket: """Приведение к агрегату Basket.""" @to_basket.instance(dict) def _(d: dict) -> Basket: return Basket.parse_obj(d) # basket/infrastructure/mappers.py @to_basket.instance(BasketModel) def _to_basket(basket: BasketModel) -> Basket: return Basket( basket_id=getattr(basket, 'basket_id', getattr(basket, 'id', None)), user_id=getattr(basket, 'customer_id', None), … ) Вызываем to_basket(BasketModel(…)) Вызываем to_basket(dict(…)) Используем to_basket из basket.domain.mappers Маппиг простых типов тоже реализуем в домене В домене объявляем точку входа в маппер
  28. 37 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой @dataclass(frozen=True) class GetBasketStory(Story): """История получения корзины.""" I.load_basket I.create_basket_if_not I.basket_recalculation I.check_ready_for_orders class State(BaseState): # Аргументы user_id: UserId # Переменные basket_info: BasketInfo | None def create_basket_if_not(self, state: State) -> None: """Создание корзины, если её нет.""" if not state.basket_info: state.basket_info = self.basket_creator(user_id=state.user_id) … basket_repo: IBasketRepo basket_creator: IBasketCreator Хранится вся основная логика Есть договоренность порядка расположения элементов, чтобы было проще читать бизнес- логику
  29. 38 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой @dataclass(frozen=True) class GetBasketStory(Story): """История получения корзины.""" I.load_basket I.create_basket_if_not I.basket_recalculation I.check_ready_for_orders class State(BaseState): # Аргументы user_id: UserId # Переменные basket_info: BasketInfo | None def create_basket_if_not(self, state: State) -> None: """Создание корзины, если её нет.""" if not state.basket_info: state.basket_info = self.basket_creator(user_id=state.user_id) … basket_repo: IBasketRepo basket_creator: IBasketCreator Название класса дает понимание про что это
  30. 39 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой @dataclass(frozen=True) class GetBasketStory(Story): """История получения корзины.""" I.load_basket I.create_basket_if_not I.basket_recalculation I.check_ready_for_orders class State(BaseState): # Аргументы user_id: UserId # Переменные basket_info: BasketInfo | None def create_basket_if_not(self, state: State) -> None: """Создание корзины, если её нет.""" if not state.basket_info: state.basket_info = self.basket_creator(user_id=state.user_id) … basket_repo: IBasketRepo basket_creator: IBasketCreator Шаги выполнения, которые дают нам понимание того, что происходит Название класса дает понимание про что это
  31. 40 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой @dataclass(frozen=True) class GetBasketStory(Story): """История получения корзины.""" I.load_basket I.create_basket_if_not I.basket_recalculation I.check_ready_for_orders class State(BaseState): # Аргументы user_id: UserId # Переменные basket_info: BasketInfo | None def create_basket_if_not(self, state: State) -> None: """Создание корзины, если её нет.""" if not state.basket_info: state.basket_info = self.basket_creator(user_id=state.user_id) … basket_repo: IBasketRepo basket_creator: IBasketCreator Шаги выполнения, которые дают нам понимание того, что происходит Название класса дает понимание про что это Состояние истории: какие артефакты рождаются в процессе выполнения
  32. 41 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой @dataclass(frozen=True) class GetBasketStory(Story): """История получения корзины.""" I.load_basket I.create_basket_if_not I.basket_recalculation I.check_ready_for_orders class State(BaseState): # Аргументы user_id: UserId # Переменные basket_info: BasketInfo | None def create_basket_if_not(self, state: State) -> None: """Создание корзины, если её нет.""" if not state.basket_info: state.basket_info = self.basket_creator(user_id=state.user_id) … basket_repo: IBasketRepo basket_creator: IBasketCreator Шаги выполнения, которые дают нам понимание того, что происходит Название класса дает понимание про что это Состояние истории: какие артефакты рождаются в процессе выполнения Реализация шагов: что происходит внутри каждого шага
  33. 42 Реализация слоев Интерфейсный слой DI Инфраструктурный слой Мапперы Прикладной

    слой @dataclass(frozen=True) class GetBasketStory(Story): """История получения корзины.""" I.load_basket I.create_basket_if_not I.basket_recalculation I.check_ready_for_orders class State(BaseState): # Аргументы user_id: UserId # Переменные basket_info: BasketInfo | None def create_basket_if_not(self, state: State) -> None: """Создание корзины, если её нет.""" if not state.basket_info: state.basket_info = self.basket_creator(user_id=state.user_id) … basket_repo: IBasketRepo basket_creator: IBasketCreator Шаги выполнения, которые дают нам понимание того, что происходит Название класса дает понимание про что это Состояние истории: какие артефакты рождаются в процессе выполнения Реализация шагов: что происходит внутри каждого шага Зависимости нашей истории
  34. Коммуникация между модулями/контекстами EventBroker 46 Используем как обработчик событий на

    глобальном уровне и в контекстах class EventBroker: __slots__ = ('_event_handlers',) def __init__(self) -> None: self._event_handlers: dict[Callable[[Event], bool], set[Callable[[Event], None]]] = {} def __call__(self, event: Event) -> None: self.publish(event) def subscribe(self, event_predicate: Callable[[Event], bool], subscriber: Callable[[Event], None]) -> None: """Подписка на события.""" subscribers = self._event_handlers.setdefault(event_predicate, set()) subscribers.add(subscriber) def register(self, event_predicate: Callable[[Event], bool]) -> Callable: """Зарегистрировать подписку обработчика на события.""" def decorator(func: Callable[[Event], None]) -> Callable[[Event], None]: self.subscribe(event_predicate, func) return func return decorator def unsubscribe(self, event_predicate: Callable[[Event], bool], subscriber: Callable[[Event], None]) -> None: """Отписаться от события.""" if subscribers := self._event_handlers.get(event_predicate): subscribers.discard(subscriber) def publish(self, event: Event) -> None: """Отправить событие всем подписчикам. Каждый подписчик получит каждое событие только один раз, даже если на него было подписано несколько раз, возможно, с разными предикатами. """ matching_handlers: set[Callable[[Event], None]] = set() for event_predicate, handlers in self._event_handlers.items(): if event_predicate(event): matching_handlers |= handlers if not matching_handlers: raise NotImplementedError(f'Подходящие обработчики событий не найдены. {event!r}') for handler in matching_handlers: handler(event) def instance(self, obj_type: type[Any] | tuple[type[Any]]) -> Callable: _type = obj_type if obj_type is not None else type(None) def decorator(func: Callable[[Event], None]) -> Callable[[Event], None]: self.subscribe(lambda e: isinstance(e, _type), func) return func return decorator
  35. Коммуникация между модулями/контекстами EventBroker 47 Используем как обработчик событий на

    глобальном уровне и в контекстах class EventBroker: def __call__(self, event: Event) -> None: self.publish(event) def instance(self, obj_type: type[Any] | tuple[type[Any]]) -> Callable: _type = obj_type if obj_type is not None else type(None) def decorator(func: Callable[[Event], None]) -> Callable[[Event], None]: self.subscribe(lambda e: isinstance(e, _type), func) return func return decorator
  36. EventBroker # core.py EVENT_BROKER = EventBroker() 48 # some_context/domain/handlers.py handle_event

    = EventBroker() Глобальный уровень (Message bus) Контекст (Обработка локальных событий)
  37. Трансляция 49 Вылавливаем событие # order/services/order.py from core import EVENT_BROKER

    from order.domain import CanceledOrderEvent, handle_event handle_event.instance(CanceledOrderEvent)(EVENT_BROKER)
  38. # order/services/order.py from core import EVENT_BROKER from order.domain import CanceledOrderEvent,

    handle_event handle_event.instance(CanceledOrderEvent)(EVENT_BROKER) Трансляция 50 Вылавливаем событие Транслируем событие на глобальный уровень
  39. # payment/services/order.py from core import EVENT_BROKER from order.domain import CanceledOrderEvent

    from payment.domain import CancelPaymentEvent, handle_event @EVENT_BROKER.instance(CanceledOrderEvent) def handle_cancel_payment(event: CanceledOrderEvent) -> None: handle_event(CancelPaymentEvent(order_id=event.order_id)) Трансляция 51 Вылавливаем событие из очереди
  40. Трансляция 52 Предохранительный слой (защищает контекст оплат от языка контекста

    заказа): Преобразовываем событие контекста заказа в событие контекста оплат Вылавливаем событие из очереди # payment/services/order.py from core import EVENT_BROKER from order.domain import CanceledOrderEvent from payment.domain import CancelPaymentEvent, handle_event @EVENT_BROKER.instance(CanceledOrderEvent) def handle_cancel_payment(event: CanceledOrderEvent) -> None: handle_event(CancelPaymentEvent(order_id=event.order_id))
  41. Обработка 53 # payment/infrastructure/handlers.py from payment.domain import CancelPaymentEvent, handle_event from

    .repositories import PaymentRepo @handle_event.instance(CancelPaymentEvent) def _(event: CancelPaymentEvent) -> None: repo = PaymentRepo() payment = repo.get_by_order_id(event.order_id) payment.cancel() repo.save(payment) Вылавливаем событие
  42. # payment/infrastructure/handlers.py from payment.domain import CancelPaymentEvent, handle_event from .repositories import

    PaymentRepo @handle_event.instance(CancelPaymentEvent) def _(event: CancelPaymentEvent) -> None: repo = PaymentRepo() payment = repo.get_by_order_id(event.order_id) payment.cancel() repo.save(payment) Обработка 54 Получаем агрегат оплаты по заказу из события
  43. # payment/infrastructure/handlers.py from payment.domain import CancelPaymentEvent, handle_event from .repositories import

    PaymentRepo @handle_event.instance(CancelPaymentEvent) def _(event: CancelPaymentEvent) -> None: repo = PaymentRepo() payment = repo.get_by_order_id(event.order_id) payment.cancel() repo.save(payment) Обработка 55 repo.save(payment) Сохранение агрегата payment.cancel() Вызов доменного метода отмены
  44. Рекомендации по применению DDD в модульном монолите Использовать четкую структуру

    каталогов Использовать единообразный язык Установить правильные границы между модулями и слоями 57
  45. Примеры проектов с использованием DDD в python Некоторые из проектов,

    использующих подход DDD в python: 58 python-ddd – Хороший пример реализаций DDD паттернов
  46. Примеры проектов с использованием DDD в python Некоторые из проектов,

    использующих подход DDD в python: 59 python-ddd – Хороший пример реализаций DDD паттернов classic_demo_medium_difficulty – есть хорошие идеи
  47. Примеры проектов с использованием DDD в python Некоторые из проектов,

    использующих подход DDD в python: 60 python-ddd – Хороший пример реализаций DDD паттернов classic_demo_medium_difficulty – есть хорошие идеи py_assimilator – Очень симпатичные реализации паттернов + CRUD patterns
  48. Примеры проектов с использованием DDD в python Некоторые из проектов,

    использующих подход DDD в python: 61 python-ddd – Хороший пример реализаций DDD паттернов classic_demo_medium_difficulty – есть хорошие идеи py_assimilator – Очень симпатичные реализации паттернов + CRUD patterns user_service – Очень крутой пример реального сервиса Чижик 🐥
  49. Выводы Значительно улучшает архитектуру, разработку и обслуживание приложения Способствует достижению

    бизнес-целей Способствует более эффективной работе команды разработчиков 62 Использование подхода DDD в приложении в рамках модульного монолита:
  50. А теперь настоящие выводы Тот кто слышал DDD – изучите

    Тот кто познал дзен DDD – научите Тот кто уже использует DDD – углубляйтесь в его понимании 63