Собственно, наличие только этих двух решений в .NET удобно тем, что ярко демонстрирует разницу между компонентным подходом (WebForms) и MVC. На других платформах тот же расклад, но на PHP, например, такое множество фреймворков, что не всегда можно понять, к какому типу данный фреймворк относится.
Сравнительный анализ вышеуказанных двух .NET решений легко нагуглить, поэтому останавливаться нет смысла.
Пожалуй, главная проблема WebForms — сложность управления HTML-кодом и как следствие проблемы для фронт-энд разработчиков. Сей недостаток “исправили” с помощью .NET MVC, переложив на программиста полное управление, со всеми вытекающими последствиями.
Предполагаю возмущение евангелистов MVC, но тем не менее выскажу свое убеждение: MVC в вебе — как на корове седло. Разумеется, речь не об общей идее разделения кода, представления и данных. Речь о типовой имплементации паттерна в вэбе — контроллеры, экшены и собственно вся архитектура.
Вообще MVC паттерн был придуман для десктопа, и там он не заменял компоненты, а применялся для построения архитектуры приложения.
Одна из существенных проблем MVC, как и других некомпонентных решений, — отсутствие обеспечения персистентности (сохранения состояния элементов) страницы между вызовами с браузера. В старом добром WebForms мы ставили на форму, например, чекбокс и не волновались как он будет отрисован и как будет восстановлено его состояние после перезагрузки. То есть программист занимался тем, чем ему положено заниматься — реализацией бизнес-логики, взаимодействуя с компонентами через их бек-энды, и обрабатывая события элементов страницы после действий пользователя. По сути то же, как в десктопе.
MVC фреймворки не обеспечивают это автоматически, посему надо состояние страницы восстанавливать вручную, и на сложных страницах (например, каталог с корзиной, фильтрами отбора товаров и т.д.) эта задача не является тривиальной.
Самый простой пример для демонстрации казалось бы надуманной проблемы с персистентностью. Некая страница с табличными данными. Вверху страницы форма с фильтром, например, диапазон дат и кнопка «Поиск». Нажимаем кнопку. Вызывается экшен с параметрами, грузится модель, вывели на страницу. Отлично. Далее — кликаем, например, на ссылке пагинатора или заголовку столбца для сортировки. Вызывается соответствующий экшен с параметрами… ой, а где ж наш диапазон дат? Вернемся назад, запишем его в сессии (повесим все элементы на ссылку пагинатора и т.д.). И это только простой, хотя и повседневный, пример. Отдельная проблема сложных страниц представления: одно огромное с огромным контроллером и моделью, или разбиение на модули, виджеты, и как тогда все это комбинировать?
Есть разные способы решить проблемы. Например:
— большие модели, включающие всю страницу, что есть абсурд, потому как модель — это, прежде всего, модель бизнес-данных, а не свалка с данными страницы;
— всякого рода иерархические MVC типа маленькие MVC в одном большом. Даже комментировать этот кошмар не стоит;
— ViewState как в WebForms — это еще куда ни шло, но там есть свои, не раз описанные, проблемы;
— асинхронные обращения к серверу без перезагрузки страниц вплоть до так называемых SPA страниц.
Последний вариант — гораздо более трудоемок в разработке, но, тем не менее, стал весьма популярным, хотя для этого нет ни малейших оснований. Правда жизни в том, что пользователю абсолютно все равно как там что загружается. Более того: пользователя, как правило, раздражает, что страница типа загрузилась, но что-то там еще не загружено и непонятно когда и как уже будет загружено. Плюс тормоза браузера, особенно при использовании клиентских javascript фреймворков, и особенно на мобильных устройствах. Конечно, подобные проблемы в основном мучают разработчиков во всяких Кремниевых Долинах. Для наших пользователь или заказчик — валенок, который сам не понимает что хочет, и наша задача не делать, как он требует, а объяснить ему, что на самом деле ему надо совсем другое (как правило, то, что мы уже умеем написать). А сомневается — пусть погуглит что в этом сезоне ангуляры и всякие реакты — последний писк, тренд и вообще.
Но тут напрашивается вопрос: для кого умные люди придумали облачные вычисления, если некоторые запихивают логику на клиента (аргумент в виде экономии десятка килобайт при нынешних скоростях инета и копеечном железе неубедителен)?
На самом деле эти мучения (а точнее извращения) делаются только по одной причине — обойти проблему персистентности элементов страницы. То есть придумали костыль в виде MVC, затем вместо того, чтобы вылечить конечности и отказаться от костыля, просто-напросто добавили ему еще одну пару — клиентские javascript фреймворки.
Разумеется, есть сайты типа соцсетей или гугледока где, как основной функционал, нужен асинхронный обмен данными, сообщениями и всякими уведомлениями, но процент таких сайтов среди всего множества оных примерно как процент цукербергов среди всех программистов.
Что, на мой скромный взгляд, следовало бы сделать: взять компонентное решение и разрулить проблему управления HTML кодом, не усложняя работу программисту. Попытки подобных решений есть на других платформах. Например PHP фреймворк Prado, но там разработчики наступили на другие грабли — начали создавать всякие кастомные тэги для фронт-энда, всякого рода и иже с ними. Впрочем, Razor с его “птичьим” языком тоже не приводит верстальщиков в восторг.
Пример нужного нам решения — Java фреймворк Wicket, на основе которого был сделан PHP фреймворк Zippy. По сути, были портированы основные архитектурные решения. В частности, в качестве фронт-энда используется чистый HTML и, само собой, событийно-ориентированная компонентная архитектура.
Останавливаться на Zippy не будем — у проекта есть домашняя страница и репозитарий на GitHub. Поэтому возвращаемся к тому с чего начали — альтернативе существующих решений для .NET — фреймворку ZippyNet, как результату портирования из PHP (Wicket) на .NET.
Фреймворк не требует ни WebForms, ни .NET MVC библиотек. Исходники на GitHub. Дополнительно в проект входит простой сайт-заготовка для демонстрации конфигурации и работы фреймворка и страничка с некоторыми элементами, как пример использования.
Вкратце принцип работы.
Фронт-энд:
<a zippy="link1">click me</a> <span zippy="label1" ></span>
Бек-энд:
namespace TestApp.Pages
{
public class MainPage : ZippyNet.Html.WebPage
{
public MainPage ()
{
Label label1 = new Label("label1", "Hello");
ClickLink link1 = new ClickLink("link1");
//назначаем серверный обработчик
link1.setClickHandler((HtmlComponent sender) =>
{
label1.setText("I am clicked");
});
// добавляем элементы к классу страницы.
this.add(link1);
this.add(label1);
}
}
}
Как это работает.
Бек-энд и фронт-энд связываются с помощью идентификаторов label1, link1 в специальном атрибуте zippy. Используется пассивная шаблонизация. При рендеринге страницы экземпляр класса соответствующего компонента находит свой тэг в DOM модели (используется библиотека CsQuery) и изменяет его значение или атрибуты в соответствии со своими данными. При этом, для элементов-источников событий формируются специального вида адреса HTTP запросов, по которым сервер понимает какой из элементов был кликнут или переключен, то есть, является источником события (при первом обращении к странице, когда создается экземпляр ее класса, URI содержит имя класса страницы). И при этом не нужен никакой ViewState — экземпляр класса страницы с дочерними компонентами и, соответственно, их данными, хранится в серверной сессии.
И кстати, по поводу AJAX. Несмотря на то, что необходимость в асинхронных вызовах при использовании данного фреймворка отпадает, тем не менее ряд компонентов поддерживают асинхронную работу для тех случаев, когда это необходимо. Причем асинхронная работа обеспечивается тем же компонентным подходом. Разработчику не нужно ничего программировать и тем более прилеплять всякие, прости Господи, ангуляры и реакты.
Все, что нужно сделать — заменить обработчик на setAjaxClickHandler. Компонент link1 при рендеринге шаблона страницы на сервере добавит обработчик onclick с AJAX запросом. Компонент label1 при рендеринге асинхронного ответа ответит яваскриптом, изменяющим его текстовое значение на клиенте новым текстом (что то типа $(‘#label1’).text(‘I am AJAX clicked’) ), который выполнится с помощью eval(). Ничего дополнительно кодировать не нужно.
При этом следует заметить, что AJAX запросы, как и обычные синхронные, выполняются в контексте экземпляра страницы (с доступом к состоянию всех элементов страницы и их данным) — в отличие от MVC контроллера который пересоздаеться каждый раз.
В этом и суть компонентов — они сами обеспечивают сохранение своего состояния, рендеринг интерфейса и обработку событий, в зависимости от контекста. Программист сосредотачивается на бизнес-логике а верстальщик работает с чистым HTML. Компоненты обеспечивают модульность проекта и, как следствие, масштабируемость разработки. Компоненты легко расширяются простым наследованием от соответствующего класса с перегрузкой необходимых методов. Например, компонент ввода даты наследует функционал элемента ввода текста (для тега ), но при рендеринге дополнительно формирует строку яваскрипта, назначающую тэгу плагин DatePicker из JQuery UI. А при обработке запроса с сервера преобразует принятые значения в DataTime.
На первый взгляд, статья — пиар некоей наколенной поделки, но в этом нет особого смысла. В отличие от большинства «велосипедистов» я реалист и знаю что использовать свои велосипеды буду только я сам — собственно для того их и писал чтобы выполнять свои проекты, которые дают мне кусок хлеба, в разы быстрее и проще.
Но мне хотелось бы обратить внимание сообщества, особенно новичков, убежденных что ООП и MVC это одно и тоже, что несмотря на наличие распиаренных модных фишек и всякого рода “трендов”, зачастую существуют гораздо более простые логичные и естественные решения и не всегда стоит бежать как стадо леммингов только потому что это направление указано некими гуру — теоретиками программирования или потому что гугл пиарит очередной JavaScript фреймворк.
Комментарии (45)
A_HREF
05.10.2015 14:03+2Изобрели свой ВебФормс)))
caballero
05.10.2015 14:13нет. Переделал вебформс для удобоваримого употребления. Да и идея не моя — я не такой продвинутый. Просто незакомплексованый — у меня за многолетний опыт програмирования уже имунитет на очередные модные фишки. Потому смотрю обьективно что есть хорошо а что есть от лукавого.
caballero
05.10.2015 14:09-4Кому?
здравому смыслу вообще и компонентам в частности
А по-моему, та его вариация, которую предлагает asp.net, прекрасно ложится на работу http-сервера. Что неестественного?
на работу сервера может быть -серверу все равно. А вот на работу програмиста — нет. Програмист должен заниматся имплементацией бизнес логики а не заботой о том как восстановить состояние страниы после перезагрузки.
которой в протоколе нет. Это и есть костыль.
в таком случае и сессионные хранилища предоставляемые вебсерверами -костыль
О том, что сессия — это серверное хранилище. Серверное хранилище не позволяет вам сделать дешевую серверную ферму (горизонтальное масштабирование) — вам надо либо прилеплять сессии к конкретной машине, либо выносить их за пределы машины (а это означает, что появляются дополнительные накладные расходы).
раскажите это J2EE серверам которые лихо масштабируются вместе со всеми сессиями. Чего кстати не делает и ваш драгоценный MVC.
Компоненты «по своей природе» ортогональны масштабируемости.
что именно там не масштабируется? Если уж вы такой фанат MVC то могу вас обрадовать — архитектура любого компонента и есть маленький MVC — там есть контроллер (обработчик событий) модель и представление. разница только в том что этот MVC не размазан по всему приложению.
И не пытайтесь меня сразить вумными конструкциями типа «ортогональны масштабированию». Я не начинающий разраб.
Чем ваша архитектура отличается от WebForms?
Прежде всего, устранением проблемы взаимодействия с HTML кодом (фронтэнд-разрабы которые работали с WebForms понимают о чем я). В остальном идея совпадает. Майкрософт устранил проблемы заменив технологию на черти что.
Я устранил взявши другую идею, которая сохраняет компонентный подход (служивший годами верой правдой — разработчики WebForms опять же понимают о чем я, начинающие, которые ничего не видели кроме MVC разумеется нет) но при этом представление полностью отделяется от серверной логики. Там нет никаких автоматически сгенеренных ID, никаких «птичьих» языков шаблонизаторов. Конечно пассивные шаблонизаторы тоже не идеальны. но нет в мире ничего идеального.
И вообще непонятна ваша болезненная реакция — боитесь что завтра ваш менеджер прочитавши статью уволит вас как больше ненужного MVC програмиста.
lair
05.10.2015 14:20здравому смыслу вообще и компонентам в частности
Нет, MVC не противопоставляет себя компонентам. Собственно, вы сам позже пишете, что [ваше] компоненты внутри себя представляют маленький MVC.
Програмист должен заниматся имплементацией бизнес логики а не заботой о том как восстановить состояние страниы после перезагрузки.
Если вы абстрагируете программиста от естественных деталей протокола, для которого он пишет, вы делаете это ценой чего-то. Идеология asp.net MVC была в том, чтобы вернуть программиста ближе к протоколу, чтобы писать более естественные для окружения вещи.
в таком случае и сессионные хранилища предоставляемые вебсерверами -костыль
Так и есть.
раскажите это J2EE серверам которые лихо масштабируются вместе со всеми сессиями.
А чего там рассказывать, я даже понимаю, как это делается.
Чего кстати не делает и ваш драгоценный MVC.
Кстати, неправда. В MVC можно сделать оба описанных выше сценария.
что именно там не масштабируется?
А я вроде и не говорил, что что-то не масштабируется.
Прежде всего, устранением проблемы взаимодействия с HTML кодом (фронтэнд-разрабы которые работали с WebForms понимают о чем я).
Ага, а у вас html-код не меняется?
боитесь что завтра ваш менеджер прочитавши статью уволит вас как больше ненужного MVC програмиста.
Нет, не боюсь. Моя нынешняя работа вообще не связана с MVC.caballero
05.10.2015 14:38-2Если вы абстрагируете программиста от естественных деталей протокола, для которого он пишет, вы делаете это ценой чего-то. Идеология asp.net MVC была в том, чтобы вернуть программиста ближе к протоколу, чтобы писать более естественные для окружения вещи.
Я считаю что задачf програмиста реализовывать бизнес логику, то есть практическую задачу. Вас не смущает что языки высокого уровня абстрагируют програмиста от функционирования процессора? Наверно есть ряд задач которые требуют работы с протоколом, но большинство задач требуют реализации бизнес-логики. Это и есть «естественныве вещи».
Ага, а у вас html-код не меняется?
Не меняется ничего что влияет на работу фронт-энд разраба.
Работа бэск-энда и работа фронтэнда, как вы изволили выразится — ортогональна.
lair
05.10.2015 14:40+1Вас не смущает что языки высокого уровня абстрагируют програмиста от функционирования процессора?
Не смущает. Вопрос цены этой абстракции.
Наверно есть ряд задач которые требуют работы с протоколом, но большинство задач требуют реализации бизнес-логики.
Бизнес-логику надо реализовывать в бизнес-слое.
Не меняется ничего что влияет на работу фронт-энд разраба.
Кстати, интересно. А как у вас реализуется классическая задача «если выбран радио 1, показать вот эту группу, если выбран радио 2, показать вот эту группу»?
Работа бэск-энда и работа фронтэнда, как вы изволили выразится — ортогональна.
Вот если они несогласованы, мы получаем impedance mismatch, который постоянно приходится чем-то затыкать.caballero
05.10.2015 14:54-1Не смущает. Вопрос цены этой абстракции.
что то мне подсказывает что писать на аcсемблере гораздо более хлопотно чем на C#.
Кстати, интересно. А как у вас реализуется классическая задача «если выбран радио 1, показать вот эту группу, если выбран радио 2, показать вот эту группу»?
как и в вебформс — выбрали радиокнопку сработало событие на сервере — для одной панели (сервер компонент Panel, клиент — div) вызвали setVisible(false) для другой setVisible(true)
Все.
lair
05.10.2015 14:56что то мне подсказывает что писать на аcсемблере гораздо более хлопотно чем на C#.
… и тем не менее ассемблер не вымер.
сработало событие на сервере — для одной панели (сервер компонент Panel, клиент — div) вызвали setVisible(false) для другой setVisible(true)
Очень хорошо, и как этиsetVisible
повлияли на html-код?caballero
05.10.2015 15:51и тем не менее ассемблер не вымер.
да, но использует в узком классе (или правилнее количестве) задач. Чего желаю и MVC
Очень хорошо, и как эти setVisible повлияли на html-код?
кэп подсказывает что будет показан тот или другой кусок HTML кода.lair
05.10.2015 15:53кэп подсказывает что будет показан тот или другой кусок HTML кода.
Что значит «будет показан»? В ответе HTTP-сервера будет либо один, либо другой фрагмент?caballero
05.10.2015 16:26в данной реализации да
можно было бы конечно выставлять display:none но зачем гонять лишнее.
если у компонента стоит что он невидим шаблонизатор его просто проигнорирует.
В противном случае будет вызван метод render()
и компонент изменит соответствующий ему фронтэнд тэг.
lair
05.10.2015 16:28в данной реализации да
В этот момент у верстальщика взрывается мозг: он делал верстку с учетом двух дивов, а он стал один.
и компонент изменит соответствующий ему фронтэнд тэг.
И снова плач верстальщика: он написал одно, а в аут ушло другое.caballero
05.10.2015 16:41В этот момент у верстальщика взрывается мозг: он делал верстку с учетом двух дивов, а он стал один.
пока ни разу не взорвался
верстальщик вообще то когда верстает имеет представление что он верстает и как оно работает.
а как по вашему работает интерфейс на каком нибудь ангуляре где виимость блока прибиндена к тем же радиокнопкам?
И снова плач верстальщика: он написал одно, а в аут ушло другое.
какое другое? Ушли данные а не изменилась верстка. Да текст в поле может оказаться разной длины но я не вижу как бывает по другому при других технологиях.
lair
05.10.2015 16:43В итоге все сводится к очевидному: верстальщик должен быть знаком с технологией, под которую он верстает. И в этом смысле нет фундаментальной разницы, используете вы ваш подход, Razor, или что-то еще — все равно результирующий html-код будет отличаться от того, к чему привык верстальщик, и ему надо будет это учитывать.
caballero
05.10.2015 17:10В итоге все сводится к очевидному: верстальщик должен быть знаком с технологией, под которую он верстает.
Он должен быть знаком с поведением контента который он верстает.
И в этом смысле нет фундаментальной разницы, используете вы ваш подход, Razor,
Верстальщака интесует не фундаментальность а то что он видит.
Видит HTML тэг или неведомую конструкцию.
lair
05.10.2015 17:11Видит HTML тэг
Угу, давайте посмотрим на конкретном примере.
Вот мы хотим компонент «грид». Стандартный компонент, в любом фреймворке неплохо бы иметь. Как он выглядит в html-коде в вашем случае? В том html-коде, который верстальщик пишет?caballero
05.10.2015 17:33<table class="ctable" > <tr ><th >Наименование </th><th width="50">Ед.изм.</th><th width="40">Кол.</th> <th width="40">Цена</th> <th width="50"> </th> </tr> <tr zippy="detail"> <td zippy="item"></td> <td zippy="measure"></td> <td zippy="quantity" align="right"></td> <td zippy="price" align="right"></td> <td><a zippy="edit"><i class="icon-edit"></i> <a zippy="delete"><i class="icon-remove"></i></a> </td> </tr> </table>
не пойму как тут отформатироватьlair
05.10.2015 17:36А сортировка? Фильтрация? Пейджинг? Раскраска зеброй? Инлайн-редактирование?
(отдельно большое вам спасибо за необходимость дважды определять колонку — сначала в заголовке, а потом в теле)caballero
05.10.2015 18:04-1А сортировка?
как и в .NET MVC соответстующая ссылка в заголовок.
Фильтрация?
каким боком тут фильтрация?
Раскраска зеброй?
CSS3 — 21-ый век за окном
Пейджинг?
есть компонент который прописывается одной строкой кода.
На клиенте просто Все остальное автоматически — генерация страницы, обработка событий и вынимание сооьвеььсвующей порции данных из источникак данных для грида.
пример из PHP
$detailt = $this->add(new DataView('detail', new DocDataSource(), $this, 'doclistOnRow'));
$this->add(new Paginator('pag', $detailt));
. Это компоненты, сынок.
Инлайн-редактирование?
а что в MVC есть инлайн редактирование?
Впрочем, здесь я могу автоматически вывести таблицу в любыми инпутами и все они автоматически же отправятся на сервер.
(отдельно большое вам спасибо за необходимость дважды определять колонку — сначала в заголовке, а потом в теле)
Почему дважды? заголовок это верстка — там ничего не надо определять.
Кроме того для чисто текстовых жанных есть еще один компонент который автоматически
рендерит сортировку и пагинацию
lair
05.10.2015 18:09как и в .NET MVC соответстующая ссылка в заголовок.
В html-коде это видно?
(и нет, «в asp.net mvc» это не так)
каким боком тут фильтрация?
Таким — хочу кликнуть на заголовке колонки и ввести фильтр.
есть компонент который прописывается одной строкой кода.
В html-коде это видно?
а что в MVC есть инлайн редактирование?
Для asp.net mvc, конечно же, есть компоненты, поддерживающие инлайн-редактирование в гридах.
Впрочем, здесь я могу автоматически вывести таблицу в любыми инпутами и все они автоматически же отправятся на сервер.
Как это выглядит в html-коде?
Почему дважды? заголовок это верстка — там ничего не надо определять.
Потому что колонка — это одна сущность, у которой есть название (заголовок) и привязка к данным. А в вашем коде название прописано отдельно, привязка к данным — отдельно. Ничего не стоит перепутать.caballero
05.10.2015 18:34В html-коде это видно?
видно. куда оно денется
(и нет, «в asp.net mvc» это не так)
там вообще никак. Но это один из способов имплементации.
Таким — хочу кликнуть на заголовке колонки и ввести фильтр.
можно запрограмировать. Где это встроено в MVC?
Как это выглядит в html-коде?
древний пример для PHP версии
zippy.com.ua/index.php?p=/Pages/Examples/Example10
Для asp.net mvc, конечно же, есть компоненты, поддерживающие инлайн-редактирование в гридах.
Это, как и вышеуказаное, не относится к сути обсуждаемого архитектурного решения. Надо будет сяду и напишу такой компонент для своего фреймворка. С фильтрами блекджеком и юными пррамистками. Пока инлайн редактирование нафиг не нужно было ги в одном проекте…
А в вашем коде название прописано отдельно, привязка к данным — отдельно. Ничего не стоит перепутать.
С чего бы оно путалось? Табличные данные выводят сколько существует HTML — первый раз слышу о проблеме перепутывания заголовков и данных.
lair
05.10.2015 18:39видно. куда оно денется
Вот, покажите. Меня, собственно, интересует html-код для нормальной полнофункциональной реализации грида (сортировка, фильтрация, инлайн-редактирование, пейджинг).
там вообще никак.
Это неправда.
Это, как и вышеуказаное, не относится к сути обсуждаемого архитектурного решения.
Относится-относится. Вы декларируете, что в вашем решении верстальщик имеет дело с обычным html-кодом. Вот я и хочу на это посмотреть.
С чего бы оно путалось?
С того, что люди ошибаются.caballero
05.10.2015 18:58Это неправда.
Еще раз. Здесь обсуждается концепция. А вы мне расказываете о неких готовых решениях гридов и прочее.
У майкрософта сотни програмистов. Они понаписали компонентов. Я написал то что успел написать.
Как наличие того или иного компонента написаного в пределах концепции влияет на саму идею? Завтра напишут компонент с фильтрами и сортировками на перле для CGI и что это докажет?
Я вам показал простой грид где чистый HTML код и код который для Razor где вообще никакого HTML а непонятная верстальщику абракадабра где ему надо еще догадаться как добавить атрибут в тег. А вы продолжаете доказывать что верстальщику там проще.
В MVC програмиста волнует какой там будет заголовок а в моем решении нет. И не должно волновать — програмисту незачем заглядывать в PSD файл созданный дизайнером чтобы прописать заголовки в модели для грида. Вы считаете что должен? Тогда в чем смысл разделения кода от представления?
Вообще спор уже бессмысленный. Я не ставлю задачу когото переубедить.
. пишите как вам по кайфу.
Мне понравилась идея я ее реализовал попинал, понял что это есть хорошо и изложил это на бомаге. Удачи.
.
.
Относится-относится. Вы декларируете, что в вашем решении верстальщик имеет дело с обычным html-кодом. Вот я и хочу на это посмотреть.
я вм уже сто раз показал — это все чистый HTML
С того, что люди ошибаются.
А в MVC человек не может прописать не тоо название в атриуте модели?lair
05.10.2015 19:02Как наличие того или иного компонента написаного в пределах концепции влияет на саму идею?
Так я не про наличие компонентов говорю, я хочу увидеть, как такой компонент выглядит в коде.
Я вам показал простой грид где чистый HTML код
Вы покажите пример этого же грида для описанной мной ситуации.
В MVC програмиста волнует какой там будет заголовок а в моем решении нет. И не должно волновать — програмисту незачем заглядывать в PSD файл созданный дизайнером чтобы прописать заголовки в модели для грида. Вы считаете что должен?
В любом LoB-приложении заголовки прописывает не дизайнер, а бизнес-аналитик. И они должны быть униформны по всей системе.
А в MVC человек не может прописать не тоо название в атриуте модели?
Может. Но вероятность этого ниже, потому что одно дело — следить за атрибутом на свойстве, а другое — сопоставлять порядок в двух разных списках.
lair
05.10.2015 14:11О тестировании (и вообще о граблях):
/master/src/ZippyNet/Core/HttpRequest.cs
public class HttpRequest { //... public HttpRequest() { this.uri = HttpContext.Current.Request.Url.Query; //... if (HttpContext.Current.Request.QueryString["q"] != null || HttpContext.Current.Request.Form["q"] != null) { //... if (HttpContext.Current.Request.HttpMethod == "POST") { q = HttpContext.Current.Request.Form["q"]; } else { q = HttpContext.Current.Request.QueryString["q"]; } //... } if (HttpContext.Current.Request.QueryString["r"] != null) { //... string r = HttpContext.Current.Request.QueryString["r"]; //... object[] p = (object[])Newtonsoft.Json.JsonConvert.DeserializeObject(r); //... } } }
/master/src/ZippyNet/Core/Html/Form/TextInput.cs
public override void getRequestData() { this.setText(HttpContext.Current.Request.Form[this.id]); }
(и так далее)
Вот в WebForms по этим граблям уже походили, поэтому в MVC сделали иначе, а в WebAPI и OWIN — ушли еще дальше от них. А вы продолжаете по ним прыгать.
caballero
05.10.2015 14:29-1нет там никаких граблей. Я не претендую на особое качество кода — моя задача была просто описать идею. Код можно совершенствовать.
Просто в данном случае нет смысла тестировать дергая экшены или API. Тут другая идеология. Вы как пользователь взаимодействуете с компонентами. и тестировать надо точно также. Как передать данные серверу и получить ответку задача компонента. Туда вообще незачем лазить — black box. Завтра протокол может быть переписан по человечески но это не отразится на работе существующего сайта — там компонент, как он передал данные как получил как себя отрендерил никак не влияет на бизнес логику и взаимодействие с клиентом.
Что касается вышеназваных технолий — они ушли чтобы устранить проблемы MVC а не WebForms.
Зачем мне WebApi если у меня просто сайт, как в большинстве случаев, и нет необходимости взаимодействовать в с внешними сервисами.
Использовать это чтобы взаимодействовать с клиентскими фреймворками — как минимум непродуктивно с точки зрения трудозатрат на разработку. В этом просто нет смысла если задача реализуется класическим способом.
Про грабли на мобильных браузерах (да и просто браузерах) от клиентских javascript фрейморков я уже упоминал.
lair
05.10.2015 14:34Просто в данном случае нет смысла тестировать дергая экшены или API. Тут другая идеология.
Почему нет смысла? Нам же надо протестировать, что код, который реализует бизнес-логику, корректно передает данные в представление и получает их оттуда.
Что касается вышеназваных технолий — они ушли чтобы устранить проблемы MVC
Нет, просто они развивали начатые в asp.net MVC архитектурные принципы.caballero
05.10.2015 14:48Почему нет смысла? Нам же надо протестировать, что код, который реализует бизнес-логику, корректно передает данные в представление и получает их оттуда
Нет нам надо протестировать что приложение работает корректно.
К примеру, есть компонент — ссылка по которой сервак должен ответить «Hello, dude!»
Вот и тестируйте что если кликнуть по компоненту (с помощью selenium или там Codeception) вас поприветствут. А как тестировать сами компоненты — это уже задача их разработчика Не вы их пишете не вам и тестировать — тестируйте свой сайт. А если кривой компонент то тут то же самое если кривая реализация .NET MVC фреймворка или AngularJs — вопрос к производителю оного..
Именно в этом одна из фишек компонентов — масштабируемость разработки.
Нет, просто они развивали начатые в asp.net MVC архитектурные принципы..
Это дипломатичный способ сказать — устраняли косяки :).lair
05.10.2015 14:51Нет нам надо протестировать что приложение работает корректно.
Это слишком общая задача.
Не вы их пишете не вам и тестировать — тестируйте свой сайт
Вот я и хочу протестировать, что когда компонент мне сказал «по мне кликнули», пошел вызов в бизнес-слой «пришел пользователь и кликнул», а ответ бизнес-слоя завернули в компонент. И следующим тестом — что когда в бизнес-слое вызвали «пришел пользователь и кликнул», тот вернул «Hello, dude!». Unit-тестирование во всей его красе.
Это дипломатичный способ сказать — устраняли косяки
С этой точки зрения, любое развитие ПО — устранение косяков.caballero
05.10.2015 15:16-1Это слишком общая задача.
это главная задача нет никаких причин придумывать еще какие то.
Unit-тестирование во всей его красе.
зачем вам эта краса? У вас приложение — вы тестируете его бизнес логику. Какое вам дело что происходит в каких то слоях. Точнее у вас один слой — слой бизнес логики.
На сервере — это просто функция — обработчик события которая реализует логику. В данному случае Вызвать что то типа LabelHello.setText(«Hello dude»);
Одна строка кода. Никаких model которые чем то заполнить потом куда то передать, да еще и указать какой view
просто ( код абстрактно)
function linkDudeOnClick(Sender){
LabelHello.setText(«Hello dude»);
}
в статье более полный пример.
вот этот ваш код вы и тестируете. Остальное забота компонентов
— их достаточно оттестировать один раз и это забота их разраба.
Юнит тесты надо гонять там где програмист кроме бизнес логики програмирует еще кучу всего связанного с инфраструктурой а не полезным кодом. — как том же MVC. И в каждом новом сайте опять програмирует то же самое а значит надо опять тестировать.
.
С этой точки зрения, любое развитие ПО — устранение косяков.
вот я и устраняю только другим способом
Удивляюсь как все эти новомодные тренды, всякие TDD и прочие изобретения кибинетных теоретиков перекручивают людям мозги.lair
05.10.2015 15:25+2это главная задача нет никаких причин придумывать еще какие то.
Эту задачу необходимо разумным образом декомпоновать, иначе она в голову не влезает.
зачем вам эта краса?
Чтобы тестировать один кусочек за раз.
У вас приложение — вы тестируете его бизнес логику.
Именно. Я хочу тестировать бизнес-логику, а не интерфейс. А вы предлагаете мне тестировать интерфейс.
Точнее у вас один слой — слой бизнес логики.
А как же разделение на представление и бизнес-логику? Пропало?
В данному случае Вызвать что то типа LabelHello.setText(«Hello dude»);
Одна строка кода. Никаких model которые чем то заполнить потом куда то передать, да еще и указать какой view
Да нет, просто у вас модель выродилась в одну строчку, которую вы собственно и передаете в представление через методsetText
.
function linkDudeOnClick(Sender){ LabelHello.setText(«Hello dude»); }
вот этот ваш код вы и тестируете.
Я правильно понял, что вы это называете бизнес-логикой и бизнес-слоем?
Юнит тесты надо гонять там где програмист кроме бизнес логики програмирует еще кучу всего связанного с инфраструктурой
А бизнес-логику юнит-тестами покрывать не надо?
вот я и устраняю только другим способом
Ну, в вашем конкретном случае вы возвращаетесь к тем проблемам, которые были в WebForms, причем были давно.
Удивляюсь как все эти новомодные тренды, всякие TDD
Очень новомодные, ага. TDD меньше, чем на год моложе, чем asp.net (это если от книжки Бека считать, на самом деле — старше).
caballero
05.10.2015 15:45-2Эту задачу необходимо разумным образом декомпоновать, иначе она в голову не влезает.
Ну если в голову влезает реализация проекта то как то уже место для тестирования того что уже и так в голове должно быть.
Чтобы тестировать один кусочек за раз.
зачем? вы пользователю отдаете проект или его кусочки?
Именно. Я хочу тестировать бизнес-логику, а не интерфейс. А вы предлагаете мне тестировать интерфейс.
нет. вы тестируете бизнес логику с помощью интерфейса. Того самого которым пользуется конечный юзер.
вы сели в автомоиль и едете — проверяете как он едет. можно конечно полезть в двигатель но зачем если он нормально едет.
А как же разделение на представление и бизнес-логику? Пропало?
как раз бизнес-логика отделена от представления максимально насколько это возможно.
Да нет, просто у вас модель выродилась в одну строчку
это потому что оперируете категориями MVC.
модель — это по сути то где хранятся данные и где им манипулируют. как правил персистентное хранилище. Это класическое понятие модели. оддель не передается скопом в представление.
Я правильно понял, что вы это называете бизнес-логикой и бизнес-слоем?
да. Это то что делает приложение с прикладной точки зрения. не больше и не меньше.
А бизнес-логику юнит-тестами покрывать не надо?
ну считайте что проверка этого конкретного клика и есть юнит тест.
Юнит тест именно бизнес логики а не компонента.
Ну, в вашем конкретном случае вы возвращаетесь к тем проблемам, которые были в WebForms, причем были давно.
в таком случае я просто использовал бы WebForms. именно устранение тих проблем и есть смысл данной поделки. просто погулите изначальные проблемы webForms именно то что свзано с предсталением.
Очень новомодные, ага. TDD меньше, чем на год моложе, чем asp.net (это если от книжки Бека считать, на самом деле — старше)
я имел ввиду модные сейчас. Наконецто удалось распиарить.
Лет 15 назад был моден RUP(Rational unified proces) теперь Agile. Что изменилось по сути в програмировании? Да ничего. Потому все это уйдет в небытие и вынут что то новое, новые теории организации проектов, тестирования и т.д. Только производительность труда, то есть достижение конечного результата от этого мало меняется. Тогда рисовали в UML теперь сидим на скрамах.
lair
05.10.2015 15:52+1Ну если в голову влезает реализация проекта
Так не влезает же.
зачем?
Чтобы получать точную диагностику, где и что сломалось.
можно конечно полезть в двигатель но зачем если он нормально едет.
А если не едет — где искать, в двигателе, в КПП, еще где-то?
как раз бизнес-логика отделена от представления максимально насколько это возможно.
Значит, не один слой.
модель — это по сути то где хранятся данные и где им манипулируют. как правил персистентное хранилище. Это класическое понятие модели.
Вы, конечно, можете указать авторитетный источник для этого «классического понятия»?
да. Это то что делает приложение с прикладной точки зрения. не больше и не меньше.
… и этот бизнес-слой размещен напрямую в коде, отвечающем за представление, прямо в обработчике UI-события. Здравствуй, прекрасный мир WebForms. Спасибо, нет, никогда больше.
ну считайте что проверка этого конкретного клика и есть юнит тест.
Так как мне его проверить, не трогая слой представления?
именно устранение тих проблем и есть смысл данной поделки.
Проблема в том, что вы унаследовали кучу проблем WebForms, которые сейчас сохранять… стыдно.
Лет 15 назад был моден RUP(Rational unified proces) теперь Agile. Что изменилось по сути в програмировании? Да ничего.
А, если для вас за последние 15 лет в программировании ничего по сути не изменилось, тогда понятно, почему у вас код такой, как писали 15 лет назад.
Только производительность труда, то есть достижение конечного результата от этого мало меняется.
Странно, а я вижу, как меняется.caballero
05.10.2015 16:18-2Так не влезает же
потому что запииваете туда много лишнего не относящегося к сути дела.
Чтобы получать точную диагностику, где и что сломалось.
а если сломалось в сетевой карте?
А если не едет — где искать, в двигателе, в КПП, еще где-то?
искать ближайшую СТО и дать возможность специалистам по компонентам(двигателям и пр.) занятся своим делам.
… и этот бизнес-слой размещен напрямую в коде, отвечающем за представление, прямо в обработчике UI-события. Здравствуй, прекрасный мир WebForms. Спасибо, нет, никогда больше.
За представление отвечает компонент.
Так как мне его проверить, не трогая слой представления?
вы его не трогаете.
Даже не знаю как обьяснить. Нет никакого слоя представления в привычном вам понимании.
Каждый компонент сам отвечает за свой рендеринг. Общий layout то есть куда какой компонент разместить — это задача верстальщика он просто работает с HTML тэгами. Некоторые из них являются фронт-эндом компонентов но верстальщику это по боку. В вебформс было не совсем по боку. Вот эта проблема и устранена.
Проблема в том, что вы унаследовали кучу проблем WebForms, которые сейчас сохранять… стыдно.
я бы конечно покраснел но не пойму за что
.А, если для вас за последние 15 лет в программировании ничего по сути не изменилось, тогда понятно, почему у вас код такой, как писали 15 лет назад.
код как код. я не говорю что он красивый моя задача реализовать идею в свободное время. Потому и опенсорс. Кому надо шашечки а не ехать — пул реквесты никто не отменял.
а что такого появилось в КОДЕ за эти 15 лет? Особое форматирование?
Странно, а я вижу, как меняется.
конечно но не по вышеуказаным причинам. У меня поменялся когда я делаю проект на своем велосипеде. А о верстальщике который видит только HTML а не, прости Господи, Html.TextBoxFor(m => m.from, new { class = «form-control date» }) даже не говорю
lair
05.10.2015 16:27потому что запииваете туда много лишнего не относящегося к сути дела.
Нет, потому что сложность проекта больше, чем может съесть один человек.
а если сломалось в сетевой карте?
То я этого в юнит-тесте не увижу никогда, и слава б-гу.
За представление отвечает компонент.
… и страница, которая их объявляет. Разве нет?
Вот этот код — это представление:
public class MainPage : ZippyNet.Html.WebPage { public MainPage () { Label label1 = new Label("label1", "Hello"); ClickLink link1 = new ClickLink("link1"); //назначаем серверный обработчик link1.setClickHandler((HtmlComponent sender) => { label1.setText("I am clicked"); }); // добавляем элементы к классу страницы. this.add(link1); this.add(label1); } }
вы его не трогаете.
Как же не трогаю, когда в тестируемой функции явно сказаноLabelHello.setText("Hello dude")
? Прямое обращение к представлению.
Нет никакого слоя представления в привычном вам понимании.
Тогда как разделены представление и бизнес?
я бы конечно покраснел но не пойму за что
Это было видно еще по вашей реакции на ваш же код.
Кому надо шашечки а не ехать — пул реквесты никто не отменял.
А смысл, если я могу взять работающий и решающий мои задачи asp.net MVC?
а что такого появилось в КОДЕ за эти 15 лет?
C#, например. И посмотрите, какой путь он проделал: дженерики, лямбды, LINQ, динамики.
конечно но не по вышеуказаным причинам.
А по каким же?caballero
05.10.2015 16:54-1Нет, потому что сложность проекта больше, чем может съесть один человек.
ну так ешьте командой.
То я этого в юнит-тесте не увижу никогда, и слава б-гу.
вы ввобше ничего не увидите и ваш юнит тест будет нае намного полезнее комлексного. Отсюда вопрос а на фига он.
Прямое обращение к представлению.
обращение к компоненту. Или по вашему промежуточная структура в виде модели (а точнее не модели а DTO) в MVC меняет суть дела и вы там не обращаетесь к представлению?
считайте что компонент такой же промежуточный слой.
А смысл, если я могу взять работающий и решающий мои задачи asp.net MVC?
WebForms тоже решал ваши задачи. Более того старый добрый CGI тоже.
C#, например. И посмотрите, какой путь он проделал: дженерики, лямбды, LINQ, динамики.
я имел ввиду код а не язык. Если вы имеете ввиду что все эти шняги нужно юзать то я не против.
В данном случае я не хотел чтобы код сильно отличался от PHP прототипа. Меньше писать коментов, проще искать ошибки, и все такое. Потому просто переложил с учетом другого синтаксиса.
Да и какая разница как я там написал. Речь о сути идеи и ломке стереотипов. забудьте вы мой код как страшный сон.
lair
05.10.2015 17:04ну так ешьте командой.
А чтобы есть командой, надо побить на кусочки. А кусочки, в свою очередь, хочется тестировать вне зависимости друг от друга.
вы ввобше ничего не увидите
Почему? Юнит-тесту не нужная сетевая карта для работы.
обращение к компоненту.
А компонент — не часть представления разве?
Или по вашему промежуточная структура в виде модели (а точнее не модели а DTO) в MVC меняет суть дела и вы там не обращаетесь к представлению?
Во-первых, не DTO, а ViewModel. А во-вторых — меняет. В asp.net MVC я не обращаюсь к представлению, я возвращаю данные, которые использует представление. Inversion of control.
WebForms тоже решал ваши задачи. Более того старый добрый CGI тоже.
asp.net MVC решает их лучше.
я имел ввиду код а не язык.
А язык определяет код, который на нем написан. Появился новый язык — изменился код.
Речь о сути идеи и ломке стереотипов.
Да какая ломка стереотипов, если ваш код не рассматривать, то все ваше предложение сводится к asp.net WebForms с другим шаблонизатором и viewstate в сессии.caballero
05.10.2015 17:27-1А чтобы есть командой, надо побить на кусочки. А кусочки, в свою очередь, хочется тестировать вне зависимости друг от друга.
они и так независимы. тестируете реакцию одного линка потом другого.
Почему? Юнит-тесту не нужная сетевая карта для работы.
Никакому не нужна если на локале. Всем нужно если нет.
Во-первых, не DTO, а ViewModel. А во-вторых — меняет. В asp.net MVC я не обращаюсь к представлению, я возвращаю данные, которые использует представление. Inversion of
control.
Это именно DTO как бы вы его не называли. Присваиваете данные некоей структуре и отправляете ее менять представление. Как шаблонизатор использует данную структуру сути дела не меняет. Там Inversion of
control у меня DOM модель.
asp.net MVC решает их лучше.
с точки зрения фаната MVC — да.
На работе я програмирую и на WebForms и на MVC — мое решение лучше этого. Все относительно. По крайней мере я могу сравнивать на практике свсе три подхода а просто теоретизировать.
А язык определяет код, который на нем написан. Появился новый язык — изменился код.
смотря что писать. 2+2=4 ybrfr yt vtyztncz jn yfdjhjxtyjcnb zpsrf/
то все ваше предложение сводится к asp.net WebForms с другим шаблонизатором и viewstate в сессии.
viewstate там ни причем.
мое предложение сводится к использованию компонентного подхода вместо дебильного MVC.
lair
05.10.2015 17:34они и так независимы. тестируете реакцию одного линка потом другого.
… и если ни один не работает, я не знаю — ошибка в самих линках или в бизнес-логике. А еще бывают, внезапно, совместно используемые бизнес-объекты.
Никакому не нужна если на локале. Всем нужно если нет.
Да нет же. Юнит-тест гоняется в памяти машины, ему никогда не нужна сеть (разве что результаты куда-то запостить).
Присваиваете данные некоей структуре и отправляете ее менять представление. Как шаблонизатор использует данную структуру сути дела не меняет.
Есть фундаментальная разница между «я явно вызвал слой представления и передал ему данные» и «я вернул данные, которые потом будут использованы слоем представления». Второе намного легче тестировать (да и представление подменять тоже).
с точки зрения фаната MVC — да.
С точки зрения моего личного опыта. Я семь лет писал на WebForms, потом пять лет на MVC — и после WebForms писать на MVC было намного проще.
viewstate там ни причем.
Угу, а состояние компонентов у вас нигде не хранится?
мое предложение сводится к использованию компонентного подхода вместо дебильного MVC.
Во-первых, если вы не можете что-то понять и применить, это еще не делает его дебильным. Во-вторых, в MVC прекрасно можно использовать компоненты, так что «вместо» здесь неприменимо.caballero
05.10.2015 18:20-1… и если ни один не работает, я не знаю — ошибка в самих линках или в бизнес-логике.
ошибка в бизнес логике. В самих линках это когда вы дергаете экген а контроллер не реагирует. и ка тут поможет югит тестинг.
А еще бывают, внезапно, совместно используемые бизнес-объекты.
и какие проблемы?
Я семь лет писал на WebForms, потом пять лет на MVC — и после WebForms писать на MVC было намного проще.
а я сделал чтобы было ее проще.
Есть фундаментальная разница между «я явно вызвал слой представления и передал ему данные»
это словоблудие. я не вызываю никакой слой представления.
Я передаю данные компоненту. Там может быть мильен всякизх слоев а может быть ни одного.
компонент например может ответить аяксом.
Суть компонентов чтобы программист не пудрил себе и другим мозги всякими слоями.
Во-первых, если вы не можете что-то понять и применить, это еще не делает его дебильным
Именно потому что применяю и вижу что оно дебильное.
я трачу намного больше времени чем в WebForms. хотя бы для реализации персистентности.
Во-вторых, в MVC прекрасно можно использовать компоненты, так что «вместо» здесь неприменимо.
там где компоненты MVC нечего делать.
У меня есть компонент — он реагирует на клиентское событие серверным обработчиком. Шо тут делать MVC?
Ну запихните MVC в WebForms или наоборот если не понимаете о чем речь.
.lair
05.10.2015 18:32В самих линках это когда вы дергаете экген а контроллер не реагирует. и ка тут поможет югит тестинг.
Юнит-тесты помогут тем, что я просто не буду взаимодействовать с линками, а буду взаимодействовать прямо с бизнес-логикой.
и какие проблемы?
Никаких. Просто их лучше тестировать отдельно.
а я сделал чтобы было ее проще.
Это так не выглядит.
это словоблудие. я не вызываю никакой слой представления. Я передаю данные компоненту.
Компонент — это и есть слой представления.
Суть компонентов чтобы программист не пудрил себе и другим мозги всякими слоями.
Вы считаете, что слои не нужны?
Именно потому что применяю и вижу что оно дебильное.
Вы уверены, что проблема в asp.net MVC, а не в вас?
я трачу намного больше времени чем в WebForms. хотя бы для реализации персистентности.
А я — меньше (потому что у меня есть маппер из вью-моделей в персистентные модели, а дальше все делает ORM).
Но вы, наверное, хотели сказать, что вы тратите больше времени на то, чтобы сохранить состояние страницы. Странно, но опять-таки, я его трачу меньше (точнее, я трачу меньше времени на то, чтобы понять, что и почему viewstate передает таким или другим образом, и в каком порядке оно инициализируется).
там где компоненты MVC нечего делать.
Почему?
У меня есть компонент — он реагирует на клиентское событие серверным обработчиком. Шо тут делать MVC?
Вызвать серверный обработчик в рамках контроллера, в чем проблема-то?
lair
У меня простой вопрос: а почему вы противопоставляете MVC и «компонентные решения»? Это, в общем-то, ортогональные вещи, можно иметь компоненты в MVC, можно писать не в MVC без компонентов.
Это не «проблема», это данность. Вы в вебе, он по природе своей stateless. Любые попытки это состояние ему придать — в той или иной форме «костыли».
А-га. Здравствуй, масштабируемость, возвращение через час и тому подобные милые вещи. А уж как это мило тестировать…
caballero
MVC сам себя противопоставляет в вебе. Он просто неестественнен. Об этом, например, довольно доходчиво пишет Дмитрий Котеров в своей книге.
На самом деле компоненты уже реализуют принцип отделения логики от представления и (в данном случае) являются событийно ориентированными. MVC тут нечего добавить.
я ж не меняю протокол HTTP. Я делаю так чтобы с точки зрения програмиста была персистентность. То же самое делает WebForms — на нем разраб програмирует как на Делфи.
не очень понятно о чем вы. И какая проблема с тестированием напримрер тем же selenium. А компоненты масштабируемы по своей природе.
Еще раз -нет никаких проблем ни с масштабируемостью ни с чем то еще. Фремворк (PHP вариант, по крайней) уже обкатан, это не просто теория.
Я не говорю что это идеальное решение для всех случаев но для интерактивных сайтов где нужно постояное взаимодействия пользователя с интерфейсом (бизнес-приложения в первую очередь) не вижу ничего лучше. Лучше не моей реализации (можно взять тот же, более профессионально написаный Wicket ) — а в принцие самой архитектуры, самой идеи.
lair
Кому?
А по-моему, та его вариация, которую предлагает asp.net, прекрасно ложится на работу http-сервера. Что неестественного?
… которой в протоколе нет. Это и есть костыль.
Вот как раз это в вебе неестественно.
Интерфейсным тестированием мир не ограничивается. Меня всегда больше интересует unit и прочие серверные вариации.
О том, что сессия — это серверное хранилище. Серверное хранилище не позволяет вам сделать дешевую серверную ферму (горизонтальное масштабирование) — вам надо либо прилеплять сессии к конкретной машине, либо выносить их за пределы машины (а это означает, что появляются дополнительные накладные расходы). И это еще не считая проблем с уникальной идентификацией всех компонентов внутри сессии (а что, если я решил открыть два экземпляра одной страницы?).
Компоненты «по своей природе» ортогональны масштабируемости.
Чем ваша архитектура отличается от WebForms?