Владимир Хориков - Domain-driven design: Cамое важное

Sdílet
Vložit
  • čas přidán 15. 02. 2021
  • Ближайшая конференция - DotNext 2024, 10 - 11 сентября, Москва + online
    Подробности и билеты: jrg.su/x2GKnA
    - -
    Владимиру хотелось бы показать самые важные на его взгляд части DDD с примерами на C#. Цель - дать обзор основных вещей из DDD, чтобы зритель смог быстро начать ориентироваться в них и применять на практике.
  • Věda a technologie

Komentáře • 115

  • @user-lb9rz2jg9l
    @user-lb9rz2jg9l Před rokem +31

    Отличный доклад - редко когда видно, что человек понимает о чем говорит, а значит и может донести мысль до слушателя

  • @slikeiv4477
    @slikeiv4477 Před 2 lety +38

    шикарный доклад и огромное спасибо Владимиру за талант понятно доносить материал

  • @xonicov
    @xonicov Před rokem +22

    Владимир, Вы потрясающий докладчик! Давно не слушал настолько крутого доклада!

  • @antonshinkevich7019
    @antonshinkevich7019 Před rokem +27

    Тайм-коды:
    2:32 Начало. План лекции
    3:20 Основные принципы DDD. Ограниченные контексты (bounded context). Problem space, solution space
    10:55 Единый язык (ubiquitous language)
    15:58 Фокус на доменной модели. Многослойные архитектуры
    21:51 Разница между анемичной и богатой моделью
    23:32 Богатая модель = Инкапсулированная модель. Про понятие инкапсуляции
    27:31 (Не) анемичная доменная модель в рамках функционального программирования
    30:17 Рефакторинг анемичной модели через два шага. Строгая типизация
    34:15 Уменьшение количества методов, которые изменяют состояние класса
    41:06 Вопрос: влияние единого языка на развитие проекта
    42:38 Изоляция доменной модели
    49:21 Взаимосвязь между инкапсуляцией и изоляцией доменной модели
    53:19 DDD трилемма. Изоляция > Инкапсуляция > Быстродействие
    59:57 Конец доклада. Вопросы
    1:00:56 Как склонить коллег к переходу к богатой модели от анемичной?
    1:02:20 Интеграция между Entity Framework и DDD. Наследование от POCO (или DTO?)
    1:04:55 Что-то там про передачу интерфейса
    1:06:41 Разница между subdomain и bounded context
    1:09:00 Рефакторинг при database first approach
    1:11:30 Стоит ли использовать DDD прототипирование?

  • @namesecondname663
    @namesecondname663 Před 3 lety +10

    Супер! Отличный доклад, спасибо.

  • @emotional_stuff
    @emotional_stuff Před 23 dny

    спасибо за практические примеры, Очень важный момент

  • @Chat-Mayevskogo
    @Chat-Mayevskogo Před 2 lety +14

    Пока лучшее что я видел по DDD для чайников (коим и являюсь).
    33 минута, по этой логике и Name нужно обернуть в класс ведь можно указать и пробел или символ и т.п. Важно найти баланс но с другой стороны обернуть в класс не сложно оставив различные валидации и усовершенствования класса своим потомкам)))

  • @alexanderinkognito8379
    @alexanderinkognito8379 Před 2 lety +10

    Книга по тестированию супер! рекомендую всем, must read

  • @borisnikulitsa695
    @borisnikulitsa695 Před 2 lety +5

    Пример по моему подобран удачно.
    Сразу становится понятно про разделение контекста в случае схожих сущностей (Customer и Product)
    Спасибо за доклад

  • @learning867
    @learning867 Před 11 měsíci +8

    56:00 для таких инвариантов придется знание о доменной модели выносить в бд - уникальный индекс на email. И тогда при вставке бд выдаст ошибку, и ее можно будет обработать на доменном уровне.
    Ни один из описанных в видео способов не явлется рабочим в конкурентной среде. Если два пользователя решили одновременно сменить email на другой одинаковый, то выборка из бд и проверка в памяти потенциально могут у обоих завершиться успешно. И оба вставят в бд один и тот же email.
    Если вынести проверку на уровень бд, то и код в доменной модели станет проще.
    Да, можно сказать, что мы переносим проверку инварианиа на инфраструктурный уровень - уровень бд.
    Но вообще-то бд является отражением наших сущностей в доменной модели.
    Мы не переносим эту логику в реопозитрий. Мы переносим ее в саму бд - место хранения инфы о нашем домене.
    Ну и это единственный адекватный рабочий способ. Ну либо можно брать блокировки (или распределеоные блокировки), но вот это уже перенос сохранения инварианта в инфраструктуру

    • @ilmakeyouone
      @ilmakeyouone Před měsícem +1

      а если у нас подтверждение на изменение пользовательского email зависит не только от его уникальности, но и от других факторов (например подтверждение модератором)? и количество таких условий может множится и бизнес логика может проходить сквозь всю структуру. тогда мы опять попадаем в анемичную модель.

  • @user-fg6ng7ej6w
    @user-fg6ng7ej6w Před 8 měsíci

    очень толковый доклад, спасибо

  • @theeasywaytr4293
    @theeasywaytr4293 Před rokem

    Владимир отличный пример привел! Спасибо за доклад!

  • @judexmars4214
    @judexmars4214 Před 3 měsíci

    Интересный подход. Вроде как и много сказанное здесь звучит логично и правильно, но и очень непривычно. Спасибо за доклад

  • @nikolay4362
    @nikolay4362 Před 7 měsíci

    на редкость хорошая лекция

  • @natasha1059
    @natasha1059 Před rokem

    Очень емко, точно, мне все стало понятно, спасибо!

  • @ilya3894
    @ilya3894 Před rokem

    Очень хороший доклад!

  • @user-rh1ty5rx3h
    @user-rh1ty5rx3h Před 8 měsíci

    Спасибо, отличный доклад

  • @bfdhtfyjhjj
    @bfdhtfyjhjj Před 2 lety

    Классный доклад!

  • @hezymal9109
    @hezymal9109 Před 3 lety +2

    Супер спасибо

  • @alekseyshibayev5243
    @alekseyshibayev5243 Před rokem +4

    Спасибо коллегам из мира java для windows 😊
    В пет проекте так и делал, пользуясь С и О принципами солид, не догадываясь про ДДД. Оказывается ДДД расширяет эти принципы. Очень похоже про те штуки, за которые топил Егор Бугаенко. Единственное не согласен, что надо лепить все в один класс. Я бы выделил емеил и статус в отдельную бизнес сущность, и создал класс ответственный за эту сущность и пусть он ее меняет.
    Книгу записал в todo.

  • @zscauer
    @zscauer Před 8 měsíci

    очень крутой доклад

  • @IvanenkoStepan
    @IvanenkoStepan Před 7 měsíci

    Вангую, это видео наберёт больше всего просмотров на канале.

  • @-dubok-
    @-dubok- Před rokem +8

    Мне кажется, что Владимир на 41:13 приуменьшает роль DDD в проекте. Потому что, на мой взгляд, хотя я только его ещё изучаю, DDD даёт не только единый язык и взаимопонимание, но и в целом раскладывает всё в проекте по полочкам, позволяя легко в нём ориентироваться, легко его поддерживать и вносить изменения. Благодаря DDD избегается превращение проекта в то, что адепты DDD называют «большим комом грязи». А это уже очень полезно и в разы улучшает поддерживаемость кода и экономит по-настоящему огромные ресурсы компании, обеспечивая при этом высокую функциональность, feature-richness продукта. По-моему, DDD в корне меняет всё, он просто переворачивает всю игру с головы на ноги, раскладывая все яйца по своим корзинам. DDD - это must have на любых проектах, связанных с реальным миром, реальными предметными областями и бизнесом в частности. DDD - это инструкция к тому, как правильно писать код, как сделать его чистым и логичным, и никак не меньше.
    Ну а в целом доклад очень крутой, всё чётко и по полочкам. Особенно понравилось как показано превращение анемичной модели в богатую. Вообще, когда что-то на примерах показывается - это очень хорошо воспринимается всегда. За это прям большое спасибо!
    А насчёт трилеммы. Лично я не думаю, что использование репозитория в модели - это страшно, потому что репозиторий - это основа любой программы, так как он сохраняет стейт, загружает его, работает с ним, а это - основа основ. Поэтому бояться его и тем более отказываться от него в пользу меньшего быстродействия, по-моему, не разумно. Наоборот, работая с репозиторием в модели, ты видишь, где необходима транзакционная целостность, а значит видишь какие агрегаты ты должен объединить в один, чтобы транзакции по нему проходили нормально. Лично я бы поставил на инкапсуляцию + быстродействие. Смещать логику в контроллеры - это тоже ерунда, так как логика приложения должна быть в модели, и не важно, что эта логика зависит от репозитория. Просто считаем репозиторий частью модели - вот и всё, ведь по сути так оно и есть.
    Репозиторий хранит данные по бизнесу, а разве это не часть бизнеса? Списки e-mail'ов, адресов - это ведь всё часть бизнеса, это не что-то, связанное с инфраструктурой. Это реальные данные по бизнесу. К тому же репозиторий - это виртуальная сущность, которая изолирует модель от прямой связи с базой. Базу репозитория всегда можно заменить. Это вообще может под капотом быть какой-нибудь API, или частично API, частично БД, частично вообще файловая система или LDAP - всё, что угодно, может внутри содержать репозиторий, и это никак не повлияет на модель, потому что репозиторий всегда выдаёт лишь те данные, которые реально связаны с моделью и являются частью бизнеса.

    • @tedikvredik
      @tedikvredik Před rokem

      отличный коммент! Спасибо

    • @sergafanasiev7956
      @sergafanasiev7956 Před 7 měsíci

      Согласен! Отличный коммент! Действительно, что плохого в том, чтобы модель могла делать save, пусть даже в абстрактную БД, чтобы потом, в случае смены БД, можно было через адаптер подменить логику Хранилища! Более того, что если Фабрика, создающая Агрегат, будет также создавать Хранилище и подсовывать его Агрегату на старте?

  • @volumesurup2078
    @volumesurup2078 Před rokem

    Wow!Killlin!!!

  • @alexanderinkognito8379

    Спасибо за доклад. В примере с изменением почтового адреса можно соблюсти изоляцию, инкапсуляцию и быстродействие: ChangeEmail(Email email, List usersWithEmail) а в контролере выбрать не всех пользователей а только тех что имеют такую же почту, оверхед - 1 запрос к БД, но запрос все равно должен быть, лист будет либо пустым либо содержать одного пользователя. Понятно, что не всегда можно провернуть такой фокус.

    • @user-cd5se7lf5t
      @user-cd5se7lf5t Před 2 lety +18

      В этой ситуации контроллер всё ещё частично "догадывается" об ограничениях, которые существуют в предметной области, поэтому предоставляет все необходимые данные для принятия решения, но зачем-то отдаёт само право принятия решения доменной сущности. При изменении и расширении этих требований необходимо будет актуализировать код как в контроллере, так и в модели.
      Точно так же можно было бы в метод просто передавать булево значение, указывающее на то, есть ли другой активный пользователь с данным имейлом.
      В общем, на мой скромный взгляд, это решение, где контроллер лезет в домен, но мы пытаемся обмануть себя и сделать вид, что это по каким-то формальным признакам не так.

  • @Berill82
    @Berill82 Před rokem +5

    33:15 Очень хочется посмотреть, что будет с этой скидкой, если она будет зависеть от чего-то ещё, кроме статуса. И вообще, как можно поместить функцию расчета скидки в объект Статус?! А если от статуса будет ещё что-то зависеть? Тоже сделаем методом в классе Статус?!

    • @alekseykouzmenko9096
      @alekseykouzmenko9096 Před měsícem

      Ответ чуть далее по ходу - там где требуется использовать внешние данные инвариант должен быть неизменяемым.

  • @sergafanasiev7956
    @sergafanasiev7956 Před 7 měsíci

    Супер, спасибо! Чуть-чуть не уловил на 47 минуте, там где Владимир говорит, что Изоляция доменной области нарушается, если Customer умеет сам себя save в БД. А как тогда будет правильно? Его должен сохранять контроллер? Ну т.е. согласно триллемы DDD (55 минута) выбираем вариант Изоляция+Время ?

  • @sau9703
    @sau9703 Před 3 lety +17

    Вот те раз , всегда боролись и пропагандировали за анемичную модель , из-за ее слабой связанности и гибкости в плане расширения возможных операций над ней , и тут на тебе , снова в 90-е ) . У богатой модели много проблем , это паутина , в которой с ростом модели не разобраться , ее тяжело масштабировать , а при внезапно новом бизнес правиле многое переписывать.
    При анемичном подходе , нам ничего не мешает возложить валидацию консистентности модели на доменные сервисы , а клиентам отдавать record-ы дабы не могли накосячить с моделью напрямую. , ну и нет никаких проблем с добавлением новых бизнес правил , при этом не затрагивая существующие модели и методы работы с ними.

    • @-dubok-
      @-dubok- Před rokem +3

      Сервисная модель полна излишеств и не отражает сущность бизнеса. Глядя на неё ты не понимаешь, для чего она существует. В ней также перемешана логика и инфраструктура. В богатой модели как раз таки всё понятно. Она описывает саму себя. Главное - всегда рисовать её диаграмму.

    • @SergeMSO
      @SergeMSO Před rokem +1

      Аналогичное чувство появилось, только связал с серединой 2000-х. Очередные ребята сделали "открытие". "Я не знаю почему так лучше, но бизнесу нравится"

    • @SergeMSO
      @SergeMSO Před rokem +3

      А, вот это вообще пушка: "вот в таком подходе сделаете за 2 дня, а в старом займёт 2 месяца".

    • @user-qp1fk8tm2v
      @user-qp1fk8tm2v Před rokem +1

      на контексты надо разбивать, про это сказано вначале

  • @KikrAzz
    @KikrAzz Před 6 měsíci

    Спасибо. Очень ценно. Докладчик - супер!

  • @user-dv3bq6bi8g
    @user-dv3bq6bi8g Před 7 měsíci

    круто !

  • @Eugene.g
    @Eugene.g Před 3 lety +1

    👍

  • @zerg100500
    @zerg100500 Před 2 lety +10

    Черт. Нельзя еще раз лайкнуть...

  • @GamDevRus
    @GamDevRus Před 9 měsíci

    что такое рефорт для рефакторинга??

  • @rtm0076
    @rtm0076 Před 5 měsíci +1

    Почему вы пропускаете тот момент что CustomerService так же инкапсулирует в себя DataLayer + CacheLayer? Можно приводить примеры более как то приближенные к жизни?

  • @zond_amond
    @zond_amond Před 2 lety +2

    Не думал, что рекомендуется заводить 2 разные сущности кастомера физически.

  • @user-lp6rp
    @user-lp6rp Před měsícem

    Нам бы проблемы трехлетней давности. Печально вспоминать из 2024г.

  • @IvanenkoStepan
    @IvanenkoStepan Před rokem +1

    Жаль, что pluralsight закрыт для России. Хотел там подписаться на курсы автора

  • @alexanderinkognito8379
    @alexanderinkognito8379 Před 2 lety +4

    Такой вопрос, разбили на контексты и написали приложение. Через год понимаем что помимо Продаж и Поддержка нужен еще контекст Маркетинг(или любой другой), как быть в такой ситуации? Насколько деление на контексты является гибким решением? А бывает так что "добавьте вот такую маленькую фичу", фича и правда маленькая но не ложится в существующие контексты, а создавать новый может быть дорого для бизнеса.

    • @magicmanam
      @magicmanam Před 2 lety +1

      Вероятность того, что "маленькая" фича станет со временем большой > 0. В этом случае поддержка изменённого существующего контекста будет тоже дорога для бизнеса, т.к. будет требовать всё более и более объёмной регрессии при будущих модификациях этого контекста с "маленьким" костылём. Это нужно иметь в виду, ну а поступать так, как считаете нужным. Ведь лучше ошибиться и получить опыт, чем пытаться найти "общий" ответ там, где его нет)

    • @timur43378
      @timur43378 Před 8 měsíci +1

      Так добавление нового контекста никак не влияет на другие контексты, в этом и есть смысл, чтобы ограничить влияние контекстов. Так что новый контекст добиваться легко

  • @MrChelovek68
    @MrChelovek68 Před 9 měsíci +2

    реально отличный доклад, жаль что уже 2 года прошло(

    • @vladislavbondarenko6153
      @vladislavbondarenko6153 Před 2 měsíci +1

      почему жаль?

    • @MrChelovek68
      @MrChelovek68 Před 2 měsíci +1

      @@vladislavbondarenko6153 потому что прошло уже 2 года,а я только дополз до ddd

    • @vladislavbondarenko6153
      @vladislavbondarenko6153 Před 2 měsíci +1

      @@MrChelovek68 и я))

    • @MrChelovek68
      @MrChelovek68 Před 2 měsíci +1

      @@vladislavbondarenko6153 ахахаха, ну штош, велкам ту зэ клаб,бадди)))

  • @user-zt2ob3le7e
    @user-zt2ob3le7e Před 9 měsíci +1

    Можно уточнить с момент - Должны быть Рич модели, которые знают сами что можно делать с ними, но они не должны знать как себя сохранить? То есть, рич на пол-шишечки?!

    • @serg4568
      @serg4568 Před 9 měsíci +2

      А еще возникает вопрос, как быть, если какое-то поле сущности lazy-инициализируемо. При обращении к этому полю его значение нужно получить из другого сервиса, например. В этом случае сущность должна еще и знать как себя дозагрузить.

    • @timur43378
      @timur43378 Před 8 měsíci +1

      Она должна знать все о себе в терминах предметной области. Сохранение это уже техническая, инфраструктурная деталь и об этом модели знать не надо.

    • @timur43378
      @timur43378 Před 8 měsíci

      ​​@@serg4568нет, как раз тут сущность не может быть на полшишечки дозагруженной, она должна быть целостная. Лучше конкретный пример привести, что за lazy свойство такое может быть у модели.
      Если значение этого поля находится в другом сервисе, то скорее всего это поле не этой модели, а что-то совершенно другое

    • @sergafanasiev7956
      @sergafanasiev7956 Před 7 měsíci

      @serg4568, Вот-вот, поэтому кажется что Агрегат должен всегда иметь ссылку на своё Хранилище и дёргать по необходимости

  • @ryazanov13
    @ryazanov13 Před rokem +1

    58:18 Если мы выберем вариант с инкапсуляцией и интерфейсом, то можно прекрасно всё тестировать без моков. При этом 3 вариант где плохое быстродействие не обеспечит инкапсуляции. Я имею ввиду вариант прокинуть все заказы в кастомера, т.к. ничто нам не мешает прокинуть пустой или неполный массив (и вообще будет не очевидно какой список кидать в метод). Как по мне нормальная реализация 3го варианта (с инкапсуляцией, изоляцией, но плохой скоростью) это запихнуть список кастомеров в другую модель.

  • @mikhailsloushch5052
    @mikhailsloushch5052 Před rokem +3

    не вижу проблемы передать в модель репозиторий через интерфейсную ссылку. Да, это говорит о том, что это где то храниться, но где и как - не открывает. Можно хранить по факту хоть в блокноте

    • @xonicov
      @xonicov Před rokem +1

      Тоже не понимаю почему автор так против прокидывания интерфейса в модель. Если очень хочется то можно сегрегировать интерфейс. Хотя у автора есть прямо серия статей на эту тему, стоит почитать.

    • @DenisMakarovPersonal
      @DenisMakarovPersonal Před rokem +1

      Проблема начинается когда у тебя в модель 10 разных зависимостей на разные подсистемы добавить нужно чтобыреализовать новые требования, таким образом модель превращается в монстра который делает все

    • @mikhailsloushch5052
      @mikhailsloushch5052 Před rokem

      @@DenisMakarovPersonal а какая альтернатива, если для системы важна производительность?

    • @DenisMakarovPersonal
      @DenisMakarovPersonal Před rokem

      @@mikhailsloushch5052 альтернативы нет, есть DDD трилема в которой нужно делать выбор, в конкретном примере я бы подумал нужно ли знать Customer уникальный он или нет, может с этим другая сущность модели должна разбираться, или можно в Customer передавать результаты проверки уникальности (флаг/статус) а не саму лямбду с вызовом функции проверки

  • @user-gw6df6ns7e
    @user-gw6df6ns7e Před 4 měsíci +2

    Жертвуем быстродействием и получаем замену email за 15 секунд на запрос. И риск out of memory.

    • @D0nSergio
      @D0nSergio Před 3 měsíci

      Интересно, в чем проблема использования ICustomerRepo в домен модели, это слабая зависимость в угоду быстродействия. Эти интерфейсы и описывают в слое домена. Также не понял зачем простые вещи типа "Email" делать value object-ом, почему то мне кажется, в книжках это рекомендация , а не правило. Автор шарит, но зачем то перебарщивает с этим "православным DDD" из книжек

    • @ilmakeyouone
      @ilmakeyouone Před měsícem

      @@D0nSergioпро valueobject тоже не догоняю. за коим нам стремиться, чтобы все объекты были immutable? может лучше тогда уж на haskell писать?😂

    • @ilmakeyouone
      @ilmakeyouone Před měsícem

      @@D0nSergio видимо, автор имеет ввиду, что проблема не в композиционной зависимости (ведь интерфейс от неё итак избавил), а в семантической. то есть, попытка уйти от зависимости одной доменной области от другой на чисто ментальном уровне

  • @wanderingxsx
    @wanderingxsx Před 3 lety +5

    Очень холиварный вопрос, тестить без интерфейсов будет так себе. Засунуть всё в модель не очень хорошая идея. Возможно хотя бы сделать модель и интерфейс действий, потом это всё унаследовать в конечную сервис-модель, но опять же это не общепринято.
    Но искать логику по моделям размазанную, идея не очень хорошая. Триллема бы не появилась, если бы работали отдельно.
    К тому же CQRS или сам подход разделения чтения и записи сомнителен в озвученном подходе, и прелестно ложится на интерфейсный.
    Всё таки хорошо, когда есть домен, есть действия домена и объединение это в сущность домена, которая может действия.

    • @vladislavbondarenko6153
      @vladislavbondarenko6153 Před 2 měsíci

      По тестам у автора есть топ 1 книга в этой области

  • @user-gy3ei7fk8w
    @user-gy3ei7fk8w Před rokem +1

    Спасибо за доклад! Поддержу предыдущих комментаторов - отличная подача и разъяснения, очень помогли!
    ЗЫ. Единственное замечание: было бы хорошо поменьше использовать англицизмы при докладах на русском языке, но я понимаю, что когда практики английского больше, чем русского, это может происходить невольно (читай "на-автомате").

    • @NoName-wy4pq
      @NoName-wy4pq Před rokem +6

      ну это же не доклад 1с. Когда все коммуникации на английском, сложно не использовать понятный для всех сленг

    • @sergafanasiev7956
      @sergafanasiev7956 Před 7 měsíci +3

      Глупости! Всё там норм с английским, человек умный и трудолюбивый! Вы лучше поучитесь, как надо работать

    • @ilmakeyouone
      @ilmakeyouone Před měsícem

      смотрите, вот заимствованные слова из других языков, которые Вы сами используете:
      комментатор
      доклад
      практика
      автомат
      🫳🏻
      ⤵️
      ⤵️
      🎤
      💢

  • @ryazanov13
    @ryazanov13 Před rokem

    для чего синхронизировать поля модели из одного контекста в другой, а потом ещё постоянно контролировать какие из них можно только читать, если можно просто хранить ID на модель из другого контекста, и всё выше перечисленное можно будет избежать

  • @Nazaro4ka
    @Nazaro4ka Před 9 měsíci +1

    У меня возник вопрос
    Будет ли богатая доменная модель прототипом так называемого God object, реализация которого не является хорошей практикой?

    • @timur43378
      @timur43378 Před 8 měsíci +1

      Не будет. Модель отвечает только за свои прямые доменные задачи, т.е. обеспечивает целостность соответствующих данных.

    • @Nazaro4ka
      @Nazaro4ka Před 8 měsíci

      @@timur43378, спасибо за исчерпывающий ответ!

  • @fuckptn
    @fuckptn Před 4 měsíci

    Для емейла абсолютно не нужен ValueObject, верификация емейла является инкапсулированной логикой самого кастомера, валидация емейла кастомера - это одна процедура, валидация емейла админа или еще какого специфичного эктора - это другая логика, основной принцип - использование простых типов данных настолько насколько это возможно (никто не мешает валидировать при присвоении, кстати точно также как это будет делать класс Email и логика верификации будет в единственном экземпляре, потому что она кастомер-специфична, если есть необходимость проверить на соответствие RFC, это эта часть выносится в модуль "или в провайдер"). Короче ValueObject появляется, когда представление в доменной модели объекта с помощью простых типов невозможно или нецелесообразно (в частности паттерн адаптер), во всех остальных случаях - будет больше проблем из-за желания накодить побольше артефактов и разнести логику, не говоря уже о области видимости такой логики (скажем так если бы опредленное свойство было бы производным от нескольких полей кастомера, а не самодостаточным как в случае с емейл, в общем с точки зрения ООП задолбались бы вы это в отдельный объект отдавать с полноценной инкапсуляцией и нужной областью видимости). С ордером тоже проблемы, поднимаясь из базы получается, что кастомер хранит ордера, а накой их поднимать, если они не нужны и вообще не являеются частью кастомера? Это все равно что поднимать все ордера вместе с курьером который их выполнил, или возможно, которые он выполняет в данный момент??? Таких колекций будет хреналиард и они не нужны. Ну скажете вы давайте лейзи пропертю сделаем, а куда денем транзакционную целостность? И сказки про вторичность БД неуместны, потому что абсолютно все равно откуда их не надо поднимать вместе с кастомером, это я в понятных терминах описываю. Ниже там пример был про метод ChangeEmail(Email email, ....) вопрос а что если потребуется поискать в базе невалидные емейлы (для кастомера), причин - дофига, последствия интеграции с другими системами, банальная эволюция требований к емейлу и т.д. - пишется код обновляющий существующие данные, надо найти всех аффектед. Как мне проинстанциировать емейл с "неправильным" содержимым? Получается перегружаем конструктор и делаем все тоже самое, напрямую передаем стринг, который юзаем внутри емейл. Так накой нам такой емейл VO? Оверкомпликейтед, который загоняет нас туда, откуда DDD должен был вытащить. В нормальном подходе методы кастомера, которые отвечают за поиск не осуществляют валидацию на бизнес правила, а вот методы для изменения будут проверять. И это в данном случае и есть правила поведения самой сущности Customer, и никаким образом оно не делегируется объекту Email. Лучше бы про правило идентичности рассказали, по которому у вас в кастомере Id должен быть, и агрегаты, которые и будут собирать пачки объектов, и у которых уже был бы смысл хранить оредера. У меня например это отдельный слой и фасады, так лучше организован код, но суть таже - это все еще агрегаты. Вот уж сколько мнений столько и реализаций DDD и часто корявых )))

  • @nikitazhukov7372
    @nikitazhukov7372 Před 4 měsíci

    52:00 Получается циклическая зависимость: CustomerRepository зависит от Customer и наоборот

  • @ryazanov13
    @ryazanov13 Před rokem +1

    1:06:30 Как это? Если приложение работает в памяти, это ещё не значит что в нём нет списка кастомеров => репозитория кастомеров хранящего этот список.

    • @LotmineRu
      @LotmineRu Před rokem +1

      Таким образом, мы переходим к главному вопросу DDD - можно ли выкинуть репозитории из домена?

    • @ryazanov13
      @ryazanov13 Před rokem +1

      @@LotmineRu Так вроде как репозиторий и не в домене лежит, только его интерфейс, а сам репозиторий в инфраструктуре. Вопрос был больше в том, почему наличие репозитория обязательно требует БД?

    • @LotmineRu
      @LotmineRu Před rokem

      ​@@ryazanov13 ссылочная прозрачность же, класть в домен реализацию или интерфейс - принципиальной разницы нет
      вводя интерфейс, хранилище все равно никуда не пропадает, зависимость все равно есть, хоть мы ее "замели под ковер"

    • @ryazanov13
      @ryazanov13 Před rokem +1

      @@LotmineRu не понял причём тут ссылочная прозрачность. Если вы не используете интерфейс, то:
      1) для замены реализации нужно менять домен, что нарушает OCP и DIP
      2) невозможно использовать несколько реализаций
      3) сложно тестировать (как следствие из п.2 , не можем написать фейковую реализацию)
      4) невозможно вынести домен/инфраструктуру в отдельный пакет/гит репозиторий и т.п.
      И повторюсь вопрос изначально был в том, почему автор решил что без БД не нужен репозиторий. У нас вполне может быть репозиторий который хранит данные в памяти.

  • @sevaelunin
    @sevaelunin Před 3 lety +8

    Владимир не очень честно поступает, показывая полную модель с передачей репозитория в агрегат =) В такой реализации модель выглядит "грязнее", чем модель без асинхронного поведения. Репозиторий это прикладной сервис и ему нечего делать в доменной модели
    Честнее было бы передавать доменный сервис как интерфейс IUniqueEmailCheck

    • @andrewtsvetsih2675
      @andrewtsvetsih2675 Před 3 lety +4

      Согласен. Для себя мы выбрали другой вариант - использовать делегаты. Подробности можно посмотреть тут czcams.com/video/Bd83nPK_K3U/video.html
      Как вам такое решение?

    • @sevaelunin
      @sevaelunin Před 3 lety

      @@andrewtsvetsih2675 тоже думал над вариантом с делегатами, но обобщенными. В вашем докладе довольно изящно и выразительно выглядит, спасибо. Может быть асинхронное поведение агрегата через делегаты будет проще "продать" сторонникам анемичной или чистой модели =)

    • @IgorPchelko
      @IgorPchelko Před 3 lety

      @@andrewtsvetsih2675 можно еще специализированные интерфесы делать (Interface segregation principle from SOLID). Но это если возможно. Но это не решает дилему описанную Владимиром, функция не получает ссылочной прозрачности.

    • @andrewtsvetsih2675
      @andrewtsvetsih2675 Před 3 lety

      ​@@IgorPchelko Спасибо за ответ. Есть похожий подход в докладе czcams.com/video/_dQRAsVhCqA/video.html
      Я считаю, что интерфейсам репозиториев (и любой другой инфраструктуры) в модели не место. А вот как их убрать из модели - это вопрос!
      Делать еще один уровень интерфейсов вокруг инфраструктуры - это просто лишний уровень абстракции. Так как реализация такого интерфейса будет просто прокидывать вызов к инфраструктуре. Это сильно усложнит проект а выигрыш минимален. Уж лучше напрямую инфраструктуру из модели вызывать.
      На мой взгляд делегаты - это лучшее решение т.к. они не создают новый уровень и изолируют модель от инфраструктуры.
      Хотя может я вас неверно понял. Можете привести пример как будет реализация с Interface segregation?
      Спасибо

    • @sau9703
      @sau9703 Před 3 lety +4

      @@andrewtsvetsih2675 Просто не воспринимайте репозиторий как обязательную завязку на инфраструктуру и БД , в общем смысле это просто интерфейс доступа к коллекции сущностей , разве доменная модель не имеет права знать о том , какие сущности существуют , если от этого зависит бизнес логика ?
      Другое дело , что реализация интерфейса уже зависит от инфраструктуры и должна быть размещена за рамками домена.

  • @alexlo5655
    @alexlo5655 Před rokem

    если бы я не знал аглийский то ничего бы непонял. на пример, Сущность ето Entity? @Vladimir should you publish this to Pluralsight as another DDD course? Something like "DDD in one hour"?

  • @vpurazov
    @vpurazov Před rokem +1

    Вот сомневаюсь - например вычисление процентов по кредиту -там 100500 интеграций. Вот не место им сущности Заемщик

  • @ryazanov13
    @ryazanov13 Před rokem

    36:15 Интересно так получается, т.е. если у вас несколько областей, которые добавляют ордера, то почему они все не используют AddOrder? Ну т.е. в случае с CustomerService мы нарушаем инварианты если в обход CustomerService меняем Customer, а в случае с богатой моделью мы нарушаем инварианты если в обход модели изменяем состояние модели (например через рефлекшны или др. способы доступа к памяти). Это получается больше вопрос договорённости: не используй рефлекшны и др. прямой доступ к памяти/используй только соответствующий сервис. Используй только соответствующий репозиторий и т.д.

    • @DenisMakarovPersonal
      @DenisMakarovPersonal Před rokem +1

      менять поля класса рефлекшеном это конечно сильный пример )))

    • @ryazanov13
      @ryazanov13 Před rokem +2

      @@DenisMakarovPersonal я не сторонник использования рефлекшнов, но кто-то же использует. Это просто пример. Можно сделать sql запрос на update в обход любых моделей, без всяких рефлекшнов, можно создать другую модель которая будет мапиться в ту же таблицу, и никакая инкапсуляция не спасёт, кто запрещает? Ну только лишь соглашения.

    • @bfg5244
      @bfg5244 Před rokem

      @@ryazanov13 более того, бизнес вообще может не использовать софт и всё делать на бумажках. Соглашение двух людей - сделка, нескольких людей - договор, государства - закон, нескольких государств - международные конвенции.

    • @ryazanov13
      @ryazanov13 Před rokem

      @@bfg5244 смотря какой бизнес. Если у вас бизнес - компьютерная игра или антиспам сервис, не понятно, что вы будете на бумажках делать :)

  • @Berill82
    @Berill82 Před rokem +1

    52:20 Зачем вытягивать из базы ВСЕГО кастомера вместе с его заказами и другими данными для изменения е-мейла??? Вы представляете какие это тормоза для простой, по сути, операции? А ещё там есть проверка на равенство "вытянутого" кастомера с самим собой. То есть, ещё надо оператор перезагрузить или метод Equals() перезагрузить. Иначе у вас будет только проверка равенства ссылок на два объекта. Короче, много кода для реализации простого функционала + тормоза в работе.

    • @LotmineRu
      @LotmineRu Před rokem

      Какие тормоза, ты в онлайн магазе ежедневно делаешь тыщу заказов? А если действительно есть что-то такое, то наверняка оно и смоделировано будет иначе.

    • @Berill82
      @Berill82 Před rokem

      @@LotmineRu , да, вы правы. Оно моделируется иначе. Это только в туториалах или при очень маленьких объемах данных вытягивают весь объект со всеми зависимостями чтобы поменять одно поле. В нормальном приложении никто не будет так делать. А если ещё и агрегаты использовать, то там вообще начинается веселье. А вам всего-то надо ОДНО поле поменять и сделать это можно двумя запросами:
      1. Проверить есть ли вообще кто-то в базе с таким е-мейлом: db.Customers.Any(c => c.Email == email)
      2. Если нет, то Update одного поля по ключу customerId.
      И все. Бизнес правило уникальности е-мейла обеспечено. Не надо всего пользователя поднимать из базы со всеми его данными, а потом ещё ненужный код писать.
      Проблему вижу в репозитории, так как чаще всего за ним стоит EF (опять же благодаря туториалам =)) ), а EF не умеет делать Update без предварительного Select. Решается заменой EF на ORM, которая умеет.

    • @LotmineRu
      @LotmineRu Před rokem

      ​@@Berill82 я имел ввиду, что в одних случаях нормально, когда что-то содержит коллекцию чего-то, когда понятно, что в этой коллекции и не бывает 100500 элементов
      а этот пример с уникальностью емейла... такой себе, уникальность емела зачастую не такое уж важное бизнес-правило, чтобы его непременно нужно было пихать в домен (но допускаю, в каких-то бизнесах оно может быть реально важным.. но чаще - нет)
      страшную вещь скажу - можно констрейнт в бд повесить и не париться, никто еще от этого не умирал

    • @Berill82
      @Berill82 Před rokem +1

      @@LotmineRu , даже если оставить в стороне обновление е-мейла. Мне не понятно другое. Это же не эффективно загружать весь объект, чтобы потом произвести над ним какие-то действия. В этом примере у нас не такой уж и большой объект. А ведь потом он начинает обрастать дополнительными свойствам по мере того как бизнес начинает добавлять новый функционал. И вот как тут быть? Количество таблиц, в которых содержится вся связанная с пользователем информация, растет. Загрузка этой информации начинает тормозить, запросы усложняются и т.д. И вот тут я вижу проблему в самом подходе. Нам не нужна, допустим, информация о заказах для обновления персональных данных. Тогда зачем ее загружать?!

    • @LotmineRu
      @LotmineRu Před rokem +3

      @@Berill82 Зачем на информация о заказах для обновления персональных данных? Т.е. почему эти две обязанности живут вместе?
      Ну и как бы предметная область развивается. Если поначалу что-то было ок, а потом становится как-то не ок - очевидно, нужно перерабатывать модель вслед за развитием предметной области.
      Короче, я бы сильно не зацикливался на примерах, это ж примеры, они не для работы в проде, а для наглядной демонстрации концепций.
      Во-вторых, я также же бы сильно не зацикливался на производительности, модель важнее. Вот когда припрет - тогда и надо думать об оптимизации.

  • @ZVA_NOOK
    @ZVA_NOOK Před 2 lety +1

    Хм...
    Программирование - точная наука. Но, сам процесс - творческий.
    Любой паттерн - превращает искусство программиста в ширпотреб для масс.
    Это НЕ плохо, но и не надо безоговорочно следовать правилам. Следовать надо контексту задачи и окружения.
    Тот же customer в 99% случаев принадлежит "третьей стороне" (каталогу пользователей). И, смысл городить цепочку вызовов "потому, что так в паттерне" вместо прямого обращения к каталогу на проверку дублей e-mail? Да и алгоритмы посика/сравнения в службе наверняка поинтереснее (оптимальнее) самописных.
    Всё больше убеждаюсь, что со времен изобретения ООП, SOA, витрин данных, "наросла" масса модных трактовок/методик суть которых - Думай прежде чем что-то написать и помни о тех, кто будет править код после тебя.

  • @MrKelebras
    @MrKelebras Před 5 měsíci

    жаль что лайк можно поставить только 1 раз))