завязанных на реализацию?
Пример такого хранения:
https://github.com/slimphp/Slim-Skeleton/tree/master/src
Просто я слышал мысль, что "DDD это не про разделение по папкам и классам, а про подход и смысл".
То есть я видел примеры, когда всё в одной директории — и VO, которые больше к домену относятся, и конкретные экшены с реализацией через Eloquent, и так далее.
Если проект небольшой и работает не так много людей - я бы возможно даже начинал с технических консернов, а уже потом с ростом понимания че система из себя представляет и из каких функциональных блоков состоит уже бы менял структуру. тут как с контекстами и т.д. Нет смысла на ранних этапах развития продукта, пока его предметная область и понимания ее не устаканились, резать на контексты. Слишком часто будут меняться. В определенный момент контексты более-менее стабилизируются (будут меняться не раз в месяц а раз в пол года скажем и то местами и чуть-чуть, могут новые контексты появляться). Этот момент времени часто совпадает с необходимостью скейлить разработку. по той же причине не рекомендуют начинать проект с микросервисов.
"слои не папки". Для начала введем понятие "модуля". Модулем будет некая относительно самостоятельная часть системы. Если твой язык умеет в модули (js, python) то все просто. Если нет - то есть похожие концепты (неймспейсы, классы, пакеты). Главное что можно сказать о модуле что есть "снаружи" (кто юзает) и "внутри" (как работает, че делает, детали реализации). Модули выстраиваются в иерархии. То есть один модуль может состоять из нескольких других модулей. С точки зрения организации кода сделаем допущение что "минимальный уровень модуля который нас интересует это файл". Так что модуль который состоит из других модулей это уже "папка". С точки зрения разделения ответственности, если у тебя разные консерны в разных "модулях" то этого достаточно. С нашими допущениям достаточно иметь несколько файлов. Где они лежат и как сгруппированы - это уже другой вопрос. У модулей есть две "метрики" (не численные) на которые стоит смотреть при построении структуры: - coupling - это мера того как изменения в одном модуле влияют на другой. Низкий coupling - низкое влияние изменений. High coupling - в крайней форме меняешь один модуль - всегда надо менять другой. Мы стремимся к low coupling что бы ограничить каскад изменений. Можно в википедии почитать про виды каплинга. Виды эти проще воспринимать с точки зрения "а че поменялось". Например есть temporal coupling - это когда порядок выполнения операций влияет на модуль. Если он меняется и приходится менять зависимый модуль - значит там temporal coupling. Убрать каплинг или снизить его до минимума не выйдет - иногда и global coupling нужная штука. Проблемы обычно с unnesesary coupling - его в среднем проекте со временем набирается много. - cohesion - это мера того, как "то из чего состоит модуль" сгруппировано вместе. Есть несколько степеней - самый "высокий" - functional cohesion - когда все элементы модуля нужны всегда без исключений и всегда задействованы - это эталон. На уровне отдельных маленьких модулей он достижим (и то не всегда), на уровне более высоком - не особо. Самый низкий уровень - coincidental cohesion - это как понятно из названия - рандомом накидать. Типичная папка utils такая. Второй по "плохости" уровень - logical cohesion - это когда мы группируем элементы модулей по какой-то категории логической. "все что про юзера - в папку юзер" например. Ну или, как в примере который ты показал - по техническим консернам. "все VO в папку VO, все контроллеры в папку контроллеры". Стремимся мы к high cohesion. почему? просто потому что при high cohesion проще находить код. "то что меняется вместе должно лежать рядом, то что не должно меняться вместе не должно лежать рядом". Почему большинство проектов можно охарактеризовать через logical cohesion с категоризацией по техническим консернам? Потому что это универсальный вариант. Это лучше чем "рандом" но все еще не требует знания специфики твоего проекта. Это идеальный вариант для докуметаций к фреймворкам откуда люди берут структуру как эталон. так вот, "слои" могут восприниматься как такая вот "техническая категория". И мы так же можем группировать код по ним. Когда-то это было даже модным. Во времена когда например data access layer писался одним "отделом" а UI layer другим. Некоторые команды все еще так разделяют работу между собой. Для них в таком разделении есть польза. Если у тебя один разработчик "прорезает" все слои - то этот концепт перестает влиять на структуру проекта. Достаточно просто держать в разных модулях (файлах) разные консерны. Дальше ты выбираешь под свои нужны как чего хранить. Какой подход тебе проще. Всякие feature based структуры могут требовать больше усилий - надо думать куда сложить код, или это новая фичаи ли еще чето. Однако если у тебя несколько команд работают над проектом то разделение по техническим консернам не позволяет командам выстроить четких границ (ну разве что мы опять про разделение команд по техническим консернам). Все трогают все. Сложнее анализировать зависимости и т.д.
Я не уверен насчет "нет смысла на ранних этапах резать на контексты" — на прошлой работе таски были разделены по своим "доменным слоям", если с примерами говорить, то кто-то делал HR-модуль, кто-то делал CRM-модуль, кто-то Document-модуль. Связи между модулями обсуждались и исходя из них вносились изменения в доменную часть, и только потом Application/Infrastructure слой разрабатывали у каждого модуля. При том разработка доменной части по большей части напоминала работу аналитиком, потому что устаканивалось представление о том как это будет работать и фиксировалось в виде кода Domain-слоя (Entity, ValueObject, Repository Interface, итд). Это один из примеров того, как некоторые представляют себе такую разработку — ты считаешь это неправильным? Я правильно понимаю, что ты делал бы обычное веб-приложение монолитное, и разделение на контексты внёс только потом, когда у вас уже продукт в продакшене с работающей и внятной бизнес-логикой? Я не пытаюсь докопаться, просто какое-то общее представление пытаюсь у себя выстроить об этом подходе, чтобы соответствовать "рыночному" представлению о том, что такое "знать и понимать DDD". Но такое чувство, будто это какой-то каргокульт который люди интерпретируют как им удобно, а внедряют ради Resume-Driven-Development'а.
ну для начала это не "доменные слои" - это "под домены". Домены так же как и модули выстраиваются в иерархии. "контексты" это то как под домены мэпятся на solution space. В идеале у них 1:1 мэппинг но могут быть различия (если со стороны problem space другие люди делят). ну да не суть... не бывает неправильного правильного. Смысл в том как организовать структуру проекта так, что бы оно позволяло работу организовывать. Все эти solid-ы, information hiding и прочее оно про то как сделаьт так что бы что-то большое разбить на куски таким образом и с каждым куском могли разные люди работать. Если ты начинаешь уже с ситуации когда несколько команд - то да, описанный тобой вариант вполне норм. В рамках монолита менять границы все еще дешево достаточно. Делать или не делать внутри контекста разделение по "техническим слоям" - это конвеншен который вы уже сами придумываете. Есть ли для вас польза от такого разделения или нет. Мой поинт в том что если у тебя консерны (репозиторий в одном файле, чет еще в другом) разделены на отдельные модули то этого достаточно. Дальше вопрос удобства и т.д. Например делать интерфейс для всех репозиториев в целом может быть полезным в том плане что может быть одна реализация (один класс) на уровне инфраструктуры и десяток разных интерфейсов для разных выборок в домене (что бы interface segregation). Если у тебя один к одному все мэпится то смысла в этом нет. Короч, правильно неправильно нет, есть полезно бесполезно но это уже как говорится it depends.
не бывает неправильного правильного. Смысл в том как организовать структуру проекта так, что бы оно позволяло работу организовывать. Все эти solid-ы, information hiding и прочее оно про то как сделаьт так что бы что-то большое разбить на куски таким образом и с каждым куском могли разные люди работать. ++ Спасибо за мысли.
Вот, к слову сказать, пресловутый SRP как раз о том, что то, что меняется вместе, должно лежать вместе, а то, что меняется по отдельности (по разным причинам) должно быть разделено. А вовсе не про "штука должна делать только одну вещь", как любят всякие толкователи святого Боба рассказывать :)
один класс должен делать одну вещь ;D
Об такое более корректное определение SRP как раз и ломаются все эти "луково-гексагональные" архитектуры. От бизнеса не прилетает требования вида "поменяйте все адаптеры", а скорее "почините сценарий X, уходящий в глубину на несколько слоев". Т.е. в большинстве случаев деление по техническому признаку не снижает когнитивную нагрузку, либо снижает очень незначительно, на уровне: "у системы вот такие внешие артефакты есть, и вот такие ручки входные".
Так дядя Боб уже сам растолковал СРП
Ну с луковой возможно, но гексагоналка ж проще существенно. Чё ей ломаться. На тему того что бизнес не просит адаптера поменять - просит и ещё как. Особенно интеграции с сервисами, Аля "из-за data residency нам для такихто стран надо юзать другой сервис" и вжух уже поменять чисто транспорт + выбирать его по географии
Ну, я про кейс где у нас папочка (модуль) "adapters" с 15+ файлами, где единственная причина нахождения этих файлов вместе - оно все "адаптеры". Для меня эти слоистые архитектуры - конкретная имплементация IoC, который сам по себе неплох. Может через такие коекретные штуки типа гексагоналки проще придти к фундаментальным вещам - хз
Возможно гексагоналка это больше про "гексагоны и границы", но чет в целом больше интерпретации вида "делим на тех слои и збс". Типа, есть независимое ядро, которое может выдавать полезный инсайт без всех этих баз и фоеймворков
я в итоге не вижу разницы между функциональным кохиженом и SRP ну кроме того, что функциональный кохижен - это скорее метрика, а SRP - это декларация, что удобно стремиться к функциональному кохижену
Обсуждают сегодня