Веб-архитектура: MVC, Ленивая инициализация, Объекты передачи данных, Open Session In View, есть ли консенсусный подход?
Какие недостатки вы видите в следующем дизайне (и каков ваш идеальный вариант архитектуры) для типичного веб-трехуровневого приложения?
Мой текущий подход к проектам примерно такой (предполагается, что Java, Spring, Hibernate, JSP)
контроллер
Без сохранения состояния, потенциально обернутые транзакцией только для чтения (чтобы избежать отложенных исключений инициализации), объекты get из постоянного хранилища только через службу передают их представлению в качестве модели. Проводит на них бизнес-логику (должен ли BL находиться только на уровне сервиса?), При необходимости возвращается обратно на уровень сервиса.
Плюсы: для переноса транзакций только для чтения - только одно соединение, без избыточных обращений к одному и тому же постоянному объекту, лучше использует кэш запросов, сервисный уровень не должен "знать" параметры запроса или требуемый интервал графа инициализации, избегайте ленивых исключений инициализации.
Минусы: Подход транзакции только для чтения может быть рискованным, контроллеры не являются идеальным местом для бизнес-логики... очень сложно сделать JUnits (ваш ввод - запрос...)
Посмотреть
Нетранзакционный (доступ к не ленивым коллекциям / членам приведет к исключению ленивого инициализации)
Плюсы:
Автор представления не должен влиять с помощью простой точечной нотации на производительность приложения (например, причина выбора N+1 из-за отложенной инициализации большой коллекции.
Также в отключенных клиентах (Flex или других Rich Clients) отложенная инициализация удаленно либо не поддерживается, либо просто не является разумной вещью
Минусы: контроллер / служба / DAO должны тщательно подготовить правильный граф сущностей для представления, и может быть превышение (производительность) / занижение (исключение отложенной инициализации). бесчисленное множество методов на стороне сервера может вызвать беспорядок, так как существует декартово произведение числа перестановок, которые может инициализировать граф сущностей
модель
При использовании постоянных объектов как есть (без объектов передачи данных) состояние сохраняется в сеансе.
Плюсы: нет необходимости переписывать POJO, повторное использование существующих сущностей, состояние сеанса более безопасно, чем обработка состояния скрытых полей.
Минусы: плохо для отключенных платформ, риск сохранения устаревших отключенных объектов, риск проблем с блокировкой, переопределение данных других, иногда требует оптимистической блокировки.
обслуживание
Транзакционный, не знает объема запроса, вызывает уровень DAO для фактического доступа к постоянному хранилищу. это где BL должен быть классически, но кажется, что BL просачивается на сторону контроллера снова и снова.
DAO
Содержит атомный персистентный фасад хранения, неосведомленный о BL, или любой другой контекст
Напоследок вопрос:
Что бы вы исправили в приведенной выше архитектуре?
Как вы думаете (как и я), это довольно распространенный подход (с некоторыми незначительными различиями, такими как открытая сессия и т. Д.)? Или вы впервые это видите, и я делаю что-то ужасно неправильное (или правильное)?
Как вы решаете это в своих приложениях? Используете ли вы POJO вашей сущности также для вашей модели и вида? или вы подключаете его к более простым компонентам пользовательского интерфейса (все полностью инициализированы и защищены)?
Это может быть субъективным вопросом, но я уверен, что существуют четкие шаблоны проектирования наилучшей практики, которые объединяют одну, две или три максимально общие "религии".
4 ответа
В целом, это похоже на очень хорошую архитектуру. Если вы еще не прочитали его, я бы порекомендовал шаблоны корпоративной архитектуры приложений Мартина Фаулерса, которые описывают каждую тему вашего вопроса.
Из вопроса не ясно, насколько велика проблема, которую вы ожидаете от производительности. По моему опыту, узкие места в производительности редко бывают там, где вы о них думаете, и чем раньше вы их найдете, тем проще будет изменить архитектуру в соответствии с ней.
Вы правы, что тестируемость является серьезной проблемой. Я использовал Мартин Фаулерс Пассивный Просмотр-шаблон с некоторым успехом. Вам также следует взглянуть на Supervising Controller с того же сайта.
Супер, если только не делать интерфейс в стиле SOFEA, который в основном избавляет от части контроллера в вышеупомянутой архитектуре.
Внешний интерфейс полностью содержится на клиенте, который напрямую вызывает службы REST или SOAP, возвращающие JSON или XML. Это, кажется, решает 100% проблемы с преобразованием доменных объектов для отображения!!!!
Возможно, некоторые предполагают, что причина, по которой нет ясного решения для архитектуры N-уровня, описанной выше, заключается в том, что это просто неправильно.
связи
- http://raibledesigns.com/rd/entry/sofea_also_known_as_soui
- http://www.theserverside.com/news/thread.tss?thread_id=47213
- http://wisdomofganesh.blogspot.com/2007/10/life-above-service-tier.html
Мой текущий проект использует несколько устаревшую N-уровневую архитектуру с объектами передачи данных. DTO не нужны, потому что приложение не распространяется и никогда не будет. Единственное преимущество (которое не стоит IMO) использования DTO заключается в том, что он обеспечивает чистый интерфейс для бизнес-методов - компоненты View могут проходить граф объектов на псевдомоделях, как им угодно - никакие исключения ленивой инициализации не могут быть брошенным.
Одна из архитектурных проблем, которую я вижу в нашей архитектуре, заключается в том, что бизнес-интерфейсы чертовски сложны. Кажется, что нам нужен мета-бизнес-интерфейс для инкапсуляции всех маленьких бизнес-интерфейсов. Действительно, это происходит, когда одна служба вызывает три другие службы для выполнения своей работы.
Контроллеры (в нашем случае классы действий Struts 1.2) в конечном итоге вызывают любую комбинацию этих ультра-мелких или грубых бизнес-компонентов. К сожалению, в большинстве случаев разработчики невольно или лениво кодируют различные элементы бизнес-логики в классах контроллеров. Я кричу каждый раз, когда я смотрю на один из этих трехсот линейных методов действия!!!!
Подход SOFEA предлагает более чистый подход. AJAX позволяет даже веб-приложениям, работающим в браузерах, кодировать свои интерфейсы с использованием правильного шаблона MVC, что хорошо как для пользователей (предлагая более динамичный пользовательский интерфейс), так и для разработчиков (позволяя им кодировать надлежащий MVC).
Пользовательский интерфейс полностью отделен от бизнес-логики многократного использования. GUI толстый или тонкий? Это действительно не имеет значения - они будут закодированы в основном таким же образом.
SOFEA / SOUI является новым для меня, и я никогда не пробовал это, но я читал об этом в последнее время, и я думал, что поделюсь своими мыслями.
Ваш вышеупомянутый подход звучит хорошо.
Но я думаю, что вы должны использовать пользовательский интерфейс. Конечно, этот пользовательский интерфейс должен быть эффективно неизменным. Как только он создан, его состояние (и объект инкапсулированного домена) не должны изменяться.
Очень упрощенный пример:
class UIBean {
DomainObject o;
public String getDescription(){
return trimToSummaryText(o.getDescription());
}
private static String trimForSummaryText(){
....
}
}
Основные плюсы:
- Код шаблона имеет тенденцию становиться чище и удобнее. Ваш Frontend-Developer будет рад этому.
- Вы не склонны добавлять специфичные для внешнего интерфейса вспомогательные методы в классы объектов домена.
- Возможна группировка различных доменных объектов или view-bean-компонентов (ui-bean может иметь несколько полей). Инкапсуляция списков здесь особенно хороша.
Да, для этого нужно больше java-классов. Но этот уровень абстракции почти всегда хорош, как только ваше веб-приложение и страницы растут.
Несмотря на последний ответ на вопрос около года назад, возможно, ответ кажется кому-то полезным. В целом обрисованная вами архитектура в порядке, единственная деталь, которую нужно добавить: обычно для передачи данных между слоями (например, View и Service) вместо упомянутых выше UiBean-ов используются так называемые DTO (объекты передачи данных), это простые POJO с соответствующие поля / сеттеры / геттеры.
В одной из предыдущих / ранних версий спецификации Java EE термин "DTO" был ошибочно смешан с "ValueObject", хотя они имеют несколько иные цели.
Надеюсь это поможет.