Конференція Highload fwdays'19 — Autoscale, MySQL 8.0, Neo4j, Kafka and AWS Lambda | 05.10 | Київ

Быстрая веб-разработка с NReco

Лирическое вступление

Написать одну хорошую строчку кода, в общем-то, легко: она понятна, делает то, что нужно, ее легко прочитать и понять (если это не строчка на Perl). Когда строчек много, ситуация радикально изменяется в худшую сторону: реальность такова, что проектный код обычно пишется в условиях жестких временных ограничений далеко не клонами Джона Скита, и вместо хорошего кода «маємо те, що маємо». Другими словами, проектный код — это боль. Чем его меньше, тем лучше.

Мы используем фреймворки, чтобы создавать надежные архитектурные и инфраструктурные решения и не писать лишний код в проекте («велосипеды»). Мы используем компоненты, чтобы получить требуемую функциональность через сборку вместо написания одноразовой проектной реализации «с нуля».

Повторное использование кода — это серебряная пуля в борьбе с проблемой увеличения его количества. Мы используем полный арсенал (OOP, FP, CBD, TDD, DDD, MDD и т.д.) для организации кода таким образом, чтобы уменьшить количество кода, избежать дублирования и обеспечить те или иные его характеристики.

В этой статье я хочу рассказать про новый инструмент для организации эффективного повторного использования артефактов как в рамках одного проекта, так и между разными проектами. Инструмент называется NReco (для платформы .NET), и с его помощью можно:
— генерировать прототипы веб-приложений за несколько минут;
— быстро разрабатывать полнофункциональные бизнес-приложения и админки с написанием минимума проектного кода (до х10 раз быстрее, по сравнению c классической разработкой на WebForms/MVC);
— увеличить степень повторного использования как внутри проектов, так и между разными проектами в одной организации (уменьшение издержек = профит);
— радикально уменьшить затраты и улучшить качество при разработке проектов через организацию продуктовой линии с помощью фабрики программ (software factory).

NReco — это .NET проект с открытым исходным кодом, и он полностью бесплатен для коммерческого использования.

Немного истории


В далеком 2006 году мы в NewtonIdeas разрабатывали достаточно сложное BPM-решение для управления процессами аудитов в области обеспечения качества.

Согласно традиции, имелся в наличии 23-летний сеньор (я) и команда со скромным опытом .NET и ASP.NET WebForms в частности.
Уже тогда мы использовали Spring-подобный IoC-контейнер собственного производства, поскольку подходящего контейнера с XML-конфигурацией тогда не было (Spring.NET появился позже). Инверсия управления помогала в некоторой степени уменьшить связность проектного кода и обеспечить подобие порядка на уровнях слоя данных и бизнес-логики, правда, при этом растущие объемы XML-конфигурации компонентов сами по себе становились проблемой.

С UI была вообще беда: вырастали ASCX-шаблоны размером 60kb и больше, где размножались вложенные Repeater-ы и причудливо работающие databinding-выражения. Чтобы внести минимальное изменение, требовалось вдумчивое изучение всей этой колбасы, и это в итоге занимало неприличное количество времени у разработчиков.

У меня был хороший опыт использования XSLT (в начале 2000-х была мода генерировать HTML-страницы с помощью XSLT на сервере), и возникла мысль использовать проверенные инструменты для организации максимально легкого процесса создания моделей и описания их трансформаций. Что может быть проще описания модели в виде XML, ее мета-модели в виде XSD и трансформации в виде XSL? Мы смогли описывать бизнес логику декларативно в виде простых XML-файлов и генерировали реализацию в виде композиции специальных компонентов, описанную с помощью XML-конфигурации IoC-контейнера (подход был описан в статье журнала «Кибернетика и системный анализ»). Мы пошли дальше, начали описывать UI в виде XML-моделей и генерировали ASCX мегабайтами. Наступили на все грабли, на которые могли наступить. В проектах начали появляться сгенерированные XML-конфигурации размером 40mb и больше; был момент, когда большая часть системы задавалась в виде сложных XML структур, после чего пришло понимание, что не все XML-модели одинаково полезны.

В 2008 часть наработок были открыты в виде open-source проекта Open NIC.NET (инфраструктурные компоненты), а все, что касалось MDD, было написано с чистого листа и названо NReco.

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

В результате тотального рефакторинга и переосмысления накопленных наработок появилась полностью новая версия NReco 2.0, которая, я надеюсь, достойна внимания широкой аудитории.

Генерация NReco-приложения

При проектировании NReco 2.0 я старался максимально использовать существующую инфраструктуру VisualStudio. Например, при редактировании XML-моделей работает встроенная в VS-валидация и подсказки по XML-схемам, а все компоненты оформлены в виде NuGet-пакетов с активным использованием

XDT. Инфраструктурные компоненты, такие как IoC-контейнер или DALC, являются полностью автономными и могут быть использованы в любом .NET проекте.

Начать знакомство с NReco можно одним из 2-х способов:
— Клонировать Git-репозиторий code.google.com/p/nreco (бранч nreco2) и запустить несколько примеров
— Сгенерировать готовое ASP.NET приложение в виде NuGet-пакета.

Дополнительную информацию можно обнаружить на сайте nrecosite.com. Рассчитывать на полноценную документацию пока не приходится: может быть, у меня появится свободное время (ага, а курс снова будет 8грн), или, может, появятся новые контрибьюторы, или что-то еще. С другой стороны, благодаря наличию XML-схем и описанию трансформаций в виде XSL вполне посильно разобраться, что к чему.

Рассмотрим процесс генерации ASP.NET как наиболее эффектный. Допустим, нам нужен прототип для гипотетической мини-CRM с контактами клиентов и сопутствующими справочниками. Чтобы не усложнять процесс настройкой базы, изменим БД на SQLite:

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

Далее расположен конструктор для определения схемы данных и CRUD-интерфейса. Определим таблицы для справочников:

Для таблиц-справочников CRUD Type выберем Editable List, чтобы удобно было набить значения прямо в списке.

Также добавим таблицу с клиентами, которая будет использовать определенные выше справочники:

Генерация приложения — это тоже трансформация XML-модели (формируется по введенным данным) в набор файлов (в основном других XML-моделей), которые сформируют уникальный NuGet-пакет для установки в проект VisualStudio.

Установка сгенерированного пакета не должна вызвать особых трудностей: после генерации отображается подробная пошаговая инструкция. В целях тестирования проще всего создать в VisualStudio 2013 (Express for Web подойдет) новый ASP.NET Application Project с шаблоном Empty и core references для WebForms, и сразу установить только что скачанный NuGet-пакет (момент: VS Update 2 должен быть установлен, иначе при установке пакета вывалится ошибка).

Если все прошло успешно, можно запустить приложение нажатием F5, при первом запуске приложение само создаст файл с SQLite базой и инициализирует ее схему. В результате должна отобразиться логин-страница:

В базе по умолчанию создана учетная запись «admin» с паролем «1». После входа наполняем справочники:

И создаем запись для клиента:

Список клиентов, страница с деталями и редактированием прилагаются.

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

Все артефакты представлены в виде XML-моделей и легко поддаются расширению/модификации путем редактирования в VisualStudio; рассмотрим процесс добавления нового поля:

После изменения XML-модели надо повторить процесс трансформации модели, который происходит при каждой сборке проекта. Технически это реализовано особым MSBuild Task-ом, который вызывается на AfterBuild проекта для всех файлов проекта, где указан Build Action = XmlModel или которые имеют расширение *.dsm.config.

Этот Task ожидает, что у каждого файла с XML-моделями будет processing instruction с информацией о том, как трансформировать эту модель. Например, для config/dsm/data-schema.dsm.config (которая включает измененный файл clients.xml через XInclude) эта инструкция выглядит так:

<?xml-stylesheet type="text/xsl" href="../xsl/model-data-schema.xsl" output-file="../common/_generated_data-schema.xml.config"?>

Нестандартный атрибут этой PI output-file используется для указания имени файла, где должен быть сохранен результат трансформации. Сразу отмечу, что в результатом трансформации может быть и множество файлов (например для модели config/dsm/layouts.dsm.config).

Бинго! Этого достаточно, чтобы указать, какие XML-модели трансформировать, каким образом и где расположить результат. Все просто до неприличия, а значит, с этим сможет разобраться даже начинающий девелопер.

Конечно, считать XSL полноценным языком трансформаций можно только для преобразований XML → XML, однако в случае ASP.NET-приложения этого вполне достаточно: конфигурация IoC-контейнера описывается с помощью XML, а UI-шаблоны (ASPX/ASCX) описываются XML-подобным способом. Вообще говоря через XSL можно генерировать любые текстовые артефакты (в том числе и JavaScript/C# код), но в этом случае XSL не сможет обеспечить некоторые характеристики трансформации (например, синтаксическую корректность результата) .

Основная магия происходит в XSL-правилах и компонентах, композиция которых приводит к появлению требуемой функциональности.

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

Возможности

Список того, что можно сделать с XML-моделями «из коробки», не ограничивается простейшими CRUD-ами:
— можно задавать произвольные конфигурации рендеринга (в несколько колонок, в табах и т.д.);
— к спискам можно определять фильтры, в том числе достаточно сложные (условия на несколько полей с AND/OR);
— использовать массовые операции в списках;
— использовать произвольные UserControl-ы для рендеринга кастомных элементов или редакторов полей;
— декларативно описывать зависимости между редакторами на формах (классика жанра, 2 зависимых дропдауна, в таком духе);
— определять новые элементы в XML-моделях (например, для описания специфического оформления в конкретном проекте);
— отображать в виде диалогов, абстрактные формы (не привязанные к какой-то таблице; например, для бизнес-действий);
— задавать произвольные SQL-view для использования в провайдерах данных и UI;
— централизовано ограничивать доступ к данным на уровне SQL запросов;
— включить встроенную реализацию отслеживания изменений данных в БД (change data capture / change data tracking).

Этот минимальный набор фичей уже позволяет использовать NReco для быстрого построения админок и разнообразных бизнес-приложений (как минимум, их прототипов). В дальнейшем список доступных XML-моделей будет расширен: некоторые классные фичи из предыдущей версии NReco (например, XML-модель для описания индексации и поиска с помощью Lucene.NEТ) все еще ждут рефакторинга и оформления в виде NuGet-пакетов.

Помимо готовых моделей для описания слоя данных и UI, можно воспользоваться инфраструктурой NReco для определения своих XML-моделей и их трансформаций в результирующие файлы ASP.NET приложения. Таким образом можно организовать повторное использование артефактов как внутри проекта, так и между проектами, выделяя целые блоки функциональности в виде NuGet-пакетов.

Вместо заключения

Мне кажется, разработка в аутсорсинге наиболее брутальная. Временные и бюджетные ограничения всегда на первом месте, и любой новый подход проходит жесткую аппробацию реальностью и экономической целесообразностью. Каждый по-своему, но мы все таки используем объектно-ориентированный и компонент-ориентированный подходы в ежедневной разработке. С модель-ориентированной разработкой (MDD) как-то не сложилось — видимо, всё дело в реализации.

Технология NReco сформировалась и выжила в суровых условиях украинского аутсорсинга (более того, в небольшой команде, где каждый человек на счету); она не претендует на фундаментальность (как например, MDA от OMG), но при этом позволяет получить выгоды от использования подхода MDD в совершенно обычных веб-проектах уже сейчас, с минимальными затратами на внедрение и обучение.

LinkedIn

19 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

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

Привет,

по п.1 вроде как эта статья и есть туториал: княпаем на странице download что хотим сгенерирвать, тулза генерит нугет-пакет, создаем новый эмпти-проект в VS, устанавливаем пакет и F5-запускаем, и в идеале все заработало =) дальше имеем не лапшу сгенерированного кода (как в случае обычного скаффолдинга, который есть в VS, например), а аккуратные XML-модельки которые уже меняем ручками. Расширенный туториал — что и как делать можно дальше с модельками — можно наваять конечно, если будет интерес извне. Пока что похоже, что тема DSM не пользуется особой популярностью (жаль, конечно).

по п.2: согласен кроме изначальной генерации, потом захочется догенерить разного, по мере надобности, в существующий проект. Для этого будут дополнительные wizard-ы на сайте, я думаю.
Комман-лайн для генерации схемы / крудов не сильно удобен (имхо): надо ввести кучу исходных данных, а в виде веб-аппа этим и легко пользоваться, и легко поддерживать (те же фиксы-дополнения в «генератор генераторов»).

Любопытно было бы сравнить производительность с обычным стеком ms sql — entity — mvc — razor и чем-то модным, например mongodb — webapi — angularjs

но в таком стеке три компонента вообще не имеют отношения к делу: NReco никак не влияет на использование ms sql, mvc, razor. Более того, в промышленных проектах именно такой стек часто и используется. И то же самое касается mongodb и webapi.

NReco это по сути фабрика программного обеспечения (software factory), это уже даже не фреймворк. Просто его наиболее яркое применение — сверхбыстрая веб-разработка, где продуктовыми линейками есть веб-приложения.

А сравнивать стеки смысла нет. Это как сравнить Бугатти с КРАЗом: да, Бугатти быстрее, но 10 тонн гравия на ней перевезти будет не так-то просто :)

Сравнивать имеет смысл хотя бы убедиться что производительность упадет не в 10+ раз. Где-то есть живой сайт на котором можно глянуть скорость загрузки?
В ваших аналогиях — что КРАЗ не сожжет топлива на сумму больше стоимости гравия которую он перевезет.

К живым интранет-приложениям и админкам сайтов, где UI генерируется из моделей, естественно доступа нет. С другой стороны, можно просто сгенерировать апп и посмотреть, как он бегает.

Несколько примеров, где используется инфраструктура NReco:

В чем конкретно поможет загрузка и работа конкретного сайта? В каждом проекте своя схема БД, свои данные, запросы, в конце концов ресурсы сервера. Я подчеркнул в статье, с 2009 года сделаны _десятки_ проектов, которые работают, используются и приносят пользу заказчикам. Слой данных, конфигурируемый с помощью XML-моделей, по идее работает быстрее entity framework (просто потому что это не ORM, и производительность приближается к чистому использованию ADO.NET). Сгенерированный UI работает не медленнее, чем руками написанные списки и формы на WebForms.

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

Идея неплохая для простых решений. Универсальность ограничивает его сферу применения по всей видимости. Сопровождать и расширять приложение состоящее полностью из сервис локатора, сконфигурированного через xml выдается большим архитектурным ограничением, пусть у вас там и валидация по типам предусмотрена на уровне контейнера. То что все компоненты настраиваються через xml под вопрос ставит и производительность, первый вопрос который возникает сколько раз у вас там на один http запрос применяется рефлексия.
На уровне простого CRUD вполне может работать. Смутило еще много моментов. Лично я скорее всего бы предпочел бы подготовленый нугет пакет со структурой приложения и generics компонентами(которые можно и без рефлексии реализовать на любом уровне) и полноценный IoC, огромному количеству нетепизированного хоть валидируемого xml для общего сервис локатора со всеми вытекающем недостатками для такой абстракции.

Спасибо за попытку вникнуть в суть :)

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

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

Кстати, можно устанавливать только ту часть, которая реально нужна, с помощью установки нужных NuGet-пакетов: например, NReco.Application — подключит к приложению IoC и на этом все. Пакет NReco.Transform.Build подключит трансформацию XML-моделей при билде проекта. Это все есть в примерах, клонируйте и запускайте!

Года 3 назад возможно. Сейчас для простых решений я не вижу причин взять сабж вместо Entity framework + Odata контроллеров. Не надо ничего конфигурировать и писать sql.

Кстати, добавлю от себя кое-что к статье.

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

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

wtf??? генераторы генераторов....
macode.ru

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

Рад за вас, держите нас в курсе)

Нет, извините, не буду. У меня есть масса прекрасных занятий и способов провести время. (Только не пишите опять в ответ, что рады за меня)

по сути есть что сказать? )
количество моделей в цепочке может быть и больше (то есть генераторы генераторов генераторов хехе).

Генерация UI дает визуальный результат (и близко к реальности — формошлепят как бы все), поэтому именно эта метамодель использовалась для иллюстрации в статье. Инструмент (инфраструктура NReco, в дополнении к VS+NuGet) позволяет получить управляемый процесс для формализации, композиции и повторного использования компонентов совершенно разной сложности и уровня абстракции (позволяя выйти за восприятие компонента в парадигме ООП), это реально круто и стоит попробовать.

Если сейчас неясно почему и зачем (wtf), это прийдет со временем. Спросите старших товарищей, на то они и архитекты :-)

Спасибо, ознакомился. Ночью не осилил весь текст.

Омагад, GUI для скаффолда))

конечно гуй, а как вы хотели. ради этого все и делалось...

Подписаться на комментарии