Сладкая жизнь, или Создание веб-приложения без написания кода



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


Для этого воспользуемся декларативным фреймворком Evado, в котором предметные сущности и связи между ними описываются через веб-интерфейс. Минимальное окружение необходимое для работы — это Node.js и база данных MongoDB. Среда разработки — Visual Studio Code. Впрочем последнее необязательно, если писать код не планируется.


Из гитхаба копируем шаблон приложения в локальную папку /party (в дальнейшем все пути указываются относительно нее) и выполняем установку зависимостей.


npm install

Файлы конфигурации находятся в папке config. При запуске приложения выбирается файл, в зависимости от значения глобальной переменной NODE_ENV. Каждая из конфигураций наследует параметры из config/default.


Изменим название приложения и базы данных (по умолчанию подключение происходит на localhost:27017 без пароля). Включим русский язык в компоненте i18n. Все, на этом настройка закончена. Далее выполним скрипт установки:


NODE_ENV=development node console/install

Затем запускаем приложение (запускать можно и из Visual Studio — конфигурация Start app):


NODE_ENV=development node console/start

В режиме development приложение доступно по адресу http://localhost:3000. Войти можно под пользователем Adam с правами администратора, который был создан при установке из настроек по умолчанию.


Email a@a.a
Пароль 123465

Краткий обзор фреймворка


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



В модуле "Студия" создаются и редактируются метаданные, которые описывают сущности (доменные модели) нашего приложения и связи между ними. Актуальные данные хранятся в виде JSON файлов в папке metadata/app.


В модуле "Офис" происходит эксплуатация приложения. Создаются и редактируются объекты на основе метаданных.


В модуле "Администрирование" осуществляется управление системными пользователями, правами доступа и другими параметрами приложения.


Подготовка вечеринки


Первым делом позаботимся о гостях вечеринки. Переходим в модуль "Студия" и создаем класс "Участник". Каждый участник должен быть напрямую связан с системным пользователем. Для этого добавим обязательный и уникальный атрибут "Пользователь".


На нашей вечеринки будут провозглашаться тосты. Создадим класс "Тост" со строковым атрибутом "Заголовок" и текстовым "Содержание". Чтобы привязать тост к участнику, добавим ссылочный атрибут "Автор". Ссылочный тип определяет, что его значение является ссылкой на другой объект. В данном случае на объект класса "Участник".


Не всегда тост предназначен широкой огласке. Иногда это еще черновик, и автор только решает, как ярче преподнести свою мысль. А иногда всю глубину тоста сможет понять только ограниченный круг людей, и видеть всем его необязательно. Для этого добавим строковый атрибут "Доступ". На вкладке "Перечисления" создадим перечисление с элементами значений доступа:


  • Никому кроме автора
  • Только друзьям
  • Для всех участников

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


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

Создадим атрибут "Дата создания" с кодовым именем "_createdAt" и типом представления "Локальная дата и время". Локальная дата означает, что будет учитываться часовой пояс.


Атрибут с кодовым именем начинающимся с подчеркивания относится к системным и доступен только для чтения.

Чтобы подружить двух людей необходимо получить согласие обоих. На нашей вечеринке будем придерживаться этого равноправия. Один участник создает дружбу, другой может ее принять или отклонить. Для этого создадим класс "Друг", который и будет определять дружеские отношения. Добавим обязательные ссылочные атрибуты "Приглашающий участник" и "Приглашаемый участник". Для определения состояния дружбы добавим строковый атрибут "_state" с типом представления "Состояние".


Во фреймворке Evado реализован конечный автомат для объектов сущностей. Каждый объект находится в определенном состоянии и может быть переведен в другое по указанным переходам.

Переходим на вкладку "Состояния" и создаем три состояния:


  • Ожидание решения (назначаемое по умолчанию)
  • Дружба принята
  • Дружба отклонена

Переходим на вкладку "Переходы" и создаем пару переходов:


  • Принять дружбу
    • Начальные состояния "Ожидание решения" и "Дружба отклонена"
    • Конечное состояние "Дружба принята"
  • Отклонить дружбу
    • Начальные состояния "Ожидание решения" и "Дружба принята"
    • Конечное состояние "Дружба отклонена"

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


Обратные связи


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


Чтобы отобразить все комментарии к тосту, добавим обратную ссылку "Комментарии" на класс "Комментарий" и атрибут "Тост". Чтобы отобразить все тосты участника, добавим обратную ссылку "Тосты" на класс "Тост" и атрибут "Автор".


Для отображения подтвержденных участников-друзей… И здесь предчувствие наносит первый удар, что одними декларациями обойтись не удастся. Действительно, чтобы найти таких участников необходимо:


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

Через веб-интерфейс можно задать связь только по одному атрибуту или наложить дополнительное условие. Для сложного отношения придется написать свою реализацию, и подключить ее в поле "Фильтр":


{"Class":"component/meta/relation/FriendMembers"}

Экспорт метаданных


Для того, чтобы созданные метаданные попали в приложение необходимо их экспортировать. Кнопка экспорта (импорта) расположена на верхней панели модуля "Студия". Указать можно любую целевую папку, но учтите, что приложение будет загружено только из metadata/app. После успешного экспорта приложение готово для эксплуатации в модуле "Офис".


Регистрация участников


Для самостоятельной регистрации пользователей на сайте в файле конфигурации console/default в секции params ставим


enableSignUp: true

Для нового пользователя необходимо создать объект класса "Участник". Для этого в модуле "Администрирование" создадим слушателя события регистрации.


События: auth.register
Описание: Создать участника при регистрации пользователя

И добавим к нему обработчик.


Кодовое имя: memberInstantiation
Описание: Создание объекта "Участника"
Конфигурация: {
    Class: 'evado/component/handler/MetaClassInstantiation',
    className: 'member'    
}

Права и обязанности


Базовой возможностью любого многопользовательского приложения является разграничение прав доступа. Фреймворк Evado реализует систему управления доступом на основе ролей.


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


Если вы лишились доступа через веб-интерфейс (например удалив права администратора), то внести изменения можно через консольный скрипт console/security.

В модуле "Администрирование" перейдем в раздел "Безопасность — Роли". Изначально в шаблоне приложения определены три роли:


  • администратор (полный доступ к данным)
  • пользователь (роль для идентифицированных пользователей)
  • гость (роль для анонимных пользователей)

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


Чтобы изменения безопасности вступили в силу необходимо выполнить перезапуск (кнопка в верхней панели).

Определим требуемые разрешения для участника:


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

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


{"Class":"component/meta/rbac/rule/ToastRule"}

Заключение


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


P.S.


Репозиторий готового приложения. Можно запустить в докере.


Онлайн демо. Войти как Боб:


Email: b@b.b
Password: 123456