Как оценить архитектуру? Пост 1 из 8

В прошлом посте мы определили сильную архитектуру так: Звучит хорошо. Но как понять, что архитектура действительно сильная?

Посмотреть на структуру папок? Посчитать количество интерфейсов? Проверить, используются ли DDD, CQRS и Clean Architecture?

Нет.

Красивые диаграммы и правильные названия классов ещё ничего не гарантируют. Архитектура по-настоящему проявляет себя в двух ситуациях:

🔹 когда систему нужно изменить; 🔹 когда в системе что-то сломалось. Архитектура не бывает сильной сама по себе Архитектуру нельзя оценивать в отрыве от задач системы.

Для лендинга, который проживёт три месяца, сложная доменная модель и пять слоёв абстракций, скорее всего, будут лишними. А для платёжной системы отсутствие строгих границ, аудита и защиты от повторных операций может стоить реальных денег.

Поэтому первый вопрос при оценке архитектуры: Какие свойства важны именно для этой системы? Это могут быть:

🔹 Скорость внесения изменений; 🔹 Надёжность; 🔹 Производительность; 🔹 Безопасность; 🔹 Масштабируемость; 🔹 Тестируемость; 🔹 Наблюдаемость; 🔹 Простота разработки.

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

Архитектура - это всегда компромиссы.

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

Допустим, интернет-магазину нужно добавить второго платёжного провайдера.

В одном проекте для этого придётся:

🔹 добавить условие в контроллер; 🔹 изменить `OrderService`; 🔹 расширить модель заказа; 🔹 исправить несколько обработчиков событий; 🔹 изменить таблицу платежей; 🔹 переписать часть существующих тестов.

Причём заранее никто не уверен, какие ещё участки будут затронуты.

В другом проекте новый провайдер реализует существующий контракт:

```php interface PaymentGateway { public function charge(Payment $payment): PaymentResult; } ```

```php final class NewBankPaymentGateway implements PaymentGateway { public function charge(Payment $payment): PaymentResult { // Вызов API нового банка } } ```

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

Дело тут не в самом интерфейсе. Если поставить интерфейс перед каждым классом, архитектура автоматически лучше не станет.

Разница в другом: во втором случае система заранее предусматривает точку расширения, а изменение остаётся внутри ожидаемой границы.

Но и здесь важно не переборщить. Если новый платёжный провайдер никогда не появится, заранее построенная сложная система абстракций может оказаться бесполезной.

Поэтому хороший вопрос звучит не так:

- Можно ли добавить новую возможность, изменив один файл?

А так:

- Можно ли заранее понять, какие части системы затронет изменение и почему?

Количество изменённых файлов само по себе ничего не доказывает. Главное - чтобы изменение проходило по понятному маршруту и не затрагивало несвязанные части системы Проверка отказом Теперь представим, что API платёжного провайдера перестало отвечать.

Что произойдёт?

🔹 зависнет HTTP-запрос пользователя; 🔹 заказ создастся, но платёж потеряется; 🔹 операция автоматически повторится; 🔹 клиент случайно заплатит дважды; 🔹 ошибка попадёт в лог, но её никто не заметит; 🔹 проблема затронет только платежи или положит всё приложение?

Сильная архитектура проектирует не только успешный сценарий. Она ограничивает последствия отказов.

Для этого система должна уметь ответить хотя бы на несколько вопросов:

🔹 Где произошла ошибка; 🔹 Какие данные она затронула; 🔹 Можно ли******

В сетке пост не умещается, переходите в ТГ канал. Там полный пост :) https://t.me/retro_s_pristrastiem