AI Снижаем когнитивную сложность при проектировании архитектуры приложения

AI

Редактор
Регистрация
23 Август 2023
Сообщения
3 641
Лучшие ответы
0
Реакции
0
Баллы
243
Offline
#1


Когнитивная сложность - это понятие, описывающее сложность процесса познания и мышления. Оно используется в разных областях: в психологии оно характеризует индивидуальную способность к восприятию и обработке информации. Более высокая когнитивная сложность означает, что система (будь то человек или программа) требует больше усилий для понимания и может быть трудной в поддержке.

Когнитивная сложность при проектировании приложения часто возникает из-за смешения архитектуры кода и архитектуры приложения. В большинстве случаев эти термина никак не разделены, а также эти термины не имеют однозначного толкования, как по содержанию так и по контексту использования. В практике и литературе эти понятия часто используются как синонимы или в пересекающихся контекстах, что приводит к неоднозначности. В зависимости от контекста (например, обсуждение микросервисов, монолитов, паттернов проектирования или рефакторинга), один и тот же термин может обозначать как уровень организации кода, так и более высокий уровень организации приложения или системы. В профессиональной литературе и стандартах (например, TOGAF, ArchiMate) архитектура программного обеспечения охватывает оба аспекта и организацию кода, и организацию приложения, что еще больше стирает границы между этими понятиями.

Процессно-ориентированная архитектура приложения


В рамках этой статьи термин "Архитектура приложения" рассматривается исключительно как процессно-ориентированная архитектура приложения.


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

В такой архитектуре компоненты приложения описываются как отдельные процессы, а не как абстрактные функциональные модули внутри одного процесса.

Каждый процесс выполняет свою задачу и взаимодействует с другими процессами через механизмы межпроцессного взаимодействия (IPC): каналы, сокеты, очереди сообщений, разделяемую память и т.д..
Отличие процессно-ориентированной архитектуры (где элементы — физические процессы ОС) от сервис-ориентированной архитектуры (SOA)

Основное отличие процессно-ориентированной архитектуры (где элементы — физические процессы ОС) от сервис-ориентированной архитектуры (SOA) заключается в уровне абстракции, способе взаимодействия и области применения:


  • В процессно-ориентированной архитектуре элементы — это физические процессы ОС, которые выполняют конкретные задачи и взаимодействуют через механизмы IPC (межпроцессное взаимодействие).


  • В SOA элементы — это сервисы, которые могут быть реализованы как процессы, потоки, или даже функции, но главное — они предоставляют бизнес-функции через стандартизированные интерфейсы и не обязательно соответствуют отдельным процессам ОС.


  • В процессно-ориентированной архитектуре взаимодействие между компонентами обычно происходит через IPC, сокеты, очереди сообщений и т.д., что связано с особенностями ОС.


  • В SOA взаимодействие между сервисами осуществляется через стандартные протоколы (например, HTTP, SOAP, REST), и часто используется шина сервисов (ESB) для управления взаимодействием.


  • Процессно-ориентированная архитектура чаще применяется в системах, где требуется высокая изоляция, отказоустойчивость и управление ресурсами на уровне ОС (например, микроядерные ОС, распределённые системы).


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


  • В процессно-ориентированной архитектуре управление ресурсами и масштабированием происходит на уровне ОС, а сервисы могут быть жестко привязаны к конкретным процессам.


  • В SOA сервисы могут быть независимо развёрнуты, масштабированы и управляться централизованно через ESB или другие средства управления сервисами.

Таким образом, процессно-ориентированная архитектура фокусируется на физических процессах ОС и их взаимодействии, тогда как SOA — на бизнес-сервисах, стандартизированных интерфейсах и интеграции приложений на уровне бизнес-процессов.


Итак, используя процессно-оринетированную архитектуру в качестве архитектуры приложения, критически рассмотрим типичные высказывания, которые сложны для понимания и, к сожалению, часто воспринимаются как некий малосодержательный фон повествования.

Примеры высказываний, возникающих при смешении архитектуры кода и архитектуры приложения:


  • "Приложение использует микросервисную архитектуру, потому что у нас много классов."
    Правильно: "Приложение использует микросервисную архитектуру, потому что его компоненты развернуты как независимые сервисы, взаимодействующие по сети. Количество классов в коде не определяет архитектуру приложения."


  • "Наше приложение построено по принципу Clean Architecture, потому что у нас есть слои Presentation, Business и Data."
    Правильно: "Код приложения организован по принципу Clean Architecture, если слои кода соответствуют принципам инверсии зависимостей и чёткого разделения ответственностей. Архитектура приложения - это способ развертывания и взаимодействия компонентов, а не только структура кода."


  • "Мы используем архитектуру приложения MVC, потому что у нас есть контроллеры, сервисы и репозитории."
    Правильно: "Код приложения использует паттерн MVC, если разделение на контроллеры, представления и модели соответствует принципам MVC. Архитектура приложения - это способ организации компонентов приложения в среде выполнения, а не только структура классов."


  • "Наше приложение имеет архитектуру микросервисов, потому что у нас много модулей."
    Правильно: "Приложение имеет архитектуру микросервисов, если компоненты развернуты как отдельные процессы, взаимодействующие по сети. Количество модулей в коде не определяет архитектуру приложения."


  • "Мы используем архитектуру приложения N-Tier, потому что у нас есть папки Presentation, Business и Data."
    Правильно: "Код приложения организован по принципу N-Tier, если слои кода соответствуют принципам разделения ответственностей. Архитектура приложения - это способ развертывания и взаимодействия компонентов, а не только структура папок."

Кроме проблем с коммуникацией существуют и другие проблемы.

Проблемы при смешении:


  • Разработчики могут применять архитектурные решения на уровне кода (например, сложные паттерны или декомпозицию классов), не учитывая общую архитектуру приложения, что приводит к излишней сложности и неочевидным зависимостям.


  • С другой стороны, принятие архитектурных решений без учета возможностей и ограничений кода может привести к непрактичным, трудно поддерживаемым решениям.


  • Смешение затрудняет понимание системы, увеличивает порог входа для новых участников команды и усложняет поддержку и развитие.

Как минимизировать когнитивную сложность:


  • Четко разделять уровни архитектурных решений: от глобальной структуры до деталей реализации.


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

Таким образом, осознанное разделение архитектуры кода и архитектуры приложения - ключ к снижению когнитивной нагрузки и повышению качества архитектуры.

Далее разбираемся подробно.

Архитектура кода и архитектура приложения


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

Архитектура кода - это организация исходного кода на уровне логических границ, выраженных структурой файлов, модулей, классов, функций и других конструкций языка программирования. Эти границы определяются принципами проектирования, паттернами, стилем кода и стандартами организации проекта. Например, разделение на слои (presentation, business logic, data access), модульность, инкапсуляция, декомпозиция на микросервисы или компоненты - всё это проявляется на уровне исходного кода и не зависит от того, как код будет выполняться.

Архитектура приложения - это организация исполняемого процесса, то есть того, как компоненты системы взаимодействуют между собой в момент выполнения. В исполняемом процессе границы определяются технически: память (код, данные в стеке, данные в куче), процессы, потоки, ресурсы ОС и т.д. Эти границы не обязательно соответствуют логическим границам исходного кода - например, разные модули могут быть загружены в один процесс, а один логический компонент может быть разнесён по нескольким процессам или машинам.

Один и тот же код может дублироваться в разных процессах, даже если в исходной кодовой базе он описан один раз. Например, библиотека вспомогательных функций может быть подключена к нескольким процессам (например микросервисам), и каждый из них будет использовать её независимо. Это не означает, что код физически дублируется в исходниках - он может быть собран в виде отдельной библиотеки и подключаться как зависимость. Однако на уровне архитектуры приложения каждый процесс, использующий эту библиотеку, будет иметь свою копию в своём окружении.

Когда код запускается и становится исполняемым процессом, логические границы исходного кода теряют своё значение, если только они не реализованы через технические границы (например, разные процессы, контейнеры, сервисы). Внутри процесса все данные и код существуют в едином адресном пространстве, и разделение происходит только по типу памяти (стек, куча) и по способу доступа к данным. Исходный код сохраняет свою структуру только как логическая модель, но не как физическая граница выполнения.

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

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


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

Сравнительная таблица

Критерий​

Архитектура кода​

Архитектура приложения​

Уровень абстракции​

Логический, исходный код​

Технический, исполняемый процесс​

Границы​

Файлы, модули, классы, функции​

Процессы, потоки, память, ресурсы​

Структура​

Документы, конструкции языка​

Объекты ядра ОС: память, процессы, ресурсы​



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


Архитектура приложения - это не просто набор всего исходного кода приложения, а организация исполняемых компонентов системы. Если архитектура кода строится из атомарных сущностей языка программирования, таких как операторы, функции и данные, то архитектура приложения строится из атомарных сущностей среды исполнения кода, объектов ядра операционной системы - процессов.


Процесс является минимальной единицей, внутри которой реализуется функциональность приложения, полностью или частично, и обеспечиваются изоляция, управление ресурсами и безопасность.

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

Примеры:


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


  • В микросервисной архитектуре каждая функция, а чаще набор функций, реализуется в отдельном процессе, что обеспечивает независимость, масштабируемость и отказоустойчивость для приложения в целом.
Процесс - атомарная единица архитектуры приложения


Процесс - это экземпляр исполняемой программы, который получает выделенные ресурсы операционной системы: память, файловые дескрипторы, потоки выполнения. Каждый процесс изолирован от других, что обеспечивает независимость и устойчивость системы. Внутри процесса могут быть реализованы любые логические функции приложения, но сам процесс определяет техническую границу, за которой не может быть прямого доступа к ресурсам другого процесса без специальных механизмов межпроцессного взаимодействия.

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

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

Проектирование процессов


Проектирование процессов - это ключевой этап архитектуры приложения, на котором определяются не только функции, но и технические характеристики каждого исполняемого компонента приложения.

При проектировании процесса учитываются четыре основных аспекта:


  • группа выполняемых функций


  • максимальная мощность процесса


  • способы взаимодействия с процессом


  • количество доступных ресурсов

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

Максимальная мощность процесса
Важно заранее оценить, какую нагрузку будет испытывать процесс: сколько CPU, памяти, дискового пространства и других ресурсов ему потребуется.

Это позволяет:


  • избежать перегрузки системы и сбоев из-за нехватки ресурсов.


  • оптимизировать использование железа, особенно при работе в контейнерах или облаке.


  • планировать масштабирование: если нагрузка растёт, можно добавить больше процессов или перераспределить нагрузку между ними.

Способы взаимодействия с процессом
Процесс не существует изолированно - он должен взаимодействовать с другими процессами, внешними системами и пользователями.

Способы взаимодействия определяются:


  • интерфейсами (API, очереди сообщений, сокеты).


  • протоколами обмена данными.


  • механизмами синхронизации и обработки ошибок.

Количество доступных ресурсов
Основными ограничениями для любого процесса служат ресурсы, предоставляемые операционной системой:


  • CPU - количество операций, вычислительная мощность, доступная для процесса.


  • Память - объём оперативной памяти, необходимый для хранения данных и выполнения вычислений.


  • Диск - пространство для хранения файлов, журналов, временных данных; скорость доступа к данным.


  • Сеть - пропускная способность и доступность сетевых ресурсов для обмена с другими процессами и системами.

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

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


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


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

Группировка функций и их распределение по процессам
При проектировании архитектуры кода разработчики группируют функции по смыслу, ответственности или по принципу связности (например, все операции с пользователями - в один модуль, работа с базой - в другой). Эти группы функций могут быть реализованы в рамках одного процесса (например, в монолитном приложении) или распределены по нескольким процессам (например, в микросервисной архитектуре).


Архитектура кода сильно зависит от архитектуры приложения - разделение функционала на отдельные выполняемые процессы, напрямую влияет на архитектуру кода.

Особенно сильно архитектура приложения влияет на способы взаимодействия между группами функций:


  • внутрипроцессное взаимодействие


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

Таким образом, архитектура кода задаёт логическую структуру решения, а её реализация на уровне процессов определяет, как эта структура будет работать в реальной среде, с учётом ограничений и требований к производительности.

Проектирование кодовой базы


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

Ограничения технологического стека
Выбранный стек технологий - языки программирования, библиотеки, фреймворки, системы хранения данных, кэширования, поиска и другие специализированные инструменты - задаёт рамки, в которых возможно проектирование и реализация кода.

Архитектура кода должна учитывать:


  • совместимость и ограничения используемых технологий.


  • возможности и ограничения библиотек и фреймворков.


  • ограничения на выбор решений для хранения и обработки данных.

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

Ограничения квалификации команды
Архитектура кода также определяется уровнем знаний и навыков членов команды. Утверждение, что «код пишется для человека, а не для компьютера», означает, что важна читаемость, поддерживаемость и понятность кода.

Архитектура кода должна быть:


  • доступна для понимания и модификации текущей командой


  • соответствовать опыту и навыкам разработчиков, чтобы избежать технического долга и ошибок

Если команда не владеет определённым языком или инструментом, использование его в архитектуре кода приведёт к проблемам с поддержкой и развитием системы. Поэтому архитектура кода всегда должна учитывать не только технические, но и человеческие ограничения.
Взаимное влияние архитектуры приложения и архитектуры кода


Архитектура приложения и архитектура кода тесно связаны и оказывают взаимное влияние. Архитектура приложения определяет, как компоненты взаимодействуют в среде выполнения, а архитектура кода - как эти компоненты реализованы и организованы внутри.

Примеры взаимного влияния


Масштабируемость приложения и модульность кода
Если архитектура приложения предусматривает масштабирование (например, микросервисы), то код должен быть организован так, чтобы каждый сервис был автономным модулем с минимальной связностью и высокой когезией. Это влияет на выбор структуры кода: разделение на независимые пакеты, использование API, изоляция бизнес-логики и данных. Если код не модульный, масштабирование приложения становится сложным и дорогостоящим.

Производительность кода и состав приложения
Архитектура приложения может предусматривать отдельные сервисы кэширования, использование очередей сообщений и оптимизацию взаимодействия между процессами для снижения задержек, но если оптимизация кода привела к значительному увеличению производительности приложения, то появляется возможность полностью исключить указанные дополнительные сервисы или снизить количество ресурсов для их работы.
 
Яндекс.Метрика Рейтинг@Mail.ru
Сверху Снизу