UPD June 2020: Я написал еще одну статью, чтобы пояснить, чем проблемен подход “выделим бизнес-логику” в широком смысле. См. Проблема выделения бизнес-логики.
Я не знаю насколько эта статья актуальна современным реалиям. Вполне возможно, что описываемое мною — просто ерунда и никто так не пишет, просто мне не повезло дважды. Но что-то слишком много туториалов по программированию именно в таком стиле.
Возьмем классическое enterprise-приложение. Строится оно примерно так:
- Делаем репозитории под сущности — это у нас типа DAL;
- Делаем сервисы, в которые выносим бизнес-логику — это у нас типа BAL;
- Пишем контроллеры, которые ничего не делают — это у нас типа Client.
Это называют “слоенная архитектура”. Время идет, проект расширяется. Постепенно вы начнете сталкиваться с проблемами.
Примечание: в литературе обычно описывается другая трехслойная архитектура. Добавлено 19.09.2018
В каноничной трехслойной архитектуреесть такое деление:
Грамотное ООП: организация надёжной бизнес-логики / Дмитрий Елисеев (ElisDN)
- сервер бд (к примеру, ms sql server или mongodb)
- бизнес-логика (сервер приложений)
- бизнес-приложения (приложение под веб, десктоп или телефон)
А теперь представьте, что в сервере приложений вы добавили еще деление на три слоя: логика работы с бд, бизнес-логика и адаптер под все бизнес-приложения.
Вот именно про эти три слоя внутри сервера я буду говорить.
проблема: boilerplate
Первая проблема, с которой вы столкнетесь — у вас слишком много boilerplate-кода.
По сути, где-то 50–95% вашего приложения — это тупой CRUD. В итоге ваши сервисы превращаются в классы с кучей методов вида “дергнуть репозиторий и вернуть результат”.
Если вы допускаете обращение напрямую из контроллеров в репозитории, то рано или поздно вы обнаруживаете, что у вас нету возможности взять и по-бырику поменять какую-то бизнес-логику.
Т.е. или так проблема, или так проблема.
проблема: веселый реюз
Дальше у вас появится вторая проблема. Вам не хватит какого-либо обобщенного репозитория и вы начинаете пилить репозитории, специфичные под сущность (параллельно переделывая все места, где эти репозитории используются). Тут вы либо получаете boilerplate-код в каждом репозитории, либо выносите общие вещи в базовый репозиторий.
После этого вы можете столкнуться с забавной вещью: у вас вроде есть UserTypeRepository с методом Delete, но ваша система не рассчитана работать с динамически изменяемым списком UserType. А в случае если у вас не ORM, а хранимые процедуры — вам еще придется реализовывать метод Delete, чтобы ваш репозиторий мог полноценно работать. Либо вы будете писать throw new NotSupportedException .
Это пример того, что где-то вы пошли не так и нарушили либо SRP, либо ISP. Причем вы пока что еще можете сделать хак вида “не будем вписывать метод в интерфейс репозитория, а что там с классом репозитория можно сделать — не мои проблемы”.
проблема: производительность
Дальше вас настигнет третья божья кара. Вы внезапно осознаете, что Generic-вещи вам не хватает. Вы не можете изолироваться ни от ORM, ни от SQL без падения в производительности.
Организация бизнес-логики: вебинар для разработчиков [Хекслет]
Вам придется добавлять всякие веселые методы “GetUserByEmail”, “SoftDelete”, “MarkMessagesInThreadRead”. И все это — только чтобы достичь какой-либо производительности.
Закончите вы с классом содержащим огромное количество методов и огромное количество строк.
Я видел репозитории в 6к строк — это очень милое зрелище. Правда смотрел на них я недолго: я быстро закрыл файл нахер в испуге, а вскоре и уволился :-).
И не надо про паттерн “Specification”, особенно если его у вас нет.
проблема: куда бы присунуть?
Дальше вас настигнет четвертая божья кара. Придет время и вам придется делать выборку/апдейт по двум или больше объектам.
В какой репозиторий вы это добавите? Что вы выберете:
- userRepo.AssignUserToProject(project)
- projectRepo.AssignUserToProject(user)
- а может оба? Ну там равноправие, все дела.
Некоторые решают создавать под такое дело аж отдельный репозиторий. В итоге у вас растет количество репозиториев и вы не знаете, где что искать.
проблема: неявное становится явным
Следующее вы можете и не увидеть. Ваши сервисы внезапно окажутся циклично-зависимыми. А это очень не любят IoC контейнеры. Да и люди такое тоже не очень любят. И модульная архитектура вообще на такое смотрит как на говно.
У вас, к примеру, может возникнуть необходимость описывать бизнес-правила типа “при повышении кармы юзера до 10.00 нужно выдать ему права на написание заметок” (и наоборот). Это не критично, когда таких ситуаций мало. Ну подумаешь, чуток кода раздулось.
Но проблема в том, что как только у вас таких бизнес-правил накапливается парочка, то вы перестаете видеть где проходит грань между основной логикой и дополнительными бизнес-правилами. А в итоге у вас опять чертовски большие классы, но теперь еще и с большими методами.
проблема: снова производительность
Рано или поздно вы внезапно заметите, что вы возвращаете слишком много данных из репозитория. Т.е. вы запрашиваете слишком много данных. Вы не ведь не можете из репозитория вернуть что-то другое кроме Aggregate Root или из дао примитивную модель?
У вас начинает проседать производительность, а ваши классы раздуваются. Рано или поздно вы перестаете заморачиваться подходом Generic Repository.
И вы начинаете специализироваться, если вас хоть капельку волнует производительность. Что опять приводит вас к раздуванию методов, только вот теперь вообще непонятно что куда относится.
проблема: безконтрольное размножение
Потом вы замечаете, что у вас есть аж несколько моделей, которые себя дублируют. Все ради того, чтобы четко отделить persistence от business layer.
Ну и у вас конечно же есть интерфейс репозитория, и аж одна реализация репозитория; есть интерфейс сервиса, и аж одна реализация сервиса. Вы же типа DI соблюдаете. Только кода что-то становится многовато. Интерфейс на интерфейс, интерфейсом погоняет. Ну и инжектится в ваши сервисы парочка репозиториев (а если вы еще и дао юзаете, то в репозиторий пара дао).
Даже когда не надо. Это не DI, это х@$&я какая-то.
проблема: а зачем вообще?
А может поговорим о концептуальных проблемах?
All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.
— Bertrand Meyer
Итак, у вас есть storage — бд. И у вас есть computation — ваша бизнес-логика. Почему у вас разные названия для этого?
Главная проблема подобной организации кода заключается в том, что с ростом проекта вы получаете месиво. Но поначалу все выглядит чинно и прилично. Поначалу вы даже сможете срезать углы автоматом, чтобы не заметить проблем. Писец приходит внезапно, в общем.
А как по другому?
Рекламируемый мною подход прошел множественные испытания в продакшен-системе, да и вообще был придуман не мной. Надеюсь на меня не обидятся за тыренье концепций.
не плоди лишнего
Если мы хотим добиться какого-либо uniform нам стоит перестать разделять data access и бизнес-логику. У нас и так god-объекты, так хоть абстракции не будут протекать.
SOLID?
Дальше мы начинаем вводить тотальный SRP
Т.е., например у вас есть команды IncreaseKarma и DecreaseKarma. В подсистеме проверки пользовательских прав вы можете дать пользователю право на создание новых постов. Достаточно написать обработчик типа SetCanWriteOnIncreaseKarmaHander (прим.: этот пример на случай, если вы явно храните в бд список “что может” юзер). Вы можете написать обобщенные события BeforeExecuted, AfterExecuted и т.д.
В результате у вас наметаются шаги, чтобы сделать ваше приложение модульным и более понятным.
Это не god
Я обещал вернуться к названию “god”. В действительно, если у нас все вещи вызываются через “god”, то это выглядит как god-object, что есть плохо.
Но это не god-object. Эта штука всего-лишь по типу месседжа должна попросить у IoC-контейнера обработчик сообщения и передать ему управление. Мне просто нравится что-то называть богом.
На самом деле вы можете называть эту штуку messageHandler. Или придти к CQRS и получить queryHandler и commandHandler.
А, ну и регистрацию в IoC обработчиков стоит делать через рефлексию, а не копипастой.
Ссылки на дальнейшее чтение:
- “Проблемный шаблон Repository” (но там нету разделения запроса и обработчика);
- “Рентабельный код” и часть 2 (но по-прежнему нету разделения запроса и обработчика);
- “Command Handlers” — мини-вариант;
- “Commands, Command Handlers and Command Dispatcher”;
- “Meanwhile… on the command side of my architecture”.
Источник: viktorlove.medium.com
Сервер приложений и веб-сервер
Сервер приложений (Application Sever) – это сервер промежуточного программного обеспечения (ПО, middleware). Это системное ПО, которое располагается между операционной системой (ОС) с одной стороны, внешними ресурсами, например, системой управления базами данных СУБД (DBMS, Database Management System) или Интернет-сервисами, с другой стороны, и приложениями пользователя.
Сервер приложений действует как хост для бизнес-логики пользователя, он также обеспечивает доступ к бизнес-приложениям и задаёт их параметры для пользователя. Сервер приложений должен устойчиво работать независимо от изменений трафика клиентских запросов, отказов оборудования и ПО, распределённого характера масштабных приложений, а также возможной разнородности форматов данных и ресурсов их обработки.
Внешние ресурсы, например, СУБД и Интернет-сервисы, предоставляют веб-серверы (Web Server). Они отвечает на запросы пользователя по доставке контента.
Серверы приложений иногда путают с веб-серверами. У них есть общие функции, но есть и много различий. Понимание этих различий поможет правильно сконфигурировать программное обеспечение и инфраструктуру оборудования для нужд предприятия.
Различия между серверами приложений и веб-серверами
Параметр сравнения
Веб-сервер
Сервер приложений
Основная цель
Хостинг сайтов и ответы на простые веб-запросы
Хостинг приложений и обеспечение сложных взаимосвязей бизнес-логики
Тип контента
Доставка только статического контента HTML
Доставка как статического, так и динамического контента
Протоколы
HTTP/HTTPS и другие протоколы
Соединение с приложениями
Подключения к базами данных
К статическим базам данных
К базам данных приложений
Типичные клиенты
Веб- и мобильные приложения, а также веб-браузеры
Многопотоковая обработка
Поддерживается параллельная обработка многих запросов
Потребление ресурсов
Трафик не потребляет много ресурсов
Процессы с интенсивным потреблением ресурсов
Контейнеры
Веб-контейнеры (сервлеты, JSP, JSF, веб-сервисы), контейнеры клиентских приложений (DI, безопасность)
Ёмкость
Результат запроса
Гипертекстовый документ, отображающий информацию в браузере
Файлы, содержащие данные, по требованию клиента
Что такое веб-сервер?
Веб-сервер – это компьютерная система, которая хранит, обрабатывает и доставляет веб-страницы для клиента. Клиентом в этом случае является веб-браузер на компьютере пользователя или мобильное приложение на его смартфоне или планшете. В зависимости от настроек, веб-сервер может хранить один или множество веб-сайтов. Веб-серверы доставляют клиенту только статический HTML-контент, такой как документы, изображения, видео, шрифты и пр.
Обычно веб-серверы не обрабатывают динамический контент и не позволяют программировать свои программы. Веб-серверы работают по протоколу передачи гипертекста HTTP (Hypertext Transfer Protocol) или HTTPS (Hypertext Transfer Protocol Secure). Однако, опционально, некоторые веб-серверы позволяют добавлять компоненты, позволяющие работать с динамическим контентом.
Что такое сервер приложений?
Сервер приложений (Application Server, App-Server) – это программный комплекс, предназначенный для доставки контента и средств его представления для клиентских приложений. Клиентами могут быть веб-приложения, браузеры или мобильные приложения.
Серверы приложений предоставляют для клиентов бизнес-логику, то есть, преобразуют данные в динамический контент и обеспечивают функционал приложений. Примеры такого контента:
- Результаты транзакций;
- Поддержка принятия решений;
- Аналитика в реальном времени, и др.
Сервер приложений – это связующее звено между клиентом и программным кодом физического сервера. Типичные задачи сервера приложений:
- Управление транзакциями;
- Безопасность;
- Внедрение зависимости DI (Dependency injection);
- Одновременность исполнения процессов (Concurrency).
Серверы приложений также обрабатывают такие процессы, как кластеризация, исправление отказов и балансировка нагрузки.
Рис. 2. Сервер приложений.
Что общего у веб-сервера и сервера приложений
Если в качестве основного приложения клиента выступает веб-браузер, то различия между двумя типами серверов размываются. Большинство веб-серверов имеют плагины на основе скриптов (ASP, JSP, JSF, PHP, Perl, и пр.), которые позволяют генерировать динамический контент.
Поскольку в сценариях применения у веб-серверов и серверов приложений много общего, то наиболее популярные серверы являются гибридами этих двух типов. Гибридное решение, совмещающее свойства обеих серверов, обеспечивает максимальную скорость и функциональность системы.
Для хостинга веб-сайта со статическим контентом лучше всего подходят объектные СХД.
Наиболее популярные веб-серверы
Nginx – веб-сервер с открытым кодом, который может работать как обратный прокси-сервер (reverse proxy). Обратный прокси-сервер работает не в сторону клиента, фильтруя контент и обеспечивая безопасность, а в сторону веб-сервера. Nginx имеет архитектуру, управляемую событиями EDA (event-driven architecture), позволяющую создавать и определять события, реагировать на события, измерять потребление ресурсов реакции на событие. Кроме того, он может выполнять функции прокси-сервера электронной почты и балансировщика нагрузки и может выполнять одновременно множество запросов.
HTTP-сервер Apache – популярный веб-сервер на ОС Linux, который входит с стек LAMP (Linux, Apache, MySQL, PHP). На этом веб-сервере работает около 40% Интернет-сайтов. Apache имеет богатый выбор функций, включая htaccess, FTP, HTTP/2, ограничение полосы пропускания для определённых клиентов (throttling), балансировку нагрузки и пр.
Microsoft IIS (Internet Information Services) – свободно распространяемый пакет серверного ПО, представляющий собой проприетарный набор служб от компании Microsoft. IIS распространяется с пакетом Windows NT. IIS поддерживает протоколы HTTP, HTTPS, FTP, POP3, SMTP, NNTP.
Jetty – проект свободного ПО, который может обеспечивать функции НТТР-сервера, НТТР-клиента и контейнера javax.servlet. Хотя Jetty разрабатывался как веб-сервер, он также может служить платформой для межмашинных коммуникаций (М2М).
LiteSpeed имеет хорошую производительность и масштабируемость, широкий диапазон функций и простую в использовании консоль администратора. Это четвёртый по популярности веб-сервер, который, по состоянию на декабрь 2020 года, использовался для 8.1% веб-сайтов.
Наиболее популярные серверы приложений
Apache Tomcat – контейнер сервлетов с открытым исходным кодом на языке Java. Tomcat позволяет запускать веб-приложения и содержит ряд программ для автоматического конфигурирования и часто используется вместе с конфигурационным файлом Apache HTTPD (Apache Hypertext Transfer Protocol Server daemon). Tomcat может исполнять Java-сервлеты, доставлять клиентам страницы в кодах Java Server Page, и может обслуживать приложения Java EE (Java Enterprise Edition).
Сервер Oracle WebLogic – сервер для распределённых приложений с использованием стандартов Java EE. Он полностью интегрирован с продуктами и облачными сервисами Oracle.
Glassfish – сервер приложений с открытым кодом на Java EE, который поддерживает Java-сервлеты, а также спецификацию написания и поддержки серверных компонентов с бизнес-логикой EJB (Enterprise JavaBeans).
JBoss – сервер приложений с открытым кодом для создания, развёртывания и хостинга приложений на языке Java. JBoss может работать на разных платформах и в любой операционной системе с поддержкой Java.
Какой сервер приложений будет наиболее подходящим?
Знание различий между сервером приложений и веб-сервером помогает выбрать сервер для того или иного использования.
- Если нужно обслуживать только веб-страницы со статическим контентом, то лучше использовать веб-сервер;
- Если приложения требуют наличия JSP (JavaServer Pages) и сервлетов, лучше использовать простой сервер приложений, типа Jetty или Apache Tomcat;
- Если приложения содержат много сложных функций, таких как распределённые транзакции и мессенджеры, то лучше использовать полнофункциональные серверы приложений, такие как JBoss или Oracle WebLogic.
Другим подходом может быть добавление функционала в веб-сервер при помощи плагинов. В этом случает, веб-сервер может использовать технологию программирования на стороне сервера (server-side), такую как скрипты CGI, JSP, сервлеты, ASP (Active Server Pages) или JavaScript на стороне сервера.
Использование обоих типов сервера в одной системе
Часто и веб-сервер, и сервер приложений, развёртывают в одной системе. Это даёт возможность предоставлять клиентам как статический, так и динамический контент. В этом случае, веб-сервер становится подсистемой сервера приложений и все их сервисы работают на одной и той же программно-аппаратной платформе.
Преимуществом такого подхода является более высокая производительность системы. В каждом типе сервера максимально используются их преимущества. Простые веб-запросы будут сразу же обрабатываться веб-сервером и при этом не будет снижаться производительность сервера приложений.
Например, на сайте Интернет-магазина должна предоставляться информация о ценах в реальном времени. Обычно на сайте также есть форма для приобретения товара. Когда пользователь посылает запрос, веб-страница магазина ищет актуальную цену и выдаёт результат в виде HTML-страницы. Эту функциональность можно обеспечить как при помощи сервера приложений, так и при помощи веб-сервера с соответствующими плагинами. Возможно несколько сценариев.
Сценарий 1. Использование только веб-сервера с плагинами
Веб-сервер предоставляет функционал Интернет-магазина:
- Сервер получает запрос и передаёт его в соответствующую программу на стороне сервера;
- Эта программа ищет актуальные цены в базе данных или в обычном файле;
- Программа формулирует ответ в форме HTML;
- Веб-сервер посылает запрос обратно в веб-браузер клиента.
Сценарий 2. Использование как веб-сервера, так и сервера приложений
Сервер приложений хранит бизнес-логику для поиска цены. Веб-сервер делегирует ему генерацию ответа, скрипт вызывает сервис поиска в сервере приложений, и затем формулирует ответ HTML.
Размещение логики поиска цены в сервере приложений позволяет использовать её различными частями приложения. В первом сценарии сервис поиска цены не может повторно использоваться, поскольку данные встроены в HTML-страницу.
Рис. 3. Использование как веб-сервера, так и сервера приложений.
Заключение
Пересечение функций веб-сервера и сервера приложений означает, что каждый сценарий применения может иметь несколько решений. Можно применять веб-серверы и серверы приложений отдельно, а можно использовать их комбинацию.
Однако, не каждая конфигурация будет равноценной по параметрам работы и потреблению ресурсов, хотя и будет выполнять возложенные на неё функции. Знание различий между двумя типами серверов поможет сэкономить средства, облегчить масштабирование системы и повысить производительность.
Вам может быть интересно:
Источник: itelon.ru
Логическая модель РБД. Бизнес-логика файл-серверной, клиент-серверной и N-уровневой архитектуры
В данных системах хранение, выборка и поддержание непротиворечивости данных возлагается на сервер БД , а вся бизнес-логика и логика представления исполняются на клиентских машинах. Так как все операции по манипулированию данными осуществляются только через сервер , производительность и сохранность данных зависит только от сервера БД . Серверы БД изначально рассчитаны на многопользовательский режим работы, имеют эффективные алгоритмы кеширования данных. Современные серверы имеют хорошую масштабируемость .
Клиентская часть обменивается данными с сервером посредством SQL запросов. Обработка информации в клиент -серверных системах ведется на уровне множества кортежей .
Процесс разработки разделяется на создание БД и написание клиентской части с бизнес-логикой.
Достоинства
- Высокая производительность , стабильность и надежность при многопользовательской работе.
- Легко организуется защита данных ( шифрование сетевого трафика SSH , SSL )
- Универсальность языка определения и манипулирования данными
- Более высокая цена СУБД . (сервер БД продается отдельно).
- Достаточно высокие требования к квалификации разработчиков
- Навыки администрирования сервера БД
- Повышенные требования к пропускной способности сети
- Повышенные требования к клиентским местам (на них выполняется слой бизнес- логики)
При количестве пользователей от 2 до ~50 она является хорошим вариантом. С ростом числа пользователей начинает сказываться недостаточная пропускная способность сети .
Клиент-сервер с бизнес-логикой на сервере
Используется возможность современных серверов БД исполнять хранимые SQL процедуры на сервере, куда и переносится максимально возможная часть бизнес-логики. Требования к серверу БД возрастают, однако резко понижаются требования к клиентским машинам (за счет выноса с них бизнес-логики) и к пропускной способности сети (клиенту передаются только данные, необходимые пользователю).
Достоинства
- Пониженные, по сравнению с предыдущим классом систем, требования к пропускной способности сети и клиентским местам.
- Более простой процесс создания бизнес-логики.
- Повышенные требования к серверу БД .(каждый сеанс «съедает» память из расчета предельной загрузки)
- Невысокая переносимость (мобильность) системы на другие серверы БД .
По сравнению с предыдущими классами, позволяет держать большую нагрузку.
N-уровневая архитектура
Основными элементами являются сервера БД , сервер ( кластер ) приложений и клиентская часть. Главная идея n-уровневой архитектуры заключается в максимальном упрощении клиента (тонкий клиент ) , выносе всей бизнес-логики с клиента и сервера БД .
Тонкий клиент представляет собой некоторый терминал типа HTML — browser или эмуляторы X- терминала
Вся бизнес- логика оформляется в виде набора приложений, запускаемых на сервере приложений под управлением ОС типа UNIX .
Сервера БД занимаются только проблемами хранения, добавления, модификации и поддержания непротиворечивости данных.
Сервер приложений соединен с сервером БД при помощи отдельного высокоскоростного сегмента сети .
Достоинства
- Повышенная защищенность.
- Высокая производительность .
- Легкость развития и модификации.
- Легкость администрирования .
- Возможность создания системы с массовым параллелизмом (серверов БД может быть несколько, а сервером приложений могут служить несколько соединенных в кластер компьютеров ).
- Высокая сложность.
- Высокая цена решения.
- В некоторых случаях уступает по производительности клиент -серверным системам с бизнес-логикой на сервере.
Единственная альтернатива для создания ИС для очень большого количества пользователей.
Практические занятия
Постановка задачи. Проектирование данных на концептуальном и логическом уровнях. Нормализация отношений.
Подготовка SQL скриптов генерации схемы отношений БД в ERwin. Разработка скрипта для ввода тестовой информации.
Архитектура MS SQL Server 2005. Настройка и использование основных компонент среды. Создание учебной базы данных.
Работа с СУБД MS SQL Server 2005, ORACLE 10g. Примеры соединений с БД, технологии разработки клиенского приложения
Использование технологии Java Database Connectivity (JDBC) для работы с базами данных
Примеры к презентации
Листинг P.1. Выполнение запроса: QuerySample.java
Листинг P.2. Выполнение хранимой процедуры: StoredProcedureSample.java
Источник: intuit.ru