На какие слои делятся приложения
Приложения обычно делятся на логические части, называемые "слоями", при этом каждому слою назначается своя роль. Локальные приложения могут состоять только из одного слоя, который размещается на компьютере клиента, а web-приложения по своей природе следуют N-слойному подходу. Хотя возможны разные варианты, наиболее распространенными являются приложения, использующие три слоя: слой представления; слой бизнес-логики; слой доступа к данным (хранилище). Каждый слой включает набор компонент (наборов классов), выполняющих специальные функции.
На рис. 2.3 показана типовая архитектура web-приложения с набором наиболее часто применяемых компонент, сгруппированных по функциональным областям.
Слой представления обычно включает компоненты пользовательского интерфейса (UI) и логику представления. Слой бизнес-логики включает компоненты бизнес-логики, бизнес-процесса и бизнес- сущностей. Слой доступа к данным включает компоненты, реализующие доступ к требуемым данным и web-сервисам.
Рис. 2.3. Типовая структура web-приложения
Web-приложение также можно разделить на базовые и функциональные подсистемы. К базовым подсистемам web-приложения относятся:
- • оформление web-страниц, составляющих приложение;
- • поддержка состояния сеанса работы пользователей;
- • настройка web-страниц для разных пользователей (персонализация);
- • навигация между разными web-страницами;
- • обеспечение безопасности (аутентификация и авторизация, регистрация пользователей);
- • доступ к источникам данных.
К функциональным подсистемам относятся:
Базовая функциональность во многом реализуется средой выполнения web-приложения, а для создания функциональных подсистем требуется составление программного кода.
В 2017 году Matthias Noback (автор A year with Symfony) опубликовал цикл из трех статей, в котором описал свои взгляды на идеальную архитектру корпоративных приложений, сформировавшуюся за долгие годы практики.Первая часть является вводной и не представляет особого интереса(можно ознакомитсья в оригинале). Переводом второй является данная статья. Перевод третьей будет доступен в скором времени.
Для меня, одним из обязательных требований, к "чистой" архитектуре, является грамотное разделение кода приложения по слоям. Сам слой не делает ничего, вся соль в том, как он используется и какие ограничения накладываются на компоненты, ему принадлежащие. Давайте немного пофилософствуем, перед тем как рассмотреть конеретные практические приемы.
Зачем нужны слои
- Слои помогают спрятать/защитить, то, что находится под ними. Можно воспринимать слой как фильтрующий барьер: данные, передающиеся через него, должны быть провалидированны перед тем как перейти на следующий. Они должны быть приведены к формату, который позволит другим слоям корректно работать с ними. Слой также определяет, какие данные и функции из более глубокого слоя могут быть использованы во внешних.
- Слои четко разграничивают ответственности, а следовательно расположение классов в вашем коде. Если вы добьетесь строгих договоренностей внтури вашей команды, о том какие слои используются в вашем приложении и за что отвечате каждый из них, то вам всегда будет легко найти нужный класс или определится куда следует добавлять новый, просто зная его предназначение.
- Благодаря использованию слоев, можно свободно менять приоритет и порядок этапов разработки приложения. Вы можете разрабатывать проект последовательно, начиная от ядра бизнесс логики, накладывая слой за слоем на него. А можно инверсировать процесс и начать с разработки слоя взаимодействия с пользователем. Этот пункт довольно важен для нас, так как благодаря ему можно разработать большую часть приложения до принятия решения о используемой ORM, БД, фреймворке, и т.д
- Большое количество старого софта содержит код, неразделенный на слои, который можно назвать "спагети" кодом: вы можете вызывать и использовать всё что хотите, любые методы и структуры в любой части проекта. Используя систему слоев(правильным образом) можно добиться высокого уровня разделения ответсвенности( separation of concerns). Если вы задокументируете эти правила и будете следить за их соблюдением на код ревью, то вы здорово уменьшите скорость скатывания вашего проекта в ранг гавнокода "технического долга"
Вы, конечно же, пишите тесты. Грамотная спроектирвоанная система слоёв, невероятно упрощает тестирование. Различные типы тестов подходят для кода из разных слоев. Назначения каждого теста становится более очевидным. Набор тестов в целом становится более стабильным и более быстроработающим.
Однако, у нас есть паникёр из твиттера:
ООП версия спагетти кода — это код лазанья, с переизбытком слоев.
Лично я никогда не встречал код-лазанью, зато видел очень много лапшекода. Правда бывало, что я писал код, в котором допускал серьезные архитектурные ошибки, и неверно разделял приложение на слои, что приносило некоторые проблемы. В этой статье я описываю, как мне кажется, наилучший набор слоев, большая часть из которых описана в книге Vaughn Vernon "Implementing Domain-Driven Design"(ссылка ниже). Прошу заметить, что слои не имеют жесткой привязки к DDD, хотя они и дают возможность создавать чистые доменные модели, при соответсвующем желании у разработчика.
Структура директорий и неймспейсов
Внутри src/ у меня есть директории для каждого контекста(Bounded Context), например который я выделяю в своем приложении. Каждая из них также служит корневым неймспейсом для принадлежащих ей классов.
Внутри каждого контекста я создаю директории для каждого из слоёв:
Кратко опишу каждый из них.
Слой 1 — Домен(модель/ядро)
Доменный слой содержит классы для известных DDD типов/паттернов:
- Entities
- Value objects
- Domain events
- Repositories
- Domain services
- Factories
- .
Внутри папки Domain я создаю подпапку Model, внутри неё — директории для каждого из агрегата(Aggregate root). Папка с агрегатом содержит все связанные с ним штуки(объекты-значения, доменные события, интерфейсы репозиториев и т.д)
Обратите внимание, что код из доменного слоя никак не соприкасается с реальным миром. И если бы не тесты, то никто не мог бы обращаться к его объектам напрмяую(это делается через верхние слои). Тесты для доменной модели должны быть исключительно модульными. Т.к доменный слой не взаимодетсвует напрямую с файловой системой, сетью, бд и т.д, то мы получаем стабильные, независимые, чистые и быстрые тесты.
Слой 2 — (обёртка для домена): Прикладной слой
Прикладной слой(Application Layer) содержит классы команд и их обработчиков. Команда представляет собой указание на что-то, что должно быть выполненно.Это обычный DTO(Data Transfer Object), содержащий только примитивные значения. Всегда должен быть обработчик команды, который знает, как нужно выполнить конкретную команду. Обычно обработчик команды (также его называют application service) ответственен за все необходимые взаимодействия — использует данные из команды для создания(или извлечения из базы) агрегата, выполняет над ним какие то операции, может сохранить агрегат после этого.
Код этого слоя также можно покрыть юнит тестами, однако на этом этапе можно начинать писать и приёмочные. Вот хорошая статья на эту тему Modelling by Example от Константина Кудряшова.
Слой 3(обертка для прикладного) — Инфраструктура
Код, написанный в предыдущем слое, тоже не вызываается никем кроме тестов. И только после добавления инфраструктурного слоя, приложение становится рельно пригодным к использованию.
Инфраструктурный слой содержит код, необходимый для взаимодействия приложения с реальным миром — пользователями и внешними сервисами. Например, слой может содержать код для:
Код этого слоя надо покрывать интеграционными тестами(в терминологии Freeman and Pryce). Здесь вы тестируете всё по настоящему — настоящая база, настоящий вендорский код, настоящие внешние сервисы. Это возволяет убедиться в работоспособности тех вещей, которые не находятся под вашим контролем но используюся в вашем приложении.
Фреймворки и библиотеки
Правило зависимостей
Правило зависимостей(сформулированное Robert C. Martin в The Clean Architecture) утвержадет, что на каждом слое приложения вы должны зависеть только от кода текущего или более глубокого слоя. Это значит, что код домена зависит только от себя, код слоя приложения от своего кода или домена, а код инфраструктурного слоя может зависеть от всего. Следуя этому правилу, нельзя сделать в доменном слое зависимость на код из инфрастуруктурного.
Но слепо следовать какому-либо правилу, непонимая в чем его истинный смысл — это довольно глупая затея. Так почему же вы должны использовать правило зависимостей? Следуя этому правилу вы гарантируете, что чистый код слоёв прикладного и доменного слоев не будет завязан на "грязный", нестабильный и непредсказуемый код инфраструктуры. Также, применяя правило зависимостей, вы можете заменить что угодно в инфраструктурном слое не прикасаясь и не изменяя код более губоких слоёв, что даёт нам богатые возможности для ротации и переносимости компонентов.
Этот способ уменьшения связанности модулей известен давно, как Dependency Inversion Principle — буква "D" в SOLID сформулированном Робертом Мартиным: "Код должен зависеть от абстракций, не от реализаций". Практическая реализация в большинстве ооп языков заключается в выделинии публичного интерфейса для всех вещей, от которых вы можете зависеть(интерфейс и будет абстракцией) и создании класса, реализующего этот интерфейс. Этот класс будет содержать детали, не имеющие значения для интерфейса, следовательно этот класс и будет реализацией, о которой говориться в inversion principle.
Архитектура: отсрочка технологических решений
Применяя предложенный набор слоёв вместе с правилом зависимостей, можно получить много плюшек при разработке:
- Можно много эксперементировать, прежде чем принимать такие важные решения, как, к примеру «используемая СУБД». Также можно спокойно использовать разные базы данных для разных случаев в рамках работы с одной и той же моделью.
- Можно отложить решение об используемом фреймворке. Это не позволит стать «приложением Symfony» или «Laravel проектом» в самом начале разработки.
- Фреймворки и библиотеки будут размещены на безопасном расстоянии от кода модели. Это здорово поможет при обновлении мажорных версий этих фреймворков и библиотек. Это также позволит минимизирвоать изменения в коде и трудозатраты, если вы когда-нибудь захотите использовать, к примеру, Symfony 3 вместо Zend Framework 1.
Все это выглядит крайне заманчиво: мне нравится возможность беспроблемной замены компонентов приложения + я люблю принимать важные архитектруные решения не перед стартом проекта(основываясь на своем прошлом опыте и догадках), а тогда, когда начинают проясняться реальные кейсы использования разных частей приложения, и я имею возможность выбирать подходящие решения исходя из существующих потребностей.
Заключение
Как упомяналось ранее, этот вариант расслоения приложения, хорошо уживается с любым фреймворком, т.к его место четко определено в инфраструктурном слое.
Некоторое считают, что в моём варианте "слишком много слоев". Я не понимаю, как можно считать 3 слоя, слишком большим количеством, но если вас это смущает то можете убрать прикладной. Вы потеряете возможность писать приемочные тесты(они станут чем то похожи на системные — более медленные и хрупкие) и не сможете тестировать один и тот же функционал вызываемый к примеру из веб-интерфейса и консольной команды без дублирования кода. В любом случае, вы сильно улучшите архитектуру вашего проекта благодаря раделению бизнесс логики и инфраструктурной части.
Осталось более подробно рассмотреть инфраструктурный слой. Так мы плавно перейдем к теме гексагональной архитектуры(порты и адаптеры). Но всё это, в следующей части.
Дальнейшее чтение
-
by Steve Freeman and Nat Pryce by Robert C. Martin by Robert C. Martin , chapter 4: "Architecture" and chapter 9: "Modules", by Vaughn Vernon
Также можно ознакомиться с Deptrac — инструмент, помогающий соблюдать правила использования слоев и зависиомостей.
Что такое архитектура веб-приложений? Из этого руководства вы можете изучить основы архитектуры веб-приложений. Мы обсудим, как работает архитектура веб-приложения, какие компоненты, слои и модели существуют
Архитектура веб-приложения в основном представляет отношения и взаимодействия между такими компонентами, как пользовательские интерфейсы, мониторы обработки транзакций, базы данных и другие. Основная цель - убедиться, что все элементы правильно работают вместе.
Логика довольно проста: когда пользователь вводит URL-адрес в браузере и нажимает «ввод», браузер делает запрос к серверу. Сервер отвечает, а затем показывает требуемую веб-страницу. Все эти компоненты создают архитектуру веб-приложения.
Как работает системная архитектура для веб-приложений?
Все приложения состоят из двух частей - клиентской (front-end) и серверной (back-end).
Поэтому, когда вы вводите свои учетные данные в регистрационную форму, вы имеете дело с внешним интерфейсом, но как только вы нажимаете «ввод» и регистрируетесь - это серверная часть заставляет его работать.
При правильной работе клиентская и серверная стороны составляют архитектуру программного обеспечения веб-приложения.
Слои и компоненты архитектуры веб-приложений
Чтобы лучше понять архитектуру веб-приложения, вам следует погрузиться в его компоненты и уровни. Веб-приложения разделяют свои основные функции на уровни. Это позволяет заменять или обновлять каждый слой независимо.
Базовые компоненты архитектуры веб-приложений
Веб-архитектура имеет компоненты пользовательского интерфейса и структурные компоненты. Последние также делятся на клиентские и серверные.
Компоненты пользовательского интерфейса
Компоненты пользовательского интерфейса обозначают все элементы интерфейса, такие как журналы активности, информационные панели, уведомления, настройки и многое другое. Они являются частью макета интерфейса веб-приложения.
Структурные компоненты состоят из клиентской и серверной сторон:
Клиентский компонент разработан с HTML, CSS или JavaScript. Веб-браузеры запускают код и преобразуют его в интерфейс, поэтому нет необходимости в настройке операционной системы.
Уровни архитектуры веб-приложений
Существует четыре общих уровня веб-приложений:
- Уровень представления (PL)
- Уровень обслуживания данных (DSL)
- Уровень бизнес-логики (BLL)
- Уровень доступа к данным (DAL)
Уровень службы данных
DSL передает данные, обработанные уровнем бизнес-логики, на уровень представления. Этот уровень гарантирует безопасность данных, изолируя бизнес-логику со стороны клиента.
Уровень доступа к данным
DAL предлагает упрощенный доступ к данным, хранящимся в постоянных хранилищах, таких как двоичные файлы и файлы XML. Уровень доступа к данным также управляет операциями CRUD - создание, чтение, обновление, удаление.
Типы архитектуры веб-приложений
Можно выделить несколько типов архитектуры веб-приложений, в зависимости от того, как логика приложения распределяется между клиентской и серверной сторонами. Наиболее распространенные архитектуры веб-приложений:
- Одностраничные веб-приложения
- Многостраничные веб-приложения
- Архитектура микросервисов
- Бессерверная архитектура
- Прогрессивные веб-приложения
Одностраничное приложение или SPA
SPA - это веб-сайт или веб-приложение, которое загружает всю необходимую информацию при входе на страницу. Одностраничные приложения имеют одно существенное преимущество - они обеспечивают потрясающий пользовательский интерфейс, поскольку пользователи не испытывают перезагрузки веб-страниц. Одностраничные веб-приложения часто разрабатываются с использованием фреймворков JavaScript, таких как Angular, React и других.
Известные СПА : Gmail, Facebook, Twitter, Slack.
Многостраничное приложение или MPA
Многостраничные приложения более популярны в Интернете, так как в прошлом все веб-сайты были MPA. В наши дни компании выбирают MPA, если их веб-сайт довольно большой (например, eBay). Такие решения перезагружают веб-страницу для загрузки или отправки информации с / на сервер через браузеры пользователей.
Известные MPA: eBay, Amazon.
Одностраничное приложение или многостраничное приложение ? У многостраничного и одностраничного приложения есть недостатки и преимущества.
Архитектура микросервисов
Чтобы понять архитектуру микросервисов , лучше сравнить ее с монолитной моделью.
Традиционная монолитная архитектура веб-приложения состоит из трех частей - базы данных, клиентской и серверной сторон. Это означает, что внутренняя и внешняя логика, как и другие фоновые задачи, генерируются в одной кодовой базе. Чтобы изменить или обновить компонент приложения, разработчики программного обеспечения должны переписать все приложение.
Что касается микросервисов, этот подход позволяет разработчикам создавать веб-приложение из набора небольших сервисов. Разработчики создают и развертывают каждый компонент отдельно.
Архитектура микросервисов выгодна для больших и сложных проектов, поскольку каждый сервис может быть изменен без ущерба для других блоков. Поэтому, если вам нужно обновить логику оплаты, вам не придется на время останавливать работу сайта.
Известные проекты: Netflix, Uber, Spotify, PayPal.
- Монолитные и микросервисы
- Бессерверная архитектура
Что это означает?
Чтобы сохранить веб-приложение в Интернете, разработчики должны управлять серверной инфраструктурой (виртуальной или физической), операционной системой и другими процессами хостинга, связанными с сервером. Поставщики облачных услуг, такие как Amazon или Microsoft, предлагают виртуальные серверы, которые динамически управляют распределением машинных ресурсов. Другими словами, если ваше приложение испытывает огромный всплеск трафика, к которому ваши серверы не готовы, приложение не будет отключено.
Прогрессивные веб-приложения или PWA
Одна из основных тенденций в разработке веб-приложений последних лет - это прогрессивные веб-приложения. Это веб-решения, которые работают как собственные приложения на мобильных устройствах. PWA предлагают push-уведомления, автономный доступ и возможность установить приложение на домашний экран.
Для создания PWA разработчики используют «языки веб-программирования», такие как HTML, CSS и JavaScript. Если приложению требуется доступ к функциям устройств, разработчики используют дополнительные API - NFC API, API геолокации, Bluetooth API и другие.
Известные PWA: Uber, Starbucks, Pinterest.
Как разработать архитектуру для веб-приложения
Качественная архитектура веб-приложения делает процесс разработки более эффективным и простым. Веб-приложение с продуманной архитектурой легче масштабировать, изменять, тестировать и отлаживать.
Статья знакомит с понятием архитектурного слоя, многослойной архитектуры, правил определения слоёв приложения.
Многослойная архитектура является одной из архитектурных парадигм разработки ПО, при которой функциональные области приложения разделяются на группы. Что это значит?
Всю функциональность приложения, так или иначе, можно разделить на группы, в зависимости от того, какая задача выполняется этой функциональностью, что приложение посредством неё делает (общается с пользователем или осуществляет валидацию данных, или манипулирует данными из базы, или описывает сущности, или описывает логику и т.д.).
Задача слоя определяет его роль и ответственность.
Если стараться правильно разрабатывать архитектуру, то нужно определять каждый слой так, чтобы его можно было легко заменить на аналогичный или использовать повторно в другом приложении. Это достигается посредством так называемого слабого связывания (low cohesion). А слабое связывание – это результат грамотного использования таких принципов объектно-ориентированного программирования, как абстракция и полиморфизм.
Для себя можно представить структуру приложения, построенного по принципу многослойной архитектуры, как пирамиду, каждый следующий уровень который расположен поверх предыдущего.
Особенностью такой пирамиды является то, что называют перевёрнутой пирамидой повторного использования, когда каждый слой, расположенный выше, обладает информацией о том, как обратиться к слою, расположенному непосредственно под ним. И ни в коем случае не наоборот.
И сейчас возникает необходимость уточнить, какие слои считаются нижними, какие верхними и в какой последовательности по отношению друг к другу они расположены.
Мы для примера возьмём классическую 3-слойную модель (UIL – BLL – DAL), но даже в ней могут присутствовать дополнительные прослойки.
Что же касается логики слоя находящегося под ним, UIL о нём ничего не знает. Какие алгоритма выполняются в тех методах, которые он вызывает по нажатию кнопки или по изменению текста в поле ввода, или при выборе пункта из списка, UIL остаётся в счастливом неведении.
Ниже может находиться, например, слой сервисов, которые мы сейчас подробно обсуждать не будем, лучше сделаем это в отдельной статье, посвящённой SOA (сервисно-ориентированной архитектуре). Этот слой может не присутствовать и не быть задействованным в 3-слойной архитектуре.
Ниже находится слой бизнес-логики (BLL – Business Logic Layer: классы, описывающие сущности и основную логику работы приложения (основные алгоритмы работы приложения, вычисления, обработка и т.д.). Например, если мы пишем сетевые “крестики – нолики”, то на этом слое могут быть описаны классы, описывающие игровое поле, выполненный ход, отправку данных по сети и т.д
Ниже находится слой доступа к данным (DAL – Data Access Layer). Это работа с источником данных (базами данных разных поставщиков MSSQL, MySql, Oracle…), или XML файлами, или бинарными данными, или текстовыми файлами, короче говоря, всем тем, в чём можно данные хранить постоянно). Здесь тоже описана логика, но направленная именно на взаимодействие с источником и передачу полученных результатов на уровень выше.
Могут быть ещё и другие слои, но какие и в какой последовательности они расположены в пирамиде архитектуры – зависит от приложения.
Главное, что нужно помнить – это следующий набор правил:
- каждый слой занимается конкретной задачей.
- логика разных слоёв не повторяется и не пересекается
- способ обращения к нижестоящему слою чётко определён
- способ поставки информации вышестоящему слою чётко определён
- слои слабо связаны
- слои расположены вертикально, хотя есть сквозная функциональность, которая может пронизывать пирамиду сверху вниз
- слои могут размещаться физически на одном компьютере ( в пределах одного уровня), а могут быть на разных машинах, например, в распределённых приложениях.
- логика разных слоёв инкапсулирована, соответственно, разным слоям не нужно делать никаких предположений о том, как реализован код других слоёв приложения.
- возможность использования в других сценариях (за счёт слабого связывания и чётко определённой задачи).
Итак, я надеюсь, мне удалось разъяснить понятие слоя приложения, рассказать, каким правилам подчиняются слои в приложении, а также объяснить, что такое многослойная архитектура и чем она хороша.
Читайте также: