По какому признаку единая причина изменения помогает определить границу модуля?
Единая причина изменения указывает на высокую связность модуля: его части меняются вместе по одному бизнес- или техническому поводу. Такие части обычно стоит держать внутри одной границы, а обязанности с независимыми причинами изменений — разделять.
Главный практический критерий — не размер модуля и не количество функций, а то, насколько часто его элементы изменяются совместно. Слишком широкая граница приводит к каскадным изменениям, слишком узкая — к лишним вызовам и усложнению координации.
Идея связности сформировалась в структурном проектировании как способ управлять сложностью больших программ. Наблюдение было простым: систему легче понимать и изменять, когда элементы внутри одного модуля тесно связаны, а взаимодействие между модулями минимально и явно.
Позднее этот принцип стал частью объектного проектирования и архитектуры ПО. Он дополняет принцип слабой связанности: внутри границы должны находиться обязанности, объединённые общей причиной изменения, а между границами следует оставлять устойчивые контракты.
Предположим, модуль одновременно рассчитывает цену заказа, отправляет уведомления и формирует отчёт для бухгалтерии. Цена меняется из-за правил продаж, уведомления — из-за требований каналов связи, а отчёт — из-за финансового регулирования.
Если всё оставить в одной границе, изменение одного требования увеличивает риск затронуть несвязанные части. Если разделить каждую небольшую функцию в отдельный модуль, появятся лишние контракты, преобразования данных и координационные вызовы.
Поэтому границу нельзя выбирать только по техническому размеру компонента. Нужно определить, какие элементы имеют общую причину изменения и какие зависимости между ними действительно необходимы.
Сначала для каждой ответственности фиксируют наиболее вероятные причины её изменения: изменение бизнес-правила, внешнего формата, политики безопасности, способа хранения или требований к представлению. Если несколько обязанностей обычно меняются по одному поводу, их совместное размещение повышает связность модуля.
Например, правила расчёта скидки, проверка права на скидку и объяснение причины её применения могут образовывать единый модуль ценообразования. Они используют общие понятия и должны эволюционировать согласованно.
Граница проходит между обязанностями, у которых разные владельцы изменений или разные темпы эволюции. Контракт на границе должен передавать устойчивые бизнес-смыслы, а не детали внутренней реализации.
Важно отличать связность от простого тематического сходства. Две функции могут относиться к одному предметному словарю, но изменяться по разным причинам. И наоборот, технически разные операции могут принадлежать одной границе, если их нельзя независимо изменить без нарушения единого правила.
Компромисс состоит в том, что более крупный модуль упрощает локальные транзакции и уменьшает число взаимодействий, но снижает независимость изменений. Более мелкие модули дают локализацию изменений, однако увеличивают стоимость контрактов, тестирования интеграций и согласования версий.
Границу следует пересматривать по фактической истории изменений. Если два компонента почти всегда выпускаются вместе, это сигнал проверить, не разделены ли они искусственно. Если изменение одного постоянно вызывает изменения другого, граница, вероятно, пересекает общую причину изменения.
В системе интернет-магазина один крупный модуль отвечал за правила цены, публикацию цен в поисковом индексе и подготовку бухгалтерских документов. При изменении скидочной политики приходилось проверять весь модуль, хотя формат бухгалтерского документа не менялся.
Рассматривались три варианта. Оставить границу без изменений было проще всего, но это сохраняло высокий риск побочных изменений. Разделить каждую функцию на самостоятельный сервис уменьшало размер компонентов, однако добавляло сетевые взаимодействия и необходимость согласовывать данные. Третий вариант — выделить модуль ценообразования, интеграцию с поиском и бухгалтерский адаптер — разделял ответственности по причинам изменения, не превращая каждую операцию в отдельный сервис.
Выбрали третий вариант. Правила цены остались в одной внутренне связной границе, а внешние форматы были вынесены в адаптеры. В результате изменение скидок не требовало пересмотра бухгалтерского представления, а границы оставались достаточно крупными для локального тестирования и согласованного изменения правил.
Нет. Нужно дополнительно проверить зависимости данных, инварианты и допустимый способ взаимодействия. Если два набора обязанностей поддерживают общий инвариант, их разделение может потребовать распределённой транзакции или усложнить обработку ошибок. Общая причина изменения — сильный сигнал, но не единственный критерий.
Нет. Связность помогает определить логическую модульную границу, но не диктует физическое развертывание. Несколько хорошо разделённых модулей могут оставаться в одном процессе, если им не нужна независимая масштабируемость, изоляция отказов или автономный жизненный цикл. Преждевременное превращение модулей в сервисы добавляет сетевую задержку, отказоустойчивость и операционные расходы.
Нужно искать доминирующую причину и проверять реальные сценарии изменений. Иногда общая причина указывает на общий модуль, но иногда — на недостаточно точную модель предметной области. Полезно сравнить историю изменений, владельцев требований и наборы данных: если совместное изменение происходит только из-за текущей реализации, а не из-за бизнес-инварианта, границу не следует сохранять искусственно.