Башня Байткода
69 subscribers
26 photos
2 files
27 links
Авторские заметки об архитектуре ПО на основе реальной практики.

github: https://github.com/PHPTCloud
Download Telegram
#exp #arch #gex
Слой доменов

❓Из чего состоит слой доменов?
Как я писал ранее, слой состоит из бизнес сущностей и бизнес логики. Бизнес сущность - это ограниченный контекст вашего бизнеса, который подчиняется некоторому набору бизнес правил (бизнес логике).

Предположим, вы делаете сайт знакомств.

❓Какие можно выделить сущности?
1️⃣ Пользователь - информация о пользователе (ФИО, текст "о себе");
2️⃣ Лайк - запись о том, кто кого лайкнул;

❓Какие модули с бизнес логикой можно выделить?
1️⃣ Действие "Поставить лайк";
2️⃣ Действие "Отменить лайк".

А вот, например, действие "Загрузка фото пользователя" не будет являться доменной бизнес логикой - его следует выносить в слой приложения. И отслеживать эту операцию нужно через EventBus (о нем я обязательно напишу, когда буду рассказывать о слое приложения).
👍5🤔4🔥2
#exp #gex #arch
Кстати, на практике я заметил, что в доменном слое можно "обойтись" без реализации бизнес логики. И оставить в нем только интерфейсы, которые декларируют контракты модулей этого слоя.

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

❓Где же тогда разместить саму бизнес логику?

Все просто. Для интерфейса ApplyLikeInterface вы делаете слой DomainCore, для модуля ApplyLike с вашей бизнес логикой вы должны сделать Domain слой.
👍6🔥2
#exp #arch #gex
Порты и Адаптеры
Альтернативное название Гексагональной архитектуры - “Порты и адаптеры”. Это название лучше отражает принцип работы этого паттерна, так как шестиугольник (гексагон) не является строгим требованием паттерна, а вот наличие портов и адаптеров является одним из ключевых факторов.

❓Что такое порты?
Определение портов довольно простое. По сути это любая точка входа в вашем приложении. Это может быть графический интерфейс, командная строка, даже публичный метод класса - все это является портами.

❓Что такое адаптеры?
Точки входа (порты) мы определили. Теперь наша задача - передать данные через порты так, чтобы сохранился приемлемый уровень изоляции одного слоя от другого. В этом нам помогают адаптеры. Они позвляют транспортировать данные в другой слой, ничего не зная о деталях реализации модулей, которые мы хотим использовать. Максимум, который нам известен при использовании адаптеров, - это некий контракт взаимодействия.

Используя порты в качестве точек входа, а адаптеры в качестве транспортировщиков данных, система обретает гибкость, которая позволит в будущем легче масштабироваться. Например, использовать другой бизнес-логический модуль, фреймворк, базу данных и так далее.

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

Ниже я расскажу, какие ограничения накладываются на подобные перемещения.
👍5🔥2
#arch #note #solid
Направление зависимостей или Принцип инверсии зависимостей

#️⃣ Принцип инверсии зависимостей — The Dependency Inversion Principle или DIP — один из пяти основных принципов объектно‑ориентированного программирования и проектирования (SOLID), сформулированных Робертом Мартином.

Принцип декларирует, что:
1️⃣ Модули верхних уровней не должны зависеть от модулей нижних уровней, и оба типа модулей должны зависеть от абстракций;
2️⃣ Сами абстракции не должны зависеть от деталей, а вот детали должны зависеть от абстракций.
👍6🔥2
#exp #arch #gex
Принцип инверсии зависимостей отчетливо прослеживается, когда мы используем слой представления, слой приложения, доменный слой и слой инфраструктуры. Стоит заметить, что при правильном использовании DIP все зависимости направлены внутрь системы. От конкретных деталей ко все более и более абстрактным модулям на других слоях.

Благодаря этому принципу вы сможете проектировать модули так, чтобы они не зависели от представлений или от конкретных внешних систем. Именно это помогает достичь необходимого уровня изоляции каждой отдельно взятой системы или модуля и создать "идеальную" среду для тестирования.
🔥5👍3🤔2
#exp #gex #arch
Слой приложения

#️⃣ Давайте вспомним, за что отвечает слой приложения. В данном слое расположены ваши Use Cases. Это некие агрегаторы, которые вызывают несколько разных логических модулей.

Помните про действия “Поставить лайк” (ApplyLike) и “Отменить лайк” (CancelLike) из поста про слой доменов? В слое приложения мы можем сделать use case “toggleLike” - это будет переключатель лайка. Если пользователь кликает на фото другого пользователя и у него еще нет лайка, то лайк ставится (ApplyLike), в противном случае лайк отменяется (CancelLike). В данном примере toggleLike - это Use Case двух бизнес логик.

В гексагональной архитектуре действует постулат: все Use Cases должны храниться в слое приложения и только там. Также не забываем о том, что данный слой должен иметь порты и адаптеры для того, чтобы его можно было вызвать из других слоев, например, из слоя представления (API, GUI, etc.).

На самом деле, это практически все, что необходимо знать об этом слое - его не стоит сильно усложнять. Единственное, что мы позже в него добавим, - взаимодействие со слоем инфраструктуры через EventBus.
👍6🔥4
#exp #gex #arch
Инфраструктурный слой (ИС)


❓ Что такое инфраструктурный слой?

Инфраструктурный слой - это слой для связи приложения с внешним миром. Однако, стоит ограничивать общение этого слоя, так как в противном случае он может очень сильно разрастись. Обычно в этот слой относят взаимодействия с:
1️⃣ Базой данных;
2️⃣ Системами индексации контента;
3️⃣ Кэшем;
4️⃣ Файловой системой или файловым хранилищем;
5️⃣ ORM;
6️⃣ Фреймворками;
7️⃣ Сторонними библиотеками;
8️⃣ Брокерами очередей
... и многими другими инструментами.

Некоторые разработчики ошибочно включают в этот слой такие модули как GUI, API и т.п., то есть те модули, с которыми непосредственно работает конечный пользователь системы. Это большая ошибка, потому что они перегружают слой, и постепенно он становиться трудно изменяемым. Чтобы избавиться от этой проблемы, данный слой разделяют на подмножества: слой доступа к данным (Data Access Layer) и слой представления данных (Data Presentation Layer).

Хочу отметить, что ИС - один из самых эффективных слоев, который позволяет сделать ваше ПО чрезвычайно устойчивым к изменениям требований (любых требований). Фактор устойчивости крайне важен - он помогает вашей системе деградировать медленнее.

Формируя инфраструктурный слой своего приложения, старайтесь делать адаптеры более абстрактными. Адаптеры с более абстрактными методами легче заменить, чем адаптеры, которые сильно связаны с деталями реализации.
👍6🔥4
#arch #gex #exp
Инфраструктурный слой. Что в него выделять?


Все. Все, что сможете (любые внешние модули).

Библиотеки. Я стараюсь не использовать зависимости проекта напрямую. Лучше сделать адаптер в инфраструктурном слое, чтобы библиотеку можно было легко заменить. Ведь завтра релизнется новая более удобная и производительная либа, которую я захочу подключить в свой проект.

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

Прикрываем тыл. Бывает так, что на старте нового проекта вы приняли решение использовать Инструмент А, потому что вы с ним лучше знакомы, но в какой-то момент вы поняли, что это была плохая идея, и вам нужен Инструмент Б. Если вы не сможете быстро и бесшовно внедрить Инструмент Б, то цена такой ошибки может оказаться очень большой (деньги компании, ваше время, время коллег и нервы).
👍6🔥4
#exp #arch #gex
Слой представления

❓Что такое слой представления?

Данный слой - это подмножество инфраструктурного слоя. В инфраструктуру обычно выделяют минимум две задачи: доступ к данным (Data Access Layer) и представление данных (Data Presentation Layer). В слой представления обычно выносят все, с чем взаимодействуют конечные пользователи. Это может быть графический интерфейс, командная оболочка, HTTP API и т.п.

#️⃣ Немного о факторе устойчивости

Ранее я писал о слое инфраструктуры и его подмножестве - слое доступа к данным (Data Access Layer). Упоминался такой фактор как устойчивость к изменениям. Данный фактор/критерий является частью общей теории о Функциональной устойчивости информационных систем. По сути он означает то, что ваша система должна уметь быстро изменяться. Например, пользователи вашего приложения начнут уходить в сервис, который визуально удобнее вашего. Вы должны иметь возможность без лишних замедлений обновить свою систему, чтобы пользователям стало комфортнее. И такие банальные проблемы как неразделенные слои не должны вам мешать.

Также под устойчивостью подразумевается исправление ошибок. Плохо спроектированная архитектура может заметно увеличить время исправления бага. Если возникает подобная проблема, значит, ваша система в некоторой степени не устойчива к изменениям. Решить это можно регулярным проведением качественного рефакторинга архитектуры вашей системы.
👍4🔥3🤯3
#note #cbse #arch
Компонентная разработка программного обеспечения | Component-based software engineering (CBSE)

❓ Что такое компонентная разработка?

Компонентно-ориентированное программирование — это передовая парадигма разработки программного обеспечения, которая подчеркивает модульность, повторное использование и разделение задач путем построения сложных систем посредством композиции более мелких независимых компонентов. Эти компоненты, обычно инкапсулированные в отдельные блоки, называемые «модулями», представляют собой автономные, слабосвязанные и допускающие многократное использование объекты, предназначенные для выполнения конкретных задач или выполнения определенных функций в системе.

❓ Когда следует использовать?

1️⃣Когда компоненты должны быть заменяемыми. Это означает, что компонент должен быть заменен другим, имеющим те же интерфейсы, без нарушения остальной части системы.
2️⃣Когда компоненты должны быть многоразовыми.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥4
#exp #arch #gex
Разделение на компоненты и слайсы (Feature slicing)

Компонентно-ориентированную разработку можно использовать где угодно. Эта парадигма не связана с гексагональной архитектурой напрямую. Однако, паттерн гексагона описывает некий Component (закрашенная синяя область на схеме), в котором стоит разобраться.

❓ Что такое компонент с точки зрения гексагональной архитектуры?

В гексагональной архитектуре под компонентом понимается набор модулей, который относится к одной конкретной сущности вашего приложения. Это может быть бизнес-сущность Пользователь, Товар или что-то еще. Самое главное, что модули этого компонента могут находиться на разных слоях и объединяться в CBSE компоненты.

На практике не очень удобно, что приходится оперировать одним и тем же термином (компонент) в разных контекстах. Я предлагаю выделять следующие правила для гексагоналки:

1️⃣ Термин компонент подразумевается только в конексте CBSE;
2️⃣ Набор компонентов, которые можно сгруппировать на нескольких слоях, будет называться слайс (slice или feature slice);
3️⃣ Составление компонентов происходит по правилам CBSE. За одним исключением: компоненты на внутреннем уровне не должны зависеть от компонентов на внешнем уровне. Соблюдаем принцип инверсии зависимостей.

❓ Чем полезен feature slice?

Слайсы привносят в паттерн гексагональной архитектуры не только понятие CBSE, но и способ организации кода. Когда вы думаете не только о том, как организовать компонент, но и о том, как организовать слайс, то проект становится более структурированным. Это позволяет не только быстро и удобно разрабатывать фичи, но и делать это максимально изолированно от других слайсов и их компонентов.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🤯3🔥2
#arch #cqs #cqrs #note
Command Query Responsibility Segregation (CQRS)

❓ Что такое CQS (Разделение команд и запросов / Command-Query separation)

Первым упоминанием CQS считается книга B. Meyer Object Oriented Software Construction. Основная идея CQS заключается в том, что в объекте методы могут быть двух типов:

1️⃣ Команды (Commands) - методы, которые изменяют состояние объекта, не возвращая значение;

2️⃣ Запросы (Queries) - методы, которые возвращают результат, не изменяя состояние объекта.


❓ Что такое CQRS (Разделение ответственности на Команды и Запросы / Command Query Responsibility Segregation)

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

Преимущества CQRS включают:

1️⃣ Независимое масштабирование. CQRS позволяет раздельно масштабировать рабочие нагрузки чтения и записи, снижая риск конфликтов блокировки.

2️⃣ Оптимизация схем данных. Для процессов чтения можно применить схему, оптимизированную для запросов, а для процессов записи — другую схему, оптимизированную для обновлений.

3️⃣ Четкое разделение зон ответственности. Разделение процессов чтения и записи позволяет получить более гибкие и простые в обслуживании модули. Бо́льшая часть сложной бизнес-логики переместится в модель записи. Это в некоторой степени упростит модель чтения.

4️⃣ Более простые запросы. Сохраняя в базе данных для чтения материализованное представление данных, вы предотвратите использование приложением сложных соединений в запросах.

https://cqrs.nu
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍4
#exp #gex #arch
Command/Query bus на практике


Когда мы принимаем на вход какие-либо данные от пользователей, мы обязательно валидируем входящие данные и формируем ответ в нужном формате. Обычная схема в таком случае это Controller => Use Case => Domain Logic => Responder.

Controller - принимает и валидирует данные;
Use Case - агрегирует и использует доменную логику (Domain Logic);
Responder - формирует ответ в нужном формате.

Обычно этого хватает на старте, но с развитием системы могут появиться проблемы: разработчик забыл написать валидацию, нарушение конвенций или их отсутствие, некачественная валидация и т.д.

Решением подобных проблем являются внедрение правил CQRS и организация слоя Command/Query bus.

❓ Что такое Command/Query bus?

CQB - это изолированный слой системы, который несет ответственность за все входящие данные и формирует выходящие данные.

Для CQB можно выделить следующие задачи:

1️⃣ формирует команду/запрос;

2️⃣ валидирует команду/запрос;

3️⃣ передает её в обработчик команды/запроса;

4️⃣ обработчик команды/запроса формирует ответ*.

* В случае команд (запись данных) можно отдать стандартный ответ из контролера

CQB унифицирует работу с входными/выходными данными и предотвращает передачу некорректных данных в слой бизнес логики.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍4
#exp #arch #gex
Выводы о Гексагональной архитектуре


✅ Преимущества

1. Строго декларируются зоны ответственности, которые инкапсулируются в слоях приложения;
2. Бизнес-логика отделяется от логики системы, что упрощает миграцию кода;
3. Строго определяется направление зависимостей в соответствии с DIP;
4. Исчезает проблема зависимости от конкретных технических и технологических решений;
5. Бизнес-логика и системная логика отделяются от модулей представления;
6. Появляются правила объединения модулей в компоненты, которые можно переиспользовать в других системах;
7. Появляются правила CQRS, позволяющие увеличить производительность системы.


🚫 Недостатки

1. Очень высокий порог входа в концепции паттерна;
2. Увеличение количества слоев может затруднить понимание работы системы;
3. Большое количество кодовой базы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
#gex #navi
Первая цепочка постов о Гексагональной архитектуре подошла к концу. Надеюсь, вы получили много полезой информации о том, как работать с рассмотренным паттерном. Чтобы глубже понять Гексагоналку, нам потребуется больше времени, а пока продолжим погружаться в другие интересные темы проектирования архитектуры.

Навигация по постам

1. Что такое Гексагональная архитектура?
2.
Что такое слои приложения, и зачем они нужны?
3.
Слой доменов Часть 1, Часть 2
4.
Порты и Адаптеры
5.
Принцип инверсии зависимостей в Гексагональной архитектуре
6.
Слой приложения
7.
Инфраструктурный слой
8.
Инфраструктурный слой. Что в него выделять?
9.
Слой представления
10.
Разделение на компоненты и слайсы (Feature slicing)
11.
Command/Query bus на практике
👍5🔥4
#arch #solid #note
Принцип единственной ответственности — The Single Responsibility Principle или SRP

Принцип единственной ответственности — The Single Responsibility Principle или SRP — один из пяти основных принципов объектно‑ориентированного программирования и проектирования, сформулированных Робертом Мартином.

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

Следование принципу заключается обычно в декомпозиции сложных классов, которые делают сразу много вещей, на простые, отвественность которых очень специализирована. Но также и объединении в отдельный класс однотипной функциональности, которая может оказаться распределённой по многим классам, может рассматриваться как следование этому принципу.

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

Следование SRP весьма полезная практика с точки зрения повторного использования кода. Сложные объекты с комплексными зависимостями обычно очень сложно использовать повторно, особенно если нужна только часть реализованного в них функционала. А небольшие классы с чётко очерченным функционалом, напротив, проще использовать повторно, так как они не избыточные и редко тянут за собой существенный объём зависимостей.
👍6🔥1
#arch #solid #note
Принцип открытости/закрытости — The Open Closed Principle или OCP

Принцип декларирует, что программные сущности (классы, модули, функции и т. п.) должны быть открыты для расширения, но закрыты для изменения. Это означает, что эти сущности могут менять свое поведение без изменения их исходного кода.

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

Следование принципу OCP заключается в том, что программное обеспечение изменяется не через изменение существующего кода, а через добавление нового кода. То есть созданный изначально код остаётся «нетронутым» и стабильным, а новая функциональность внедряется либо через наследование реализации, либо через использование абстрактных интерфейсов и полиморфизм.

Принцип открытости/закрытости Мейера основывается на идее, что разработанная изначально реализация класса в дальнейшем не модифицируется (разве что исправляются ошибки), а любые изменения производятся через создание нового класса, который обычно наследуется от изначального. Согласно определению Мейера реализация интерфейса может быть унаследована и переиспользована, но интерфейс может и измениться в новой реализации.
👍5🔥3
#arch #solid #note
Принцип подстановки Барбары Лисков — The Liskov Substitution Principle или LSP

Принцип в формулировке Роберта Мартина декларирует, что функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа не зная об этом. Оригинальное определение Барбары Лисков более формальное и заметно сложнее для восприятия: «В том случае, если q(x) — свойство, верное по отношению к объектам х некого типа T, то свойство q(y) тоже будет верным относительно ряда объектов y, которые относятся к типу S, при этом S — подтип некого типа T».

Следование принципу LSP заключается в том, что при построении иерархий наследования создаваемые наследники должны корректно реализовывать поведение базового типа. То есть если базовый тип реализует определённое поведение, то это поведение должно быть корректно реализовано и для всех его наследников.

LSP перекликается с контрактным программированием, определяя точные, формальные и верифицируемые описания интерфейсов. И интерфейсы, реализумые наследниками, должны соответствовать контракту интерфейсов базового класса.

Наследник класса дополняет, но не заменяет поведение базового класса. То есть в любом месте программы замена базового класса на класс‑наследник не должна вызывать проблем. Если по каким‑то причинам так не получается, то вероятнее всего имеет место либо некорректная реализация, либо неверно выбранная абстракция для наследования.

Соблюдение принципа подстановки Барбары Лисков позволяет гарантировать, что любой созданный нами подкласс будет без проблем использоваться ранее реализованными модулями, которые работали с надклассом. А это существенно упрощает расширение функциональных возможностей системы.

Но LSP, как и любой другой принцип, не является догмой. И иногда следование этому принципу при построении архитектуры может приводить к более ресурсоёмкой реализации, нежели работа с нарушением этого принципа. Но как и с любыми другими правилами — надо осознавать возможные последствия нарушения.
👍4🔥3
#arch #solid #note
Принцип разделения интерфейса — The Interface Segregation Principle или ISP

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

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

В чём‑то принцип разделения интерфейса перекликается с принципом единственной ответственности — интерфейсы не должны быть избыточно «толстыми», если вдруг в приложении формируется слишком объёмный интерфейс, то есть высокая вероятность, что так происходит из‑за того, что в этом интерфейсе слишком много разных ответственностей, а значит логичнее всего провести декомпозицию сложного интерфейса на несколько простых.

Принцип разделения интерфейса снижает сложность поддержки и развития приложения. Чем проще и минималистичнее используемый интерфейс, тем менее ресурсоёмкой является его реализация в новых классах, тем меньше причин его модифицировать, но и в случае модификации она будет значительно проще.
👍4🔥3
#arch #note #solid
Принцип инверсии зависимостей — The Dependency Inversion Principle или DIP

Принцип декларирует, что модули верхних уровней не должны зависеть от модулей нижних уровней, а оба типа модулей должны зависеть от абстракций; сами абстракции не должны зависеть от деталей, а вот детали должны зависеть от абстракций.

Следование принципу инверсии зависимостей «заставляет» реализовывать высокоуровневые компоненты без встраивания зависимостей от конкретных низкоуровневых классов, что, например, сильно упрощает замену используемых зависимостей как по бизнес‑требованиям, так и для целей тестирования. При этом зависимость формируется не от конкретной реализации, а от абстракции — реализуемого зависимостью интерфейса.

Например, мы реализуем хранение документов в веб‑приложении. На первый взгляд, кажется логичным добавить зависимость от модулей работы с файловой системой непосредственно в класс, отвечающий за высокоуровневую работу с этими документами. Но в перспективе такая зависимость может создать проблемы — например, нам потребуется хранить данные не только на диске, но и в облаке. Если зависимость внедрена от реализации, то мы столкнёмся с необходимостью её переработки. Если же зависимость выведена на уровень абстракции (интерфейса), то нам будет достаточно реализовать функционал работы с облаком, соответствующий ранее созданному интерфейсу работы с файлами.

Принцип инверсии зависимостей часто упрощает следованию принципу подстановки Барбары Лисков. Выделение абстракций и встраивание зависимостей от них снижает вероятность того, что в новом классе мы не полностью реализуем контракт базового класса, который мы расширяем в рамках нового.
👍4🔥2
#arch #note #solid #solid4mens
Принцип Стабильных Зависимостей - The Stable Dependencies Principle или SDP

«Не получится строить стабильное на нестабильном»

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

Зацепление — мера того, насколько взаимозависимы разные подпрограммы или модули.

Сильное зацепление рассматривается как серьёзный недостаток, поскольку затрудняет понимание логики модулей, их модификацию, автономное тестирование, а также переиспользование по отдельности. Слабое зацепление, напротив, является признаком хорошо структурированной и хорошо спроектированной системы.
👍4🔥3🤯3