Pull to refresh

Comments 53

Что-то не понял как не попасть в ловушку, и в чем ловушка
Ловушка описана в первой статье
Извиняюсь, заголовок не в полной мере отражает содержание статьи.

Попробую немного равзёрнуто ответить здесь в комментарии.

Суть статьи в том, что MVC и MVP парадигмы были придуманы для того, чтобы Представления не зависили от Контроллеров.
Так как MVC парадигма достаточно известна, то многие фреймворки используют эту абривиатуру для продвижения себя.
И создаётся ложное впечатление, что на MVC/MVP фреймворке можно сделать достаточно легко приложения разной степени сложности.
Но на практике всё выходит немного не так, как ожидается. И в каждом MVC/MVP фреймворке проблемы связанности контроллеров решаются по-разному. Это то их и отличает.

Вы можете спросить, а зачем повторно использовать Контроллеры?

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

Поэтому проблема повторного использования контроллеров и существует. Каждый фреймворк решает это по-своему.
> MVC и MVP парадигмы были придуманы для того, чтобы Представления не зависили от Контроллеров.

Точно вы ничего не путаете? :) Складывается ощущение, что все проблемы как раз от неправильного понимания сути шаблонов.
Прочитал обе статьи. Так и не понял, в чем же проблема, и как вы предлагает ещё решить.
Логика «зашита» во вьюшках, нельзя повторно использовать контроллеры (!!!)? Да зачем… Контроллер — всего лишь соединительный элемент. Хотите повторно использовать какую-то сложную логику? Сделайте в контроллере Стратегию, и пусть логику используют хоть все контроллеры, два ещё и разную логику, в зависимости от ситуации.
Что до компонентной архитектуры… Компоненты тоже должны взаимодействовать.
Так и не понял, в чем же проблема


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

Контроллер — всего лишь соединительный элемент.

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

Доменная модель не может состоять из сотен таблиц.

Так что кажется мне, что ваша проблема совсем не в MVC.

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

Это называется «элемент управления», и реализуется соответствующим образом. MVC/MVP тут вообще не при чем.
Предметную область количественно оценить достаточно сложно. Оценить можно модель, например по количеству внутренних связей между элементами. Модель предметной области не из каких таблиц не состоит, это не даталогическая диаграмма базы данных. Если под моделью предметной области понимать базу данных, с оберткой для работы, то можно прийти к очень плохому решению с раздутыми контроллерами, и уйти от концепции активной модели. Но, сдается мне, что речь в статье идет о веб-программировании, а там это похоже распространено.
Да, но в больших проектах контроллеры напару с вюхами приходится повторно использовать. Например, почему бы не использовать навороченную логику грида с сортировкой, фильтрацией и другими плюшками для выбора значений. Своего рода продвинутый комбобокс, только на отдельной странице.

Сделайте отдельный контрол, и ваша проблема решится. Тут нет никакой связи с MVC, да и вообще с паттернами проектирования.

Небольшое замечание по статье. Как мне кажется, суть MVC эти (безусловно красивые) схемы не отражают. Вот первая схема… зачем там пользователь? Он что, делает контроллеру ментальный запрос? Как можно смешивать на одной схеме материальное и не материальное. MVC это составной паттерн проектирования, и в нем не предусмотрено взаимодействие с пользователем (точнее, описание пользователя, как элемента схемы), ну никак. Хотя, я вот тут смотрю на английскую вики, и вижу, что она со мной не согласна.
Предметную область количественно оценить достаточно сложно.


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

Сделайте отдельный контрол


По сути так и получилось. Сделали одну страницу, как контрол и используем её повторно.

Думать приходится в терминах используемых технологий. Например, Spring MVC есть, а Spring MVP не существует.
Для небольших приложений с небольшой предметной областью, видимо, проблемы то и нету. Но для приложений, где доменная модель состоит из сотен таблиц, проблема очень даже есть.

Выделяется слой Domain Services, который замыкает на себя бизнес-логику. При этом домен-сервисы могут переиспользоваться не только в рамках одного проекта, но и в нескольких проектах (при модульной архитектуре). При использовании, например, .Net Framework одни и те же домен-сервисы могут использоваться в проектах Asp.Net Mvc, Silverlight, WPF.
При этом контроллеры служат исключительно для связи с вьюшками

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


Для этого выделяется компонент «MySuperMegaGrid», взаимодействующий с сервером по заранее определенному контракту. Например ExtJs GridPanel — настраиваете для него хранилище и правила получения данных с сервера. Потом переиспользуете в разных представлениях один и тот же компонент.
Согласен, каждый решает проблему по-своему. Есть множество вариантов, шаблонов, подходов.

Но мы пытаемся смотреть смотреть на всё приложение, как на большой визард.

Как бы вы релизовали такую логику приложения, как на этих видео?

www.youtube.com/watch?v=w59cb0gYFRI

www.youtube.com/watch?v=zTjHcLG35Gs
Ну для начала, скорее всего, я бы переименовал organisation в organization, а в качестве клиентского фреймворка выбрал бы ExtJs.

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

Если бы подобный механизм делал я, то я бы создал домен-сервисы для каждой значимой сущности (Role, User, Profile, Account). Данные сервисы должны иметь функционал по получению данных, обработке, валидации и сохранению. UserRole я бы не выделял, так как смапить m2m через отдельную табличку можно без выделения отдельной сущности для этой таблицы и редакторов данной сущности. После отделил бы логику от представления — в контролерах исключительно базовая валидация поступающих в параметрах запроса данных, проброска проверенных данных в домен-сервис для обработки, мапинг результата работы домен сервиса в ответ для клиента (например json).
Во вьюшках описал бы необходимые контролы + выделил бы отдельный слой на клиенте, отвечающий за взаимодействие клиентской вьюхи и серверного контроллера для получения и отправки данных. Редактор UserRole я бы выпилил, но добавил бы грид со списком привязанных ролей в форму редактирования пользователя.
После формирования вьюшек для сущностей, я бы выделил, 2 основных панели представления — условные «таб» и «окно» + логика показа необходимых редакторов сущностей для них. «Таб» отвечает за открытие вьюшек в текущем окне, «Окно» — в новом (используется в основном для выбора существующих элементов).

Как-то так…
Про UserRole согласен. Нужно переделать в виде редактируемой кросс таблицы, чтобы было наглядно и удобно управлять ролями.

Спасибо, за отличный фидбак! Очевидно, что опытных разработчиков/архитекторов, как вы, сложно удивить такой демкой.

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

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

Будем развивать это направление дальше, учитывая конструктивный фидбак от хабражителей :)
Ну для начала, скорее всего, я бы переименовал organisation в organization

А есть причины полагать, что это американский английский? Organisation — нормальный британский вариант.
Когда читаю подобные посты (или разгоревшиеся в комментах споры), то часто понимаю, что я чего-то не понимаю. Какое отношение к бизнес-логике имеет, например, мастер создания пользователя? С точки зрения бизнес-логики это атомарное действие. Или после первого же экрана контроллер создает в модели сущность пользователя (если это допускается бизнес-правилами), а дальше просто редактирует её, или накапливает данные где-то вне оосновной модели, а создает, только когда пользователь нажимает финиш и все необходимые данные собраны. В любом случае я это отношу к области ответственности контроллера и представления, к логике приложения и отображения, но не к основной бизнес-модели. Можно реализовать эту логику воспользовавшись так же паттерном MVC, создав отдельную триаду для сбора данных для создания пользователя, можно как-то по другому (напихав в контролер пользователя кучу методов на каждый шаг, по представлению на каждый, хранение промежуточных жанных где-то в сессии и т. п.). Но это будет другая бизнес-задача, не создание пользователя, а сбор данных для создания пользователя. Принцип единственной ответственности заставляет нас выделить для управления пользователями один (простой) контроллер, а сбор данных поручить другому (пускай большому, но тоже простому), если сбор данных не атомарное по отношению к интерфейсу действие — тогда для него нужен свой «рутовый» контроллер и свои представления, последнее из которых передаст все данные контроллеру управляющему сущностями.

В чем я ошибаюсь?
Какое отношение к бизнес-логике имеет, например, мастер создания пользователя?


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

Если рассмотреть мастер создания пользователя, как многошаговый процесс разнесённый во времени,

У вас точно все в порядке с декомпоновкой бизнес-процессов и сценариев использования?
Да, у нас требования от заказчика. Регистрация новых пользователей должна апрувится специально назначенными людьми.
Значит, у вас две разных user story в рамках одного процесса, и их интерфейсные реализации вообще никак друг с другом могут быть не связаны.
Тогда это действие, по-моему, не будет атомарным. Будет минимум два атомарных действия: «начало процесса» и «конец процесса», «создание пользователя» и «апрув пользователя». На уровне бизнес-логики как бы один процесс «создание апрувленного пользователя» для всех процессов, которым нужен такой пользователь, но разным этапам соответствуют разные состояния модели, и это валидные в рамках бизнес-правил состояния, то есть существование не апрвлуенного пользователя это не ошибка, не исключение, а нормальный рабочий процесс, в отличии, скажем, от ситуации, когда пользователь вроде уже создан, но пароль ему ещё не задан. Ну и оба атомарных на уровне бизнес-логики события вполне могут быть реализованы на уровне UI многошаговыми мастерами, если бизнес-правила не требуют фиксации каждого шага (то есть после каждого шага измененная модель будет валидна), а только нажатия кнопки «финиш».
Если не вешать транзакцию на этот процесс, то он и не будет атомарным.
«Вешать транзакцию» — это технические детали не имеющие прямого отношения к атомарности бизнес-операций. Хотя возможность обернуть в транзакцию бизнес-операцию может служить примерным признаком атомарной она или нет считаться должна. В примере с апрувом вряд ли стоит регистрацию пользователя и апрув регистрации оборачивать в транзакцию (если вообще получится), а значит операции не атомарны. А вот по отдельности можно (если нет других требований), а значит «создание нового пользователя» атомарная операция и зименение модели в её процессе нужно только одно.
Почитал, та же суть, только другими понятиями.
Проблема то одна, но у меня несколько другое решение.
Перечитал ещё раз внимательней вашу статью. Как я понял, вы делали акцент в статье на Контроллер, какую роль он играет в MVC и как лучше его использовать, чтобы разделить Модель и Представление.

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

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

Ваша же статья вполне может служить основой для понимания роли Контроллера в MVC.
Речь скорее о вашей предыдущей статье (в этой вы действительно просто сравниваете роль представления в MVC и MVP). Главное же отличие, что там вы предлагаете решение после слов «Как же вернуть былую слабую связанность модулей системы, сохранив описание бизнес процессов внутри неё?». Думаю оно не достаточно хорошо. В моем же решении все по прежнему взаимодействуют напрямую с бизнес-сущностью, и нет необходимости работать через контроллер, когда визуализация не нужна. А это важно, часто одна и также сущность нужна как с визуализацией, так и без. И когда визуализация не нужна — нет необходимости создавать городить трехзвенную структуру — не нужно вообще создавать объекты контроллер и представление.
Вы правы в случае, когда визуализация не нужна, то нет необходимости выделять и создавать триаду MVC.

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

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

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

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

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

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

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

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

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

Да и многие используют сразу CRUD модули целиком, не разбивая их на элементарные операции.

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

Наверное, всё-таки, пытаясь объяснить проблему и взяв MVC, я слишком издалека зашёл.
В нашей системе модули представляют собой MVC-триады. Каждый из них отвечает за определённое элементарное действие. Например, за создание какой-либо сущности, другое за обновление, удаление или отображение списка сущностей.

Мне кажется, это решение было ошибочно: вы получили слишком мелкую гранулярность модулей, и поэтому их у вас так много.
Ошибочно? Не думаю. Это было сделано преднамерено с тонким и хлоднокровным расчётом. :)

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

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

А всего-то надо было развязать «права» и «модули», и не было бы этой каши.

(нарушение SRP, кстати)
Проблема? О какой проблеме вы говорите?

У нас права и модули развязаны на уровне метаданных. Так что каши никакой нету. И нету нарушения SRP.
Проблема? О какой проблеме вы говорите?

Излишнего количества (сотни) модулей, и, как следствие, повышенной их связанности.
А зачем создавать модули на одно действие для этого? Собственно какая разница указывать в конфиге users_delete (название модуля) или users.delete (название модуля и действия)?
Формы для сущностей системы могут быть большими. Поэтому операции для них выделены в отдельные MVC-триады. Также в системе используется одно центральное окно для отображения представлений. Мы отказались от всякого рода модальных и выскакивающих окон. Навигация по системе осуществляется через это окно. Breadcrumb компонент отображает на каком из представлений какой сущности находится пользователь.

Это позволило обеспечить единый интерфейс для пользователя навигации по системе и её функциям. Вместо альтернативы с кучей модальных окон перекрывающих друг друга. Или немодальных, в которых можно запутаться, как только перейдёшь за первый десяток.
Причем тут вообще модальные окна или не модальные, большие формы или нет? К архитектуре это, по-моему, мало отношения имеет.
Разговор уходит в сторону. Но ответа так и нет, зачем использовать триаду MVC — в случае когда нет визуализации. Например, вы списком удаляете сущности — тогда для каждой сущности вы развернете триаду? В то время как цикле нужно вызвать метод Entity[i].Delete(). Вы же напрямую к Entity обратится не сможете, и будите зачем то это делать через контроллер.
Такие вещи мы обычно выполняем на уровне сервисов.
Что это значит?
Так как каждый клиент получает своё уникальное приложение, то это уменьшает вероятность появления ошибок на порядки


По-моему, голословное утверждение.
Конечно, это не так. Это уменьшает время исправления ошибок в той части, которая разная. Но это же увеличивает время разработки общих частей. Т.к. так или иначе больницы имеют близкую логику — то «уникальное приложение» — это сознательное дублирование.

По хорошему, нужно все равно выделять ядро, и могут быть разные для больниц исходники, где логика действительно разная. Но к MVC эта проблема ВООБЩЕ не относится.
Хочется подвести черту в этой дискуссии.

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

Чтобы немного прояснить ситуацию, как пример, можно привести всем хорошо известный HTML. Это декларативный язык разметки веб страницы. Он задаёт, как компоненты между собой будут связаны для отображения информации браузером.

В нашем же случае Lexaden Web Flow используется для описания структуры/модели всего приложения, а не отдельной страницы. Модель эта интерпретируется специальным контроллером, аналогия с браузером.

Страницы, в нашем случае — это MVC триады, общаются с контроллером посредством событий, поэтому являются слабосвязанными. Они связываются между собой только на уровне описания модели приложения, привязываются к определённым нодам. Используется что-то вроде CSS, только на Java.

Это тесно связано с термином метапрограммирование . :) Когда программы создают сами себя.

Есть книжки по этой теме Фабрики разработки прогам, Порождающее программирование.

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

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

Видимо так и происходит.

Впредь, буду тщательней выбирать аналогии и термины.

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

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

В какой-то мере у нас есть возможность не использовать целиком MVC триаду, а использовать типа ITask интерфейс. Но эту возможность нужно ещё хорошенько обдумать и проверить в боевых условиях в продакшане. :)
Sign up to leave a comment.

Articles