Для менее квалифицированного русского коммюнити такие подходы, пожалуй, сложноваты.
У меня несколько другое впечатление. Для русскоязычного коммюнити свойственна большая простота и ясность мышления в вопросах архитектуры. В первую очередь это касается публикаций на хабре. У многих главных западных авторов какая-то сложность и перекрученность. Особенно это касается книжек по ddd. Исключение это книги Мартина Фаулера. Все его книги просто замечательные.
Разделение на слои выглядит достаточно странно. Из приведенного в статье кода логично разделить на слои таким образом Controller, Application, Domain, Repository.
Репозитории я вообще не включаю в доменный слой, т.к. считаю что даже доменные сервисы не должны иметь зависимостей (хоть DDD это и не запрещает), чтобы не смешивать бизнес и инфра-логику.
Наконец-то в первый раз встречаю мнение сходное с моим. Начиная с первых книг по ддд репозитории постоянно упоминаются в составе функционала, связанного с доменом. Но это же совсем разные вещи. С доменом связаны доменная логика и модель данных домена. Репозитории работают с персистентными данными и персистентной моделью данных. И как раз книги по ддд говорят о необходимости разделения функционала домена и функционала работающего с базами данных.
Не позволять разным агрегатам напрямую ссылаться друг на друга, только по id.
В моём понимании агрегаты вообще не должны знать о существовании друг друга даже если это вызов другого агрегата по его id. Если необходимо работать с несколькими агрегатами, то это делает или доменный сервис или use case из которого идёт вызов логики домена.
InfoQ странный ресурс. Из всего что они публиковали по архитектуре только одна статья мне показалась ценной и информативной. В остальных контент странного содержания - не вижу как его применить в практических задачах. А архитектура это как раз про применимость на практике.
Неужели хранение в одной папке событий TemplateChanged.php, TemplateArchivated.php и репозитория TemplateRepositoryInterface.php - это и есть результат многолетнего осмысления ддд?
Похоже не совсем понимаю проблему у автора комментария. В одном DTO добавляю поля Tags, а в другом DTO нет таких полей, если они не нужны в функционале использующем DTO.
Если речь идёт о работе с транзакциями, то конечно надо знать условия вызова commit/rollback.
слой работы с Entity должен неявно знать структуру будущего DTO
Слой работающий с DTO более высоко лежащий, чем слой с Entity. И как раз слой с DTO знает всё про наличие и структуру Entity. И это вполне естественно, так как DTO наполняется данными из объектов Entity. А слой с Entity ничего не знает про слой с DTO. Такова логика многослойной архитектуры. Если ею не пользоваться, то ничего не могу сказать по этому вопросу.
Отличный комментарий - смешаны транзакции, Entity, LazyInitializationException и "слоеные архитектуры" как очередной marketing bullshit.
Но мне ответить очень легко.
DTO - это не обязательно какой-то аналог Entity. DTO может включать в себя информацию из разных Entity объектов и слой, содержащий DTO, конечно не имеет никакого отношения к тому как и где реализованы транзакции.
Что касается LazyInitializationException, то совершенно не обязательно, что Entity наполняется при помощи ORM фреймворка, который вызывает упомянутую исключительную ситуацию. Но даже при использовании ORM функционал слоя работающего с Entity должен быть реализован так, чтобы исключить возникновение LazyInitializationException.
Что касается транзакций, то типовой алгоритм следующий. Допустим работа с Entity идёт в logic layer. Функционал работающий с Entity помещается внутри функционала use case или по Фаулеру это application logic (это тоже logic layer). При старте метода в объекте application logic запускается транзакция, а затем отрабатывает нужный функционал с Entity. Если при работе метода не возникли exception, то по завершению функционала метода application logic отрабатывает commit транзакции, в противном случае rollback. Возникновение LazyInitializationException откатит транзакцию, а сообщение об этой ошибке покажет разработчику её источник.
Проблема настолько распространена, что породила в сообществе целые баталии на тему «где правильно делать маппинг Entity ↔ DTO».
Если использовать многослойную архитектуру, то ответ на этот вопрос очевиден. DTO находится в более высоко лежащем слое приложения, чем слой в котором находится Entity. Соответственно слой с Entity не знает, что существуют объекты DTO - слои связаны между собой однонаправленно. Поэтому маппинг Entity ↔ DTO происходит в слое в котором находятся объекты DTO.
То что мне показалось интересным было или в книгах или на хабре. На хабре в том числе переводы западных авторов.
У меня несколько другое впечатление. Для русскоязычного коммюнити свойственна большая простота и ясность мышления в вопросах архитектуры. В первую очередь это касается публикаций на хабре. У многих главных западных авторов какая-то сложность и перекрученность. Особенно это касается книжек по ddd. Исключение это книги Мартина Фаулера. Все его книги просто замечательные.
Можете подсказать ссылки на подобные публикации или это сокрыто внутри проектов и не выносится наружу?
Представляется совершенно логично, что domain содержит 2 раздела - Entities и Logic. Совершенно непонятно зачем его разбивать на 2 модуля.
Разделение на слои выглядит достаточно странно. Из приведенного в статье кода логично разделить на слои таким образом Controller, Application, Domain, Repository.
В качестве Engine логично использовать корень агрегата. Как отдельный объект это будет чрезмерное усложнение кода. Сама идея достаточно интересная.
Если какой-то из коэффициентов представляет собой нелинейную функцию, то задача переходит на совершенно другой уровень сложности.
В C# double не даёт нужной точности значения. Всегда использую decimal.
Наконец-то в первый раз встречаю мнение сходное с моим. Начиная с первых книг по ддд репозитории постоянно упоминаются в составе функционала, связанного с доменом. Но это же совсем разные вещи. С доменом связаны доменная логика и модель данных домена. Репозитории работают с персистентными данными и персистентной моделью данных. И как раз книги по ддд говорят о необходимости разделения функционала домена и функционала работающего с базами данных.
В моём понимании агрегаты вообще не должны знать о существовании друг друга даже если это вызов другого агрегата по его id. Если необходимо работать с несколькими агрегатами, то это делает или доменный сервис или use case из которого идёт вызов логики домена.
То есть предполагается, что доменный сервис всегда находится в папке определённого агрегата даже когда работает с несколькими агрегатами?
Куда поместить доменный сервис, который работает сразу с двумя агрегатами?
InfoQ странный ресурс. Из всего что они публиковали по архитектуре только одна статья мне показалась ценной и информативной. В остальных контент странного содержания - не вижу как его применить в практических задачах. А архитектура это как раз про применимость на практике.
Каждый день смотрю на на модули/неймспейсы. Но хранить вместе события и репозитории никогда не буду. Но это конечно дело вкуса.
Неужели хранение в одной папке событий TemplateChanged.php, TemplateArchivated.php и репозитория TemplateRepositoryInterface.php - это и есть результат многолетнего осмысления ддд?
Наверху командуют такие люди, что всегда повернут стрелочку куда надо.
Похоже не совсем понимаю проблему у автора комментария. В одном DTO добавляю поля Tags, а в другом DTO нет таких полей, если они не нужны в функционале использующем DTO.
В чём проблема извлечь все объекты Tag? Дайте нужные настройки при выполнении этого запроса.
Если речь идёт о работе с транзакциями, то конечно надо знать условия вызова commit/rollback.
Слой работающий с DTO более высоко лежащий, чем слой с Entity. И как раз слой с DTO знает всё про наличие и структуру Entity. И это вполне естественно, так как DTO наполняется данными из объектов Entity. А слой с Entity ничего не знает про слой с DTO. Такова логика многослойной архитектуры. Если ею не пользоваться, то ничего не могу сказать по этому вопросу.
Отличный комментарий - смешаны транзакции, Entity, LazyInitializationException и "слоеные архитектуры" как очередной marketing bullshit.
Но мне ответить очень легко.
DTO - это не обязательно какой-то аналог Entity. DTO может включать в себя информацию из разных Entity объектов и слой, содержащий DTO, конечно не имеет никакого отношения к тому как и где реализованы транзакции.
Что касается LazyInitializationException, то совершенно не обязательно, что Entity наполняется при помощи ORM фреймворка, который вызывает упомянутую исключительную ситуацию. Но даже при использовании ORM функционал слоя работающего с Entity должен быть реализован так, чтобы исключить возникновение LazyInitializationException.
Что касается транзакций, то типовой алгоритм следующий. Допустим работа с Entity идёт в logic layer. Функционал работающий с Entity помещается внутри функционала use case или по Фаулеру это application logic (это тоже logic layer). При старте метода в объекте application logic запускается транзакция, а затем отрабатывает нужный функционал с Entity. Если при работе метода не возникли exception, то по завершению функционала метода application logic отрабатывает commit транзакции, в противном случае rollback. Возникновение LazyInitializationException откатит транзакцию, а сообщение об этой ошибке покажет разработчику её источник.
Если использовать многослойную архитектуру, то ответ на этот вопрос очевиден. DTO находится в более высоко лежащем слое приложения, чем слой в котором находится Entity. Соответственно слой с Entity не знает, что существуют объекты DTO - слои связаны между собой однонаправленно. Поэтому маппинг Entity ↔ DTO происходит в слое в котором находятся объекты DTO.