Как оценить архитектуру? Пост 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