Всем привет, меня зовут Дмитрий Кремнев и я Java-разработчик в команде Jmix. Недавно на конференции смотрел доклад, в котором спикер рассказывал, как его команда справлялась с проблемой быстрого написания админок для внутренних сервисов. Сначала они реализовали дорогое самописное решение для своей команды, затем появилась идея масштабировать его и для остальных команд. Искали готовые альтернативы на рынке, которые удовлетворят все их бизнес-требования, но в итоге остановились на гибридном кастомном решении, основанном на low-code платформе. Проблемы, которые они решали мне показались очень знакомыми, ведь мы в команде тоже с ними сталкивались. В этой статье я хочу показать, как с помощью Jmix решаются типовые задачи при создании админок. Постараюсь быть конкретным, показать плюсы и ограничения.
Типичные требования к админкам
Сначала необходимо определить требования, которые должна закрывать админка.
Доступ к данным в базе и возможность их редактирования.
Речь идёт о создании сущностей, DTO и CRUD-экранах. Количество сущностей может быть довольно большим, поэтому задача оптимизации этого процесса будет иметь высокий приоритет. Помимо оптимизации процесса создания модели данных и экранов, нужно принимать во внимание время, затрачиваемое на написание скриптов миграции, мапперов и других инфраструктурных элементов, ведь эти задачи также являются ресурсоёмкими.Разграничение прав доступа.
Создание ролевой модели также является критически важной задачей, потому как административный интерфейс даёт доступ к чувствительным данным и системным функциям.Аудит.
В данном контексте аудит подразумевает собой отслеживание изменений в модели данных. Он необходим как в случае работы инженера поддержки, чтобы разобраться в проблемной ситуации, так и в случае расследования инцидентов и выявления несанкционированных действий.Предоставление доступа к функциям и данным панели для внешних сервисов.
Чаще всего имеется в виду реализация различных REST-эндпоинтов для CRUD действий с данными и работы с внутренними сервисами. И при большом количестве сущностей рутинное создание CRUD-эндпоинтов занимает значительное время.
Что такое Jmix?
Архитектура фреймворка
Jmix Framework – это fullstack open-source фреймворк для создания web-приложений, который основан на Spring Boot и Vaadin Flow. Основная задача фреймворка – автоматизировать рутинные действия разработчика, при этом сохраняя гибкость процесса разработки и доступность привычных инструментов.
Jmix приложение является Spring Boot приложением с набором Jmix-стартеров. Вы также можете подключать практически любые другие привычные вам стартеры. Для работы с данными Jmix использует единую модель данных, которая основана на ORM фреймворке EclipseLink
.
За работу с клиентской частью отвечает SSR веб-фреймворк Vaadin Flow. Он основан на спецификации Web Components и наборе библиотек Google Polymer. В Vaadin Flow визуальные компоненты состоят из клиентской и серверной части. Код компонентов клиентской части написан JavaScript и TypeScript, серверной – на Java. Общение между ними происходит посредством AJAX-запросов. Таким образом, Jmix Framework использует единый язык для написания как backend, так и frontend частей приложения.

Для работы с Jmix Framework используется специальный плагин для IntelliJ IDEA, Open IDE и GigaIDE, который называется Jmix Studio. Studio предоставляет основные инструменты для работы с компонентами фреймворка: экранами, ролями, сущностями.
Хочу отметить, что Jmix не позиционирует себя как low-code платформа, потому как для полноценной работы приложения необходимо написание бизнес-логики, однако элементы low-code всё же присутствуют в виде различных дизайнеров. В данном случае более правильным определением будет less-code платформа, в которой используются вспомогательные элементы для написания кода.
Как Jmix решает задачи админки
Генерация CRUD и визуальные дизайнеры
Как уже было обозначено в требованиях – первостепенной задачей создания админ панели является создание объёмной модели данных и CRUD-интерфейсов для работы с ней.
Для этого в Jmix Studio предоставляет различные визуальные дизайнеры для работы с основными компонентами приложения. Одним из таких является дизайнер сущностей. В дизайнере сущности можно задавать связи между сущностями, определять атрибуты и устанавливать на них ограничения.

Визуальные дизайнеры лишь модифицируют то, что представлено в коде приложения. Этот код можно редактировать и вручную, не используя дизайнер. Изменения, написанные в коде будут отражены в дизайнере, и наоборот.
Показать код
@JmixEntity
@Entity
@Table(name = "USER_", indexes = {
@Index(name = "IDX_USER__ON_USERNAME", columnList = "USERNAME", unique = true),
@Index(name = "IDX_USER__DEPARTMENT", columnList = "DEPARTMENT_ID")
})
public class User implements JmixUserDetails, HasTimeZone {
@Id
@Column(name = "ID", nullable = false)
@JmixGeneratedValue
private UUID id;
@Version
@Column(name = "VERSION", nullable = false)
private Integer version;
@Column(name = "USERNAME", nullable = false)
private String username;
@Secret
@SystemLevel
@Column(name = "PASSWORD")
private String password;
@Column(name = "FIRST_NAME")
private String firstName;
@Column(name = "LAST_NAME")
private String lastName;
@Email
@Column(name = "EMAIL")
private String email;
@Column(name = "ACTIVE")
private Boolean active = true;
@Column(name = "TIME_ZONE_ID")
private String timeZoneId;
@Column(name = "ONBOARDING_STATUS")
private Integer onboardingStatus;
@JoinColumn(name = "DEPARTMENT_ID")
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
@OrderBy("sortValue")
@Composition
@OneToMany(mappedBy = "user")
private List<UserStep> steps;
@Column(name = "JOINING_DATE")
private LocalDate joiningDate;
@Column(name = "PICTURE", length = 1024)
private FileRef picture;
@Transient
private Collection<? extends GrantedAuthority> authorities;
// getters & setters
Дизайнер предоставляет возможность ознакомиться с доступными настройками для сущности и её атрибутов. Здесь же находятся элементы управления индексами для сущностей.
Когда модель данных готова, можно приступить к генерации CRUD экранов. Для этого существует специальный генератор, который вызывается с помощью диалогового окна.

Это диалоговое окно представляет собой мастер по созданию экрана, в котором можно выбрать шаблон генерации и заполнить его нужными параметрами. Можно не использовать шаблон, а создать кастомную разметку всего экрана с нуля. Для этого есть пустой шаблон, содержащий только каркас экрана.
Генератор экранов просмотра и редактирования анализирует мета-модель сущности для того, чтобы создать нужные визуальные компоненты для работы с сущностью. Помимо этого, для экрана просмотра будет сгенерирована таблица, содержащая выбранные столбцы, которые имеют соответствие к атрибутам сущности
Например, в следующей таблице представлен пример генерации компонентов на основе информации о типе атрибута сущности:
Атрибут | Тип | Компонент для редактирования |
---|---|---|
|
| TextField |
|
| PasswordField |
|
| EmailField |
|
| Checkbox |
Сгенерированные компоненты будут иметь привязку к данным. Если редактировать атрибут через связный компонент и затем нажать кнопку сохранения, то произойдёт коммит в базу данных.
Созданные экраны можно модернизировать и реализовывать внутри них свою бизнес-логику.
Если нужны не только данные из SQL базы, то есть вариант создать DTO сущности, с которыми можно будет работать также, как и с JPA сущностями. Однако нужно будет сконфигурировать сервис, который будет получать эти данные из внешнего источника. Если конфигурировать не хочется, то можно подложить OpenAPI схему генератор в Studio, чтобы сгенерировать модель данных и клиента.
К слову, о генераторах. Всё чаще для ускорения рутинных действий используются AI-ассистенты – например, для генерации UI, REST-интерфейсов или структуры сущностей. Это существенно экономит время, но такая генерация может не учитывать специфику проекта и не понимать бизнес-контекста. С таким подходом нужно быть осторожным и работать по принципу "доверяй, но проверяй". Если не вычитывать сгенерированный код, то можно получить значительные проблемы на поддержке.
По этой причине в Jmix сделан упор на генераторы. Но если задача всё ещё остаётся рутинной и в то же время выходит за область возможностей генераторов, то можно воспользоваться встроенной в Jmix Studio панелью AI-ассистента. Предобученная под специфику Jmix модель понимает контекст и может сгенерировать более сложный код. Но тут работает всё то же правило с проверкой, потому как любая AI-модель может "галлюцинировать".
Ролевая модель
В Jmix существует встроенная система контроля доступа на основе Spring Security. Центральным элементом этой системы является роль. Всего во фреймворке 2 типа ролей: ресурсные роли и роли уровня строк.
Ресурсная роль используется для того, чтобы предоставить пользователю доступ к какому-либо ресурсу. Это может быть сущность, атрибут сущности, сервис API или экраны. По умолчанию, новому пользователю запрещен доступ ко всему.
Роли уровня строк используются для того, чтобы запретить пользователю доступ к тем строкам таблицы, к которому у него есть ресурсный доступ. Это может быть полезно в случае, когда условный менеджер должен иметь доступ на просмотр сотрудников только своего отдела.
Благодаря fullstack архитектуре мы можем сделать так, чтобы ролевая модель охватывала каждый слой приложения, от модели данных до пользовательского интерфейса.
Таким образом единожды созданная роль будет контролировать полный доступ к функциям системы. Например, при загрузке сущности из API для взаимодействия с базой будут убраны отфильтрованные результаты или выброшена ошибка доступа. Если визуальный компонент связан с данными, то к этому визуальному компоненту также будут применены ограничения текущей роли. Если пользователь имеет доступ только на просмотр, поле ввода будет отображено в режиме read-only
. Если пользователь не имеет даже прав на просмотр, то визуальный компонент не будет отображен на экране.
Для создания ролей существуют два способа: программный и ручной. При программном создании роли программист системы создаёт специальный Java-интерфейс, в котором описывает доступы для данной роли. В Jmix Studio существуют специальные дизайнеры, которые имеют информацию о архитектуре приложения и предоставляют возможность выбрать необходимые права для различных частей системы через интерфейс.

Показать код
package com.company.onboarding.security;
import com.company.onboarding.entity.Step;
import com.company.onboarding.entity.User;
import com.company.onboarding.entity.UserStep;
import io.jmix.security.model.EntityAttributePolicyAction;
import io.jmix.security.model.EntityPolicyAction;
import io.jmix.security.role.annotation.EntityAttributePolicy;
import io.jmix.security.role.annotation.EntityPolicy;
import io.jmix.security.role.annotation.ResourceRole;
import io.jmix.securityflowui.role.annotation.MenuPolicy;
import io.jmix.securityflowui.role.annotation.ViewPolicy;
@ResourceRole(name = "Employee", code = "employee", scope = "UI")
public interface EmployeeRole {
@MenuPolicy(menuIds = "MyOnboardingView")
@ViewPolicy(viewIds = "MyOnboardingView")
void screens();
@EntityAttributePolicy(entityClass = User.class,
attributes = "*",
action = EntityAttributePolicyAction.VIEW)
@EntityPolicy(entityClass = User.class,
actions = {EntityPolicyAction.READ, EntityPolicyAction.UPDATE})
void user();
@EntityAttributePolicy(entityClass = UserStep.class,
attributes = "*",
action = EntityAttributePolicyAction.VIEW)
@EntityPolicy(entityClass = UserStep.class,
actions = {EntityPolicyAction.READ, EntityPolicyAction.UPDATE})
void userStep();
@EntityAttributePolicy(entityClass = Step.class,
attributes = "*",
action = EntityAttributePolicyAction.VIEW)
@EntityPolicy(entityClass = Step.class,
actions = EntityPolicyAction.READ)
void step();
}
Помимо этого, если система уже работает на продакшен сервере, то администратор может самостоятельно создать нужную роль и назначить её на пользователя, не обращаясь к программисту. Такие роли будут сохранены базу данных и будут иметь равный приоритет с ролями, созданными на моменте разработки.

Аудит
Для закрытия задачи аудита во фреймворке имеется специальное дополнение Audit Add-on. Это дополнение, как и другие, доступно в маркетплейсе дополнений в Jmix Studio.

При установке аддона в приложение добавляется специальный Spring Boot стартер. Аддоны могут нести как сугубо сервисную функциональность, так и дополнительные элементы интерфейса, включая новые компоненты и системные экраны.
Audit Add-on приносит с собой экраны для создания правил аудита. Так, например, аудит устанавливается на все атрибуты сущности User
.

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

Мы решили вынести эту информацию прямо в UI, чтобы доступ к ней мог быть предоставлен простым пользователям системы, а не только программистам.
Работа с UI
В Jmix основным элементом пользовательского интерфейса являются экраны. Экраны – это UI компоненты Vaadin Flow. Они имеют 2 основных элемента: Java-контроллер и XML-разметку.

XML-разметка опциональная и служит для декларативного описания компонентов и их расположения на экране. Java-контроллер – это основной элемент экрана, в котором описывается бизнес-логика, связанная с UI. В нём можно обработать данные, изменить состояние компонентов в процессе жизненного цикла экранов, а также создать обработчики различных событий.
Используя XML разметку можно скомпоновать макет экрана, добавить все нужные визуальные компонент и связать их с данными. Для каждого компонента есть инспектор который показывает набор свойств, которые можно изменить, а также список хуков, которые можно реализовать.

Чтобы каждый раз не перезапускать приложение при изменении UI существует функция hot-deploy. С её помощью можно изменить экран, бизнес-логику в нём и просто сохранить файл. Действие сохранения будет триггером для подгрузки изменений в работающее приложение.
Автоматическая миграция данных
При обновлении модели данных всегда возникает проблема синхронизации изменений с БД. В Jmix Studio для этого существует механизм миграции, основанный на Liquibase. При изменении модели генерируется changelog и применяется к базе данных. Каждый аддон, который несёт в себе набор системных сущностей также имеет свой подготовленный набор changelog.

Также, для случая, если разработка ведётся от базы, существует встроенная система reverse engineering, которая может сгенерировать модель данных по таблицам базы.
Сравнение с другими вариантами и ограничения
Первым значимым отличием является наличие готовой архитектуры. В Jmix используются проектные шаблоны. От запуска IDE до запуска проекта проходит несколько минут. В случае, когда создаются однотипные админ-панели это может выиграть большое количество времени. Естественно, при таком подходе имеются минусы. Если стандартный шаблон приложения не подходит, то придётся создавать свои шаблоны, а это уже другие времязатраты.
При использовании нативных технологий такой проблемы нет, потому что проект конфигурируется гибко. Однако такая конфигурация может занимать несколько дней работы.
Второе отличие это предоставление фреймворком более высокого уровня абстракции. Имеющиеся API по взаимодействию с БД, декларативное конфигурирование UI компонентов и визуальная конфигурация модели безопасности снижают порог вхождения для разработчиков в необходимые технологии. Эти подходы не запрещают ручного и тонкого конфигурирования необходимых систем. Более того, если что-то во фреймворке работает не так, как того хочет программист, то всегда есть возможность переопределить необходимое поведение. Мы, как разработчики фреймворка, стараемся оставлять точки расширения и переопределения для большинства систем и модулей, что облегчает изменять работу фреймворка под ваши требования.
Если переходить к сравнению с технологиями, на которых основан сам фреймворк, то появляется логичный вопрос: почему просто не использовать эти технологии и зачем нужен фреймворк? Основные моменты описаны в таблице ниже.
Характеристика | Spring Boot + Vaadin Flow | Jmix |
---|---|---|
Архитектура | Гибкая | Монолит с поддержкой многомодульности |
UI | Готовая палитра компонентов, ручная работа с layout и привязкой данных | Готовая палитра компонентов + привязка данных + заготовки модулей |
Система безопасности | Самописная, гибкая | Готовая из коробки, сквозная по проекту |
Источники данных | Любые, ручная настройка | Любые SQL + генерация модели и клиента OpenAPI |
Скорость разработки | Средняя, ручное решение типовых задач | Очень высокая на начальном этапе, высокая в дальнейшем |
Стоимость поддержки | Высокая, зависит от архитектурных решений | Низкая |
Гибкость решения | Очень высокая, можно сделать все, или... почти все :) | Средняя, кастомизация в рамках архитектуры Jmix и Vaadin |
Если использовать, то Jmix можно получить большое количество готового функционала. Однако в обмен на это необходимо принимать правила его архитектуры и ограничения.
Всем известно, что server-side UI требует дополнительных ресурсов сервера. Это происходит из-за того, что при создании сессии на сервере создаётся полноценный экземпляр UI. В случае Jmix нужно закладывать примерно 10 Мб на одну сессию. Кроме того, такой подход накладывает ограничения на возможность кастомизации интерфейса, в отличие от того же React + AntDesign.
Не все привычные Java технологии поддержаны во фреймворке. Например, при использовании нереляционных баз данных придётся самостоятельно описывать DataStore. Кроме того, использование не совсем мейнстримного EclipseLink фреймворка под капотом может отпугнуть.
К тому же, Jmix точно не будет лучшим вариантом, если у вас используется микросервисная архитектура. Дело в том, что из-за server-side UI подхода сеансы пользователей занимают много памяти. Вследствие этого, сериализовать сеанс целиком очень трудно. Поэтому единственный вариант балансировки нагрузки это использовать sticky-sessions.
Итог
В целом, используя только лишь описанный функционал можно написать хорошую админ-панель. Многие проблемы, с которыми сталкивается каждый разработчик решены на уровне фреймворка. Во фреймворке есть готовый UI для написания CRUD: формы, таблицы, фильтры. Набор дополнений решает типовые проблемы: настройку аудита, интеграцию полнотекстового поиска или систему live-уведомлений.
Однако не стоит считать, что фреймворк это своеобразный silver bullet для решения бизнес-задач. Он имеет ряд ограничений, которые нужно иметь в виду в процессе анализа применимости инструмента.
