Построение Enterprise-приложения

Когда вы первый раз встречаете понятие «Enterprise-приложение» или «Приложение масштаба предприятия», то обычно это мало что вам говорит, как программисту. Предполагаю, что причина проблемы в том, что вводится оно в обиход не программистами, а маркетологами или продавцами, которые чаще всего общаются не с разработчиками, а руководителями предприятий. Вряд ли они ответят вам на вопрос «Почему JSP или Servlet относится к Java Enterprise Edition, а не к Java Standard Edition?». Как вы уже заметили, ничего страшного в Web-технологиях нет. Вряд ли они выглядят намного сложнее, чем тот же Swing. Но вот разделили. Появились всевозможные framework, которые действительно помогают решать задачи уровня Enterprise. Так что же такого особенного есть в Enterprise-приложениях ? На этот вопрос я и постараюсь дать ответ в данной главе. Возможно, кто-то не согласится с моим мнением, но для меня важным стало понимание, что каждая технология предназначена для решения определенного типа задач. Коли она решает свою задачу хорошо, то она нужна. Ежели нет — «в печь ее», как говаривал профессор Преображенский.
Если проанализировать правила построения Enterprise-приложения, то можно достаточно четко понять, какие библиотеки и технологии могут быть востребованы. И их можно даже как-то предвидеть – что может потребоваться дальше. Чтобы долго не растекаться мыслью по древу, давайте рассмотрим схему построения Enterpise-приложения (в дальнейшем я буду говорить просто «приложение»). Не буду говорить, что она неоспорима, но по моему опыту под нее подходят практически все проекты, в которых я принимал участие. Да в общем-то и не сильно она отличается от рекомендаций профессионалов. Если рассматривать спецификацию Java EE, то идеи, заложенные в ней будут на 90% пересекаться с моим рисунком.

Почему именно так, а не иначе ?

Участвуя не один год (да наверно уже не одно десятилетие) в создании систем масштаба предприятия я пришел к общему описанию, как должно работать Enterprise-приложение. Оно очень простое: пользователь посылает команду с параметрами, с данными что-то происходит и в итоге пользователь получает ответ в виде набора каких-то параметров, которые отображаются на экране каким-то образом. Я думаю, что именно это понимание привело к появлению СОА — Сервис-Ориентированной Архитектуры (SOA – Service Oriented Architecture). Просто это более новый уровень того же понимания проблемы: пользователь посылает запрос – система что-то делает с этими данными и возвращает какой-то результат. То, что привнесло СОА в технологиях построения системы — это понимание того, что сервисы для многих подразделений могут быть одинаковыми, а значит, нет смысла их множить. И надо иметь механизм, который позволит легко подключать уже готовые сервисы. Причем каждый такой сервис может быть реализован по тому же принципу, что и все приложение в целом – а именно разделено на такие же уровни. Все эти уровни данной архитектуры просто облегчают разработку и сопровождение приложения. Вы более четко понимаете, какие шаги необходимо предпринять, чтобы пользователь смог получить то, что ему надо. И самое главное – здесь легко учитывать специфику Enterprise. А для таких приложений характерно следующее:

  1. Эти приложения большие. Они работают с огромным количеством сущностей. Это счета, документы, пользователи, товары, адреса, этажи, клиенты, какие-либо группы, списки типов, телефоны и прочая, и прочая, и прочая. Не очень большие системы содержат сотни и сотни таблиц. А то и тысячи.
  2. Эти приложения никогда нельзя заморозить на уровне какой-либо версии – они «живые». Бизнес меняется постоянно и приложение должно ему соответствовать. А значит оно меняется вместе с бизнесом. Вряд ли вы видели компанию, которая не меняла свои бизнес-процессы годами – такие компании чаще всего исчезают достаточно быстро. Может и есть исключения, но разве что в государственных структурах. Для бизнеса такое положение вещей вряд ли возможно.
  3. Из-за наличия большого количества сущностей и постоянного изменения эти приложения крайне запутанные. И их поддержка может превратится в головную боль не одной команды программистов.

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

 

Схема построения Enterprise-приложения

Единственное замечание: данная схема предназначена для построения систем без использования главной составляющей СОА – ESB (Enterprise Service Bus). Если же вы используете СОА, то схема будет выглядеть не совсем так, но это уже «совсем другая история». Хотя какая-то часть данной схемы прекрасно может быть использована и в случае с СОА.

Уровень хранения данных — Persistence Layer и DataSource

Связка DataSource и Persistance Layer предназначена для работы с данными. Причем данные, как видно из рисунка, могут храниться не только в базе данных. Это может быть XML-файл или просто текстовый файл. Это может быть почтовый сервер. Или Excel-файл. В общем это может быть все что угодно – лишь бы это было более-менее постоянным хранилищем.
Для того, чтобы можно было абстрагироваться от конкретного хранилища данных применяется такое понятие, как DataSource – источник данных, за которым скрывается обычно что-то более-менее осязаемое – конкретная база данных, файл или еще что-то. Понятие DataSource выступает в роли некоего моста между Persistance Layer и реальным хранилищем. На самом деле есть даже специальный класс – javax.sql.DataSource. Он исползуется для содания коннекта к базе данных – почитайте о нем, есть смысл. Но кроме этой «абстракции» существует еще очень важный шаблон проектирования – Data Access Object (DAO). Его появление объясняется несколькими причинами:

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

Каким же образом DAO помогает решить эти проблемы ? Основная идея – определить для Business Layer интерфейс, который он может использовать для доступа к данным. А уже непосредственная реализация этого интерфейса будет зависеть от хранилища.

 

Рисунок показывает, что Business Layer никак не зависит от реализации хранилища. Ему совершенно безразлично, будет ли это база данных или файл. Или просто другая база данных. У него есть интерфес, который он использует. Если вдруг потребуется по какой-либо причине использовать тот же MySQL вместо Oracle – вам надо только реализовать интерфейс StudentDAO для MySQL и все. Что несомненно повышает гибкость и переносимость системы.
По большому секрету могу сказать – принципиальная замена хранилища данных происходит крайне редко. Например замена Oracle на MS SQL – такое я встречал. Про смену базы данных на другой источник (например LDAP) – я такое не видел. Но тем не менее об этом шаблоне надо думать – в конце концов при каком-нибудь слиянии или наоборот, новое руководство может в приказном порядке заставить всех сменить хранилища данных. Например, раньше данные приходили из 1C, а сегодня руководство купило систему «Парус» или вообще SAP. И если вы будете к этому готовы, то честь и хвала вам как профессионалу своего дела.
Не могу не упомянуть, что в моей практике нередко случались ситуации, когда такой вариант проектирования не требовался – в этом случае я думаю, что можно позволить себе не использовать интерфейс StudentDAO, а проектировать сразу класс StudentDAO. Чтобы не плодить лишние файлы. Но тут уже выбирать вам.
Что еще хотелось бы отметить – не думайте, что для всего приложения надо использовать только один DAO. Если у вас несколько сотен таблиц и для каждой нужно как минимум 3-4 метода – то количество DAO может (да и должно) быть больше одного. Разделение может быть разным – это и решение сделать для каждой сущности свой DAO, и разделение по некоторому функциональному признаку (например все, что связано со справочниками, все, что связано со счетами и т.д.) или еще как-то. Здесь сложно дать какие-либо советы. Фантазируйте, анализируйте, пробуйте.

ORM – Object Relation Mapping. Предупреждаю сразу — я не буду рассматривать случаи использования каких-либо иных хранилищ, кроме реляционных баз данных. Но даже в этом случае данные надо получить из хранилища и представить в виде, который удобно обрабатывать. Уже достаточно давно существует термин ORM – Object Relation Mapping. Я бы перевел это как «объектное отображение реляционных таблиц». Хоть в оригинале слово «таблицы» и не встречаются, но так будет точнее отражен смысл. Он обозначает технологии, которые на уровне приложений позволяют рассматривать записи в таблицах, как объекты. Каждая строка в таблице – это объект. Просто свойства этого класса отображаются на колонки в таблице. Такие классы называются Entity – Сущность. Кстати, мы почти вплотную подошли к этому – мы вводили класса «Группа» и «Студент», которые, по сути, выполняли очень простую функцию – хранили данные из таблицы. Данные системы берут на себя достаточно нудные, но в тоже время очень важные функции – реализуют функции CRUD (Create, Read, Update, Delete).
Вам не приходится писать однотипный код при вставке новой записи или при получении данных из таблицы – наверно вы помните, как мы писали SQL-запросы с параметрами, писали код по заполнению параметров в SQL-запросе, писали код для запоминания полученных данных из ResultSetв коллекции объектов определенного типа. Совершенно естественно, что это неудобство было замечено уже давно и были сделаны попытки как-то упростить. Так появились некоторые framework, о которых вы уже слышали. Вот наиболее известные:

  • Hibernate
  • TopLink
  • JPA – Java Persistence API
  • JDO – Java Data Object
  • iBatis

Скорее всего, существуют и будут созданы еще какие-то, но я не осведомлен. Что самое важное и что является главной целью этого уровня – дать простой и понятный механизм, который позволяет из базы данных получить эти самые данные и представить их в виде объектов. Обратная цель: сохранить состояние объектов в базе данных в виде записей в таблицах (если мы говорим о реляционных базах данных). Все остальное — бантики. Например, до появления аннотаций для Hibernate приходилось писать специальные описатели в виде XML. C появлением аннотаций от этих файлов теперь можно отказаться, что сделало описание еще удобнее.
Смысл Persistence Layer в целом один – с одной стороны этого уровня находится какое-либо хранилище данных (чаще всего реляционная база данных), а с другой стороны разработчик может работать с классами-Entity. Смею вас уверить — работать с объектами гораздо удобнее, чем с теми же массивами данных или ResultSet. А уж если вам надо использовать несколкьо видов хранилищь – то без Persistnece Layer обойтись крайне сложно. Да и не надо – раз уже столько умных голов его советуют (автор ни в коем случае не не претендует не это звание).

 

Уровень бизнес-логики — Business Layer

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

  • EJB — Enterprise Java Beans. По сути, каждому классу (методу), который выполняет какие-либо действия можно присвоить транзакцию, которой управляет Application Server (не забудьте, что Application Server должен поддерживать работу с EJB). Она начинается при заходе в метод и заканчивается при выходе из метода. Последняя версия EJB 3.0 (если опять же сервер такое поддерживает) достаточно неплохо справляется с нужными задачами. получается.
  • Самому честно начинать транзакции и ловить результат SQL-запросов. Этим конечно лучше не пользоваться – из-за деревьев леса не увидите. Некоторым облегчением работы может стать использование аспектов. Основная идея аспектов – это нечто вроде вставки кода в разные места программы. Например, можно определить какой-то набор методов и для них определить действия в их начале и конце. И тогда при вызове метода будет сначала выполняться код аспекта и только после этого сам метод. Более подробно можно посмотреть — Аспектно-ориентированное программирование
  • Использовать framework Spring. Содержит возможность работать с транзакциями разных видов – обычные JDBC-транзакции, транзакции для Hibernate, TopLink и даже использовать транзакции для Application Server. Вещь действительно удобная, и что очень приятно, не требует Application Server – можно работать с обычным web-сервером Tomcat. Что немаловажно отметить, для реализации этого Spring использует AOP, которым обладает

Проблема инициализации может быть разрешена с помощью концепции IoC (Inversion of Control). Чаще всего основным принципом называют «голливудский принцип» — «не звоните мне, я сам вам позвоню». Иными словами – возложить проблемы инициализации связей между классами на framework, а не писать код инициализации везде, где он потребуется. Для решения этой задачи существует немало framework, но я на сегодня выделил бы два:

  1. Spring
  2. Google Guice

 

Кроме этого чтобы еще хотелось отметить – обратите внимание, что между Persistence Layer и Business Layer мы передаем Entity, а на другие уровни (UI уровни) BusinessLayer отдает/получает View.
Во-первых – это удобно. Надо отдавать UI то, что он будет показывать. А показывать он будет не всегда точную структуру отношений между Entity. Значит задуматься над этим придеться – следовательно, проще сразу предусмотреть такую возможность. Может быть, что некоторые View будут по своей структуре даже совпадать с сущностями. Но чаще всего так не бывает.
Во-вторых – какое-то время назад мои знакомые быстренько создали Web-Sevices по структуре класса, который отдавал наружу Entity. NetBeans честно сгенерил полное описание классов. И тут они наткнулись на неприятное открытие. Дело в том, что сами Entity не всегда заполняются сразу полностью. Есть такое понятие как lazy-инициализация (ленивая инициализация). Такая техника часто используется, когда вам не нужны все поля от Entity сразу. И только когда эти данные потребуются – будет запрос к базе данных. Но запрос может быть только когда у вас открыта транзакция. В то время, как транзакция вынесена на бизнес-уровень, преобразование данных для передачи Web-Services производится позже – и возникает проблема. С одной стороны Entity хочет «доинициализироваться» окончательно, а транзакция уже закрыта. Возникает ошибка исполнения. Неудобно.
Есть еще один момент, который добавляет веса для использования View — стандартные способы передачи параметров по HTTP (см. чуть ниже раздел «Web-client»). Для преобразования классов из Java в какое-то текстовое представление часто используется механизм reflection (возможность получения информации о классе — его полях, методах и пр). Так вот при преобразовании пакеты, получая класс, пытаются разобрать все его поля. Если какое-то поле ссылается на другой объект, пакет пытается получить доступ ко всем полям этого объекта. И так до бесконечности. Вы наверно уже догадались, чем это может грозить, если два объекта ссылаются друг на друга. Или в общем случае есть какая-то замкнутая цепочка объектов. Такое преобразование просто войдет в бесконечный цикл. Можно конечно писать каждый раз свой преобразователь. Но не проще ли написать класс для View.
Исходя из этих соображений и получается – удобно сделать отдельные классы для выдачи на уровень UI

Уровень обработки HTTP-запросов – Web Layer

Почему-то на этот уровень часто возлагают управление бизнес-логикой – особенно это касается сервлетов. На мой взгляд, это не совсем правильно.
Во-первых — смешивать преобразование данных c логикой – это все методы собрать в одну кучу и сделать помойку. А вы будете вынуждены это делать – ведь приходят вам отнюдь не Java-объекты, а обычный HTTP. Да, конечно, есть способы это автоматизировать, но тем не менее будет это выглядеть ужасно.
Во-вторых – кто вам сказал, что будут ТОЛЬКО Web-клиенты? А если это будут Web-Sevices для демонов или обычные «толстые» GUI-клиенты, которые могут общаться с приложением по RMI или через те же самые Web-Services. Будете вызывать логику из сервлетов? В принципе можно, но это явно увеличит «помоечность» вашего кода.
Поэтому и родилось решение возложить на уровень Web простую задачу преобразования данных между HTTP и Java. Единственное, что еще можно возложить на Web-уровень – это логику смены экранов. Тот самый шаблон проектирования MVC (Model-View-Controller). Но это имеет не очень сильную связь именно с бизнес-логикой. Что касается технологий и framework, которые можно использовать на этом уровне, то их немало:

  • JSP+JSTL
  • JSF
  • Struts
  • Spring
  • и многое другое – перечислять сложно

 

WS client (Web-Service client)

В общем-то тут особо много говорить не о чем – будут ваши сервися использовать какие-то удаленные программы – ну и славно. Достоинства и недостатки этой технологии можно поискать в Интернете. Когда мы будем описывать пример создания такого клиента — мы вкратце коснемся этих вопросов.

GUI-client

Здесь все достаточно очевидно, разве что протокол обмена между Application Server и клиентом может вызывать вопросы, но обычно это RMI. Что касается реализации непосредственно GUI, то здесь выбор за вами. На мой взгляд, Swing позволяет очень много. Кроме этого существует SWT и море всевозможных компонентов. Ищите на просторах Интернета.
В общем-то никто не мешает вам реализовать обычное клиент-серверное приложение с толстым клиентом и базой данных. Но этот вопрос мы очень коротко рассматривали в первых частях. Так что смотрите там и в Интернете.

Web-client

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

  • ExtJS
  • jQuery
  • Dojo
  • GWT
  • JSF – RichFaces, IceFaces, MyFaces
  • и опять же много чего еще

На что бы я еще обратил внимание в данном разделе – так это на систему передачи данных от клиента до сервера и обратно. Вы ведь будете использовать HTTP, а совсем не Java-объекты. Тут на сегодня выбор пока не очень велик:

  1. Можно честно передавать параметры обычным HTTP и честно разбирать параметры.
  2. Использовать некий стандартный вариант записи. На сегодня я знаю два – XML и JSON. Второй на сегодня имеет некоторые преимущества – так что советую посмотреть.
  3. Возложить эту задачу на готовые framework. Основная идея таких framework – сделать вызов Java-метода «прозрачным» для JavaScript. Я знаком со следующими библиотеками, но думаю, что есть еще.
    1. DWR (Direct Web Remoting)
    2. GWT (Google Web Toolkit)
    3. JSON-RPC (Посмотрите в разделе «more implementations» — там есть для Java)

 

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

6 comments to Схема построения Enterprise-приложения

  • DiMoon  says:

    Спасибо. Статья расставила для меня все на свои места)

  • machet1k  says:

    Очень хорошая статья, доступно написано. Большое спасибо.
    Хочу сослаться на Вашу статью при написании ДР. Скажите, пожалуйста, как правильно указать источник в списке литературы?

    • admin  says:

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

  • machet1k  says:

    Вполне доступно написано, но после прочтения всё-таки возникли некоторые вопросы.
    В разделе ORM говорится о системах, реализующие функции CRUD. О каких именно системах идёт речь? И в схеме построения Enterprise-приложения WS-client и Web-client — это разные вещи? Объясните, пожалуйста.

    • admin  says:

      1. Подавляющее большинство систем, которые хранят данные реализуют CRUD (возможно не полностью все операции, но все равно данные надо получать и иногда сохранять). И если мне надо в своем приложениии к ним обращаться, то я могу их рассматривать как реализующих CRUD.
      2. Web-client — это по сути чаще всего браузер, со всеми вытекающими — UI, интерактивность, графика и прочая. WS-client — это клиент Web-Service, по сути программа для вызова удаленных процедур. Он в общем-то может вызываться из браузера (особенно часто это ReST), но все-таки Web-client и WS-client разные вещи.

  • machet1k  says:

    Спасибо!

Leave a reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.