Перейти к содержанию

Архитектурные стили и антипаттерны; связанность и связность модулей

Об этом материале

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

Материал для самостоятельной работы после лекции 2. Лекция дала два понятия — связность и зацепление — и критерий «модуль должно быть можно удалить». Здесь они превращаются в систематику: шкалы вместо ярлыков, каталог архитектурных стилей с ценой каждого, каталог антипаттернов и метрики, которыми зависимости можно измерить. Ориентировочно неделя-полторы.

Что входит в этот материал

  1. Связность и зацепление как шкалы, а не как ярлыки
  2. Стили архитектуры
  3. Антипаттерны
  4. Метрики и правила зависимостей

Что уже было в лекции

Сильная связность внутри модуля, слабое зацепление между модулями; определение архитектуры; критерий «модуль должно быть можно удалить»; MVC и MVVM. Дальше — систематика.

Как работать с этим документом

  • Читайте не подряд, а по темам: у каждой есть вопрос, на который она отвечает.
  • К каждому уровню связности и зацепления подберите свой пример из кода, который видели. Без примера уровень не изучен.
  • К каждому стилю запишите одну строку: «решает X ценой Y».
  • К каждому антипаттерну — случай из своей практики или из открытого репозитория.
  • Инструменты: графы зависимостей сборок, SonarQube или NDepend для метрик, NetArchTest для правил как тестов.

Тема 1. Связность и зацепление как шкалы, а не как ярлыки

Обе характеристики имеют классификацию (Стивенс, Майерс, Константайн, 1974) — от худшей к лучшей.

Связность (cohesion), снизу вверх:

Уровень Что объединяет элементы модуля Пример
Случайная ничего класс Utils, Helpers, Common
Логическая похожая категория действий InputHandler, обрабатывающий мышь, клавиатуру и файлы
Временная выполняются в один момент Startup, где инициализируется всё подряд
Процедурная порядок выполнения шаги, идущие подряд, но не связанные данными
Коммуникационная работают с одними данными отчёт, читающий и печатающий одну выборку
Последовательная выход одного — вход другого пайплайн обработки
Функциональная решают одну задачу PriceCalculator

Зацепление (coupling), сверху вниз (от худшего):

Уровень Суть Как выглядит
По содержимому модуль лезет во внутренности другого рефлексия по приватным полям
Общее общая глобальная область статические изменяемые данные, синглтоны
Внешнее общий внешний формат/протокол все модули читают один и тот же файл конфигурации
По управлению один модуль управляет ветвлением в другом булев параметр-флаг Process(bool isRefund)
По структуре данных передаётся структура, нужна её часть передаём весь Order ради Total
По данным передаются ровно нужные значения Calculate(decimal amount)

Отдельно изучите connascence (Мейлир Пейдж-Джонс) — более современный и точный язык, чем «coupling»: связность по имени, типу, значению, позиции, алгоритму, времени выполнения, с двумя измерениями — сила и локальность. Хорошая практическая формулировка: чем сильнее форма связности, тем ближе должны находиться связанные элементы.


Тема 2. Стили архитектуры

Пройдите по каждому: какую проблему решает, чем платит, когда неуместен.

  • Слоистая (layered). Классика: представление → приложение → домен → инфраструктура. Проблема: зависимость домена от инфраструктуры вниз по стеку.
  • Луковая и Clean Architecture. Инверсия зависимостей на уровне слоёв: домен в центре и не зависит ни от чего, интерфейсы репозиториев объявлены в домене, реализации — снаружи.
  • Порты и адаптеры (гексагональная). Та же идея в других терминах: ядро окружено портами, адаптеры подключают к нему БД, HTTP, очереди. Полезно понять, что onion/clean/hexagonal — это варианты одной мысли.
  • Pipes and Filters. Обработка потоками; естественно ложится на LINQ, IAsyncEnumerable, каналы Go, стримы Java.
  • Микроядро (plugin). Ядро + расширения; так устроены IDE, сборщики, браузерные движки.
  • Событийно-ориентированная. Модули общаются событиями, зацепление минимально, но появляется трудность трассировки и порядка. Изучите разницу «событие как факт» и «событие как команда».
  • CQRS и, отдельно, Event Sourcing — почему их часто путают и почему второе почти никогда не нужно там, где хватает первого.
  • Монолит, модульный монолит, микросервисы. Обязательно прочитайте критику микросервисов и понятие распределённого монолита.
  • MV*-семейство (MVC, MVP, MVVM, MVI/Redux) — уже разобрано в лекции 2, здесь только дополните MVI/однонаправленным потоком данных.

Для каждого стиля выпишите одну строку: «решает X ценой Y».


Тема 3. Антипаттерны

Архитектурные:

  • Big Ball of Mud — отсутствие структуры как результат эволюции; самая распространённая «архитектура» в природе.
  • Distributed Monolith — сервисы разделены физически, но связаны намертво: релиз только всем скопом.
  • Anemic Domain Model — сущности без поведения плюс «сервисы», делающие всё; прямая связь с запахами Data Class и Feature Envy из лекции 2. Обязательно прочитайте статью Фаулера и контраргументы к ней: тема спорная, и знать обе стороны полезнее, чем занять сторону.
  • God Object / God Class — на уровне класса и на уровне проекта.
  • Golden Hammer — любимая технология как ответ на все задачи.
  • Lava Flow — окаменевший мёртвый код, который боятся трогать.
  • Vendor Lock-in и Inner-Platform Effect — самописная «платформа внутри платформы».
  • Spaghetti / Ravioli / Lasagna — три способа испортить структуру: хаос, избыточное дробление, слишком много слоёв.

Управленческие и процессные (пригодятся не меньше): Analysis Paralysis, Design by Committee, Cargo Cult (соблюдение формы без понимания причины), Resume-Driven Development.

Задание при чтении: к каждому антипаттерну подобрать пример из собственного опыта или открытого репозитория. Без примера пункт не считается изученным.


Тема 4. Метрики и правила зависимостей

  • Метрики Мартина: Ca, Ce, нестабильность I, абстрактность A, расстояние до главной последовательности D. Понять смысл: стабильные модули должны быть абстрактными, нестабильные — конкретными.
  • Принципы связности пакетов: REP (переиспользование = релиз), CCP (вместе меняется — вместе лежит), CRP (не заставляй зависеть от лишнего).
  • Принципы зацепления пакетов: ADP (без циклов), SDP (зависимость в сторону устойчивости), SAP (устойчивое должно быть абстрактным).
  • Закон Деметры и его практические границы (почему LINQ-цепочки его формально нарушают и почему это нормально).
  • Закон Конвея — структура системы повторяет структуру коммуникаций в организации; и «обратный манёвр Конвея» как способ этим управлять.
  • Инструменты: NDepend, SonarQube, графы зависимостей сборок, dotnet list package --include-transitive; тесты архитектуры (NetArchTest, ArchUnitNET) — превращение правил в проверяемые ограничения.

Вопросы для самопроверки

  1. Чем логическая связность отличается от функциональной? Приведите свой пример класса с логической связностью.
  2. Почему булев параметр в сигнатуре считается зацеплением по управлению и чем его заменить?
  3. Onion, Clean, Hexagonal — в чём они действительно различаются, а в чём это одно и то же?
  4. Как отличить микросервисы от распределённого монолита по одному признаку?
  5. Anemic Domain Model — всегда ли антипаттерн? Сформулируйте позицию и контраргумент.
  6. Что означает высокое I и низкое A одновременно, и почему это плохо?

Практика

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

Литература

  • Р. Мартин. Чистая архитектура — принципы компонентов, метрики связности и зацепления, границы.
  • М. Фаулер. Рефакторинг — запахи, из которых вырастают архитектурные дефекты.
  • У. Браун и др. AntiPatterns — первоисточник термина.
  • С. Браун. C4 model (c4model.com) — уровни описания архитектуры.
  • Статьи о connascence (Мейлир Пейдж-Джонс) — более точный язык, чем «coupling».

Как я буду это проверять

Письменный отчёт: ответы на вопросы для самопроверки и вывод по практике, плюс код в репозитории. Критерий — не «сделано», а объяснено: почему выбран такой вариант, чем платим, что сломается при изменении требования. Формулировка «так принято» не засчитывается: принято кем и почему.