Комментарии 29
В реальной жизни выглядит так, как будто package by layer удобнее.
Ну в самом деле, отдельная коробка носков для ресторана, отдельная для работы, отдельная для встречи с друзьями это перебор)
Или на кухне отдельная соль для пиццы, отдельная соль для салатов, отдельная соль для супов.
Ну вы поняли - большое количество сущностей может быть использовано в разных features
тут как посмотреть) В целом имеет место быть обоим подходам. Как и в программировании все зависит от ситуации.
Я не хотел бы жить в городе где все школы в одном районе, аптеки в другом, а магазины в третем. Тут package by layer никак не может быть применен.
До того как я пошел заниматься в тренажерный зал у меня например вещи были упорядочены в package by layer. Это кажется логично и удобно. Но когда я начал ходить в зал. у меня появились свое отдельное полотенце для плавания, трусы и шапочка и они у меня никогда не лежат в общем гардеробе.
Также в магазинах одежды мы можем заметить упорядоченые вещи по типу - package by layer. Но если мы зайдем в шоу-рум там ты увидешь готовые луки - package by feature.
Если на кухне несколько поваров и каждому нужна своя рабочая зона для готовки еды, то удобно что все приборы и ингредиенты будут в районе этой зоны и тогда они у каждого свои, тогда package by feature выглядит идеальным выбором. Я это знаю так как у меня Брат повар) Но! если у нас повар один или зона она для готовки еды маленькая, то в этом случаи больше подойдет - package by layer.
В реальной жизни, задачи касаются какой-то задачи и области. Например, задача добавить цыпленка в меню ресторана. И когда я ее выполняю, кораздо удобнее видеть только сущности ресторана, сервисы ресторана, репозитории ресторана.
А не искать среди работы,ресторана,друзей, свадьбы, подработки
Package by Layer – это когда все одинаковые здания стоят в одном месте: Все магазины в одном районе, Все дома в другом, Все школы в третьем. Хоели бы жить в таком городе?
Сомнительная аналогия. В городе расстояние до магазина имеет значение. А в ПО разве так? Может быть ссылки на методы или данные из другого модуля вызываются гораздо дольше чем аналогичные ссылки в своем модуле?
Комментарий уходит в сторону производительности, а статья ведь про связность (cohesion) и сцепление (coupling).
Скорость вызова методов или доступа к данным внутри одного модуля vs. из другого модуля – это не относиться к выборе структуры кода. В статье речь идет не о производительности, а о логической связанности (cohesion) и слабом сцеплении (low coupling).
Аналогия с городом помогает представить, насколько неудобно, когда связанный между собой функционал разнесен по разным местам.
В Package by Feature, например весь код связанный с конкретной функциональностью, лежит рядом, что упрощает поддержку и развитие системы. Поэтому аналогия с городом тут не про расстояние, а про удобство организации и поддержку структуры.
Если есть возможность телепортироваться в любой магазин города со скоростью вызова функции в языке программирования, то в чём проблема, что все магазины города собраны в одном месте? Это будет даже красиво.
Прорвёт в этом месте канализацию – и город останется вообще без магазинов. Паразитная зависимость.
В случае программирования – пошли исправлять модуль ради одного, а сломалось при этом другое.
А так дело в канализации! Потому что я думал вы приводите аналогию города с точки зрения шаговой доступности магазинов, школ и т.п.
Что касается модулей. А как поможет не сломаться, если эти два метода будут разнесены по 2 разным модулям? Мне казалось, что модули нужны чтобы ограничивать области видимости функций и данных. Ну и просто чтобы придать коду какую то иерархию для удобства человеческого восприятия. А если 2 сущности А и Б, где Б зависит от А, значит в любом случае для Б доступна видимость А. И если изменить А, есть риск того, что сломается Б и находятся они в одном модуле или в разных нет разницы.
Т.е. я понимаю, что если ремонтировать один магазин, то есть риск, что выключат свет в соседнем магазине тоже, и тогда действительно лучше разнести магазины в разные части города. Но с методами, которые зависят друг от друга так не работает. Либо делаем методы независимыми, и тогда меняя один не сломаешь другой даже если они в одном модуле. Либо они зависимы и вместе ломаются даже если разнесены по разным модулям.
Модуль - это не только область видимости, но и, как правило, единица трансляции и единица редактирования исходного текста программы. И, как следствие, единица дистрибутива тоже.
Замечательная общая фраза. Только она не объясняет как модуль поможет справиться с тем, сломается или не сломается зависимость?
Можете привести пример, где метод B зависит от метода A и в случае когда они в одном модуле B ломается при изменении A, а когда они в разных модулях не ломается?
Если Вася физически не работает с файлом, содержащим функции, необходимые Пете, то он и не помешает Пете.
Ваши ожидания о [не]зависимости могут не соответствовать фактическому положению дел. Если, условно говоря, в начале модуля вы случайно переопределите операцию "+", то конец модуля от этого тоже пострадает.
Если Вася правит функции, необходимые Пете, то он может что то сломать из функций Пети. И при этом не важно в каких модулях эти функции лежат.
Если Вася правит только те функции, которые Петя не использует, то он ничего у Пети не сломает. И опять не имеет значение каких модулях эти функции находятся.
Такие дела))
Я привёл конкретный пример.
Вы, несомненно что то читали про модульную организацию. Возможно даже что то слышали про архитектурные слои. Но такое чувство, что не совсем понимаете зачем оно нужно и как должно быть организовано.
Ок, есть Вася у него есть свои функции, есть Петя, у него свои. Давайте положим все функции Васи в один Модуль, а функции Пети в другой и они друг другу ничего не сломают. Да? А потом приходит Ииигорь! Игорь говорит - мне нужны для работы вот те 25 функций Васи и 30 функций от Пети. Куда их класть? А потом появляется Наташа и заявляет, что ей нужны функции от Васи, от Пети, от Игоря и ещё куча всего, в том числе и из сторонних репозиториев. И прекрасная архитектура совсем сломалась...
Но такое чувство, что не совсем понимаете зачем оно нужно и как должно быть организовано.
Куда уж мне.
Ок, есть Вася у него есть свои функции, есть Петя, у него свои. Давайте положим все функции Васи в один Модуль,
Заметим, я этого не предлагал.
а функции Пети в другой и они друг другу ничего не сломают. Да? А потом приходит Ииигорь! Игорь говорит - мне нужны для работы вот те 25 функций Васи и 30 функций от Пети. Куда их класть?
В каком смысле куда класть? Эти функции как были, так лежат в модулях Васи и Пети.
Возможно тут имелось ввиду куда ложить функционал который агрегирует работу текущей функциональности. Тогда это можно решить на уровне выше. Если это бизнес-логика и нам нужна координация внутри домена, то это можно сделать в UseCase. Если же это композиция нескольких модулей, то можно сделать через фасад отдельного модуля.
Правильное применение DDD и чистой архитектуры решает эту проблему.
Код должен группироваться по bounded context’ам, а не по людям. Если Вася и Петя работают над разными бизнес-сущностями, их код логично разделять, но если у них общие задачи — значит, их код должен находиться в одном контексте.
Если Игорю нужны 25 функций Васи и 30 функций Пети, он не должен напрямую использовать их модули. Вместо этого можно выделить абстракции (интерфейсы), которые предоставляют нужные функции, не привязывая код жестко к модулям.
Наташа не должна тянуть весь код Васи, Пети и Игоря. Вместо этого можно создать слой API или фасад, который предоставляет только нужные ей функции.
Это позволит избежать хаоса и сохранить модульность, так как Наташа будет работать только с контрактами, а не с конкретной реализацией.
Если у нас есть четкие слои (Domain, Application, Infrastructure) и фичи (Package by Feature), архитектура останется устойчивой, даже если добавится новый разработчик или новая функциональность.
А если 2 сущности А и Б, где Б зависит от А, значит в любом случае для Б доступна видимость А
Не всегда. Если А и Б в одном модуле, то Б имеет доступ к internal (C#) потрохам А, а если в разных, то Б может пользоваться только public. Если, конечно, не лезть в интроспекцию.
Да, надо как-то комбинировать.
Например, бизнес-центр в городе - один. И все туда едут на работу утром.
А заводы - в другом месте (желательно за городом).
Будет не очень комфортно, если в каждом районе, кроме аптеки и школы будет завод?
Может быть ссылки на методы или данные из другого модуля вызываются гораздо дольше чем аналогичные ссылки в своем модуле?
Во многих реализациях языков программирования (наиболее распространённым примером является C/C++) так и есть. Вызов из своего модуля такой компилятор может, скажем, заинлайнить, а из другого – нет.
Обычно на нижнем уровне располагаются универсальные примитивы, выделенные по принципу by layer (условно говоря, то, что разработчики системы программирования забыли положить в системную библиотеку – да собственно и сама системная библиотека), а над ними – предметно-ориентированные модули по принципу by feature.
В одном случае by layer проще сделать DRY, но больше риск зависимостей. Удобно, когда 100500 похожих функций.
В данном случае всё изолированно. Удобно, когда много предметных областей и/или программистов
В общем то и вся разница.
Подход Package by Feature рассмотрю на примере большого приложения, использующего одну общую базу данных. При многослойном приложение функционал работы с бд размещается в persistence layer и вышележащие слои пользуются им. Для Package by Feature в каждом пакете получается надо дублировать код для работы с бд ??? Или всё-таки для работы с бд может быть выделен отдельный слой несмотря на методологию Package by Feature?
В Package by Feature можно выделять общий слой для работы с БД, Но главное чтобы он не превратился в God Object. Можно сделать как общий слой в `shared/infrastructure/persistence` так и отдельные репозитории внутри фичевых пакетов;
Код у тебя будет разный в репозиториях и дублирования будет минимальным. дублирования кода - хорошо; дублирования логики - плохо. Важно не путать эти два момента.
При многослойном приложение функционал работы с бд размещается в persistence layer и вышележащие слои пользуются им.
Можно, но я бы так не делал, чтобы уменьшить связаность и возможность возникновения ошибок при изменениях в одном репозитории. Я в таких случаях использую отдельный конект (или EM если у вас доктрина) к БД внутри своего слоя инфраструктуры.
Упакуйте свой код правильно