Client-Side Architecture Basics: II. Principles (2020)
Данная статья представляет собой вторую часть руководства по архитектуре клиентских приложений, в которой автор рассматривает ключевые принципы проектирования, применимые к фронтенд-разработке. Хотя принципы «чистой архитектуры» (Clean Architecture) эффективны, на стороне клиента не требуется их точная копия; вместо этого важно адаптировать фундаментальные идеи, которые лежат в их основе. Автор выделяет два наиболее значимых принципа: разделение команд и запросов (CQS) и разделение ответственности (SoC).
Command-Query Separation (CQS) — это принцип, согласно которому любая операция является либо командой, либо запросом. Команды изменяют состояние системы, но не возвращают данные, тогда как запросы возвращают данные, но не вызывают побочных эффектов и не меняют состояние. Основным преимуществом этого паттерна является упрощение рассуждений о коде за счет четкого разделения путей чтения и записи. Это облегчает тестирование, так как проверку запроса проще проводить, если он гарантированно не меняет систему, а также помогает в решении проблем с инвалидацией кэша: кэш остается актуальным до тех пор, пока не будет выполнена команда. Примерами использования CQS являются API хука useState в React и структура GraphQL с его запросами и мутациями.
Separation of Concerns (SoC) подразумевает осознанное соблюдение логических границ между различными архитектурными задачами приложения. Автор отмечает, что даже в простых приложениях одно действие пользователя (например, удаление элемента из списка) затрагивает множество аспектов: отображение, бизнес-логику, сетевые запросы и обновление состояния. Вместо хаотичного размещения разных видов логики (авторизации, валидации и т. д.), их следует классифицировать и делегировать соответствующим слоям.
Взаимодействие этих принципов приводит к концепции «вертикальных срезов» (vertical slices), где каждая функция приложения рассматривается как срез, проходящий через весь стек. Когда разработчик изменяет или добавляет функцию, он работает с конкретным вертикальным срезом, затрагивая только нужные слои (презентационный, слой логики взаимодействия и т. д.). Такой подход минимизирует зависимости между срезами и максимизирует связность внутри них, что позволяет быстрее находить места в коде для внесения изменений. Понимание ответственности каждого слоя помогает разработчикам осознанно выбирать инструменты (например, Apollo Client для данных или Redux для состояния) и решать, стоит ли использовать готовую библиотеку или создавать собственное решение для конкретного слоя.