All articles

Stir, Pour, Voilà: It Is Production-Ready, or How to Create an Admin Interface Quickly

We often hear at conferences and other events how different development teams struggle with the problem of having to develop an admin interface quickly and efficiently. Some of them go for expensive custom solutions they develop all by themselves; others try looking for ready-made alternatives on the market that would fit all their business requirements. Certain hybrids of the two approaches are not unheard of. The problems they thus solve might look familiar to our team members, since we have had to deal with them too. We are lucky though to have a tool that offers a natural solution to the problem, a tool we have built ourselves. So, in this article, we would like to demonstrate how to solve typical problems while creating an admin interface in Jmix and to show the benefits and the limitations of this approach.

Typical admin interface requirements

First of all, we must determine the requirements that the admin interface should cover.

  • Data access in the database and the possibility to edit them. It is related to entity creation, DTO and CRUD screens. The entity number can be quite large, so the task of optimizing this process will be of a high priority. Apart from the data model and screen creation process optimization, you have to take into account the time spent writing migration scripts, mappers and other infrastructure elements, since these tasks are also resource intensive.
  • Differentiation of access rights. Role model creation is critically important, since an admin interface gives access to sensitive data and to system functions.
  • Audit. In this context, audit means tracking changes in the data model. It is necessary both for the support engineer work, to sort out a problematic situation, and for incident investigations and unauthorized action detection.
  • Granting access to functions and data for external services. Usually it means implementing various REST endpoints for CRUD actions with data and for interaction with internal services. When the number of entities is significant, the routine creation of CRUD endpoints can take a lot of time.

What is Jmix?

Framework architecture

Jmix Framework is a full stack open-source framework for creating web applications, which is based on Spring Boot and Vaadin Flow. The main goal of the framework is to automate the routine actions for the developer, while preserving the development process flexibility and the easy access to the familiar tooling.

A Jmix application is a Spring Boot application with a set of Jmix starters. You can also include any other starters you are familiar with. To work with data, Jmix uses a unified data model based on the EclipseLink ORM framework.

The Vaadin Flow SSR web framework is responsible for working with the client side. It is based on the Web Components specification and the Google Polymer library suite. In Vaadin Flow, visual components consist of the client-side and the server-side parts. The client-side component code is written in JavaScript and TypeScript, white the server-side components are written in Java. They communicate with each other via AJAX requests. Therefore, Jmix Framework used the same language to write both the backend and the frontend parts of the application.

In order to work with Jmix Framework, a special Jmix Studio plugin suitable for IntelliJ IDEA, OpenIDE, and GigaIDE is used. Studio offers basic tools for working with the framework components, such as screens, roles, or entities.

Note that Jmix doesn't position itself as a Low Code platform, since writing up the business logic is necessary for creating a fully-functional application, but certain Low Code elements are present in the form of various designers. In this case, a Less Code platform with auxiliary code generation components would be a more accurate definition.

How Jmix solves the admin interface problems

CRUD generation and visual designers

As we have already specified while discussing the requirements, the highest priority task of the admin interface creation process is the creation of a volumetric data model and the CRUD interfaces to work with it.

For this purpose, Jmix Studio offers various visual designers to work with the most basic application components. One of those is the entity designer. In an entity designer, you can specify connections between entities, define attributes and set constraints for them. Visual designers just modify what is represented in the application source code. This code can also be modified manually without involving the visual designer. The alterations written into the code will be reflected in the designer and vice versa.

@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 

The designer offers us an opportunity to familiarize ourselves with the available settings for the entity and its attributes. The elements responsible for entity index handling are also here.

When the data model is ready, we can start generating the CRUD screens. A special generator exists for this purpose, which can be called via a dialog window.

This dialog window is a screen creation wizard, in which you can select thet generation template and fill it in with the necessary parameters. You don't have to use the template; it's okay to create a custom layout from scratch. There is a blank template for this purpose, which only contains the screen's skeleton.

The View & Edit screen generator analyzes the entity's metamodel in order to create the necessary visual components for working with entities. Besides, for the View screen a table will be generated containing the selected columns corresponding to the entity attributes.

For example, the following model represents a component generation example based on the entity attribute type information:


Attribute Type Component to edit
username String TextField
password String PasswordField
email String EmailField
active Boolean Checkbox

The generated components will be bound with the data. If you edit a component via its bound attribute and then click the Save button, a commit to the database will take place.

The screens that have been created can be modernized, and their own business logic can be implemented inside them.

If you need not just the data from the SQL-based database, there is an option to create a DTO for the entity and then work with it in the same way you work with the JPA entities themselves. However, it will be necessary to configure a service, which is going to receive this data from an external source. If you don't feel like configuring something, you can put the OpenAPI's generator schema into Studio to generate the data model and the client.

Talking about generators, AI assistants are used more and more often, for example, for UI, REST interface, or entity structure generation. It saves a lot of time, but such an approach to generation might not take into account the project specifics or business context. You have to be careful and work according to the "trust but check" principle. If you don't thoroughly proofread your generated code, you can get serious problems with its maintenance.

This is why Jmix emphasizes generators. But if the task still remains a routine one, while outside of the generators' scope, you can use the built-in AI Assistant panel in Jmix Studio. Pre-trained for specifics, the Jmix model understands the context and is capable of generating more complex code. But the same rule about checking the resulting code still applies, since any AI model can hallucinate.

Role model

Jmix has a built-in control system for access, based on Spring Security. A role is the central component of this system. There are two types of roles in the framework: resource roles and row-level roles.

  • Resource roles are used to grant the user access to a resource. It can be an entity, a service API, or screens. By default, a new user has no access to anything.
  • Row-level roles are used to forbid a user access to those table rows, for which he or she already has resource-level access. It can be useful for such cases as, for example, a manager who should have access to viewing employees from the same department only.

Thanks to the full stack architecture, we can set it up so that the role model will cover every layer of the application, from the data model to the user interface.

This way, a role, once created, will control all access to the system's functions. For example, while loading entities from the API to let them interact with the database, filtered results will be removed or an access error thrown. If a visual component is related to some data, the limitations associated with the current role will be applied to this visual component, as well. If the user has only viewing rights, the input field will be seen as read-only. If the user has no rights even to view this information, the visual component won't be displayed on the screen at all.

There are two ways of creating a role: a programmatic and a manual one. When the role is created programmatically, the developer creates a special Java interface that describes access rights for the role. Jmix Studio contains special designers that receive information about the application's architecture and make it possible to choose the necessary rights for various parts of the application via the interface.

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(); 
} 

Apart from this, if the system works on the production server, the administrator can create the necessary role and assign a user to it without contacting the developer. Such roles will be stored in the database and have the same priority as the roles created at the development stage.

Audit

To close the audit tasks, the framework has a special Audit add-on. This add-on, like many others, is accessible through the add-on marketplace in Jmix Studio.

When you install the add-on into the application, a special Spring Boot starter is added. Add-ons can have purely service functionality, or they can contain additional interface elements, including new components and system screens.

Audit add-on brings with it screens for audit rule creation. For example, all attributes of the User entity get audited.

The screen next to it contains audit records, which can be used for change tracking.

We have decided to place this information directly into the UI, so that the system users could be granted access to it, and not just the programmers.

Working with the UI

The main element of the user interface in Jmix are the screens. The screens are UI components from Vaadin Flow. They have two main elements: the Java controller and the XML layout.

The XML layout is optional and is used for the component declarative description and their placement on the screen. The Java controller is the main screen element that describes the business logic connected with the UI. That is where you process the data, change component states throughout the screen lifecycle, or create event handlers for various events.

Using XML layout, you can compose the screen template, add all the necessary visual components and connect them with the data. For each component, there is an inspector that shows the properties that can be modified, and the list of hooks, which can be implemented.

To avoid having to restart the application each time you change something in the UI, the hot-deploy feature exists. With its help, you can modify the screen, its business logic and just save the file. The saving action will trigger the loading of all modifications into the running application.

Automated data migration

While updating the data model, the problem of synchronizing the changes with the database always arises. Jmix Studio has a migration mechanism for this, which is based on Liquibase. Each time the model is modified, a changelog is generated and applied to the database. Each add-on that carries with it a set of system entities, also has its own pre-prepared set of changelogs.

Besides, if the development is done "from the database", a built-in reverse engineering system exists, which can generate the data model from the database tables.

Comparison with other options and limitations

The first notable difference is the presence of ready-made architecture. Jmix uses project templates. From the moment you start the IDE to the moment you run the project only a few minutes pass. In the case when you have to create several similar admin panels it can save you a lot of time. Naturally, this approach has its drawbacks. If a standard application template doesn't match your needs, you will have to create your own template, and that means more time spent.

When you are using native technologies, there is no such problem, since the project is flexibly configured. However, such a configuration can require a few days of work.

The second difference is the fact that the framework offers a higher level of abstraction. Existing APIs that provide database interactions, declarative configuring os UI components and visual configuration for the security model lower the entry threshold for developers for using the necessary technologies. These approaches do not forbid manual configuring and fine-tuning for the necessary systems. Moreover, if something in the framework works differently from the programmer's wishes, there is always a possibility to override the necessary behavior. We, as the framework creators, always do our best to leave extension points and override points for the majority of systems and modules, which makes it easier to change the framework behavior to match your needs.

Moving forward to the comparison with technologies the framework is based on, a logical question arises: why not use these technologies as they are and what is the framework for? The main points are described in the table below.


Characteristic Spring Boot + Vaadin Flow Jmix
Architecture Flexible Monolith with multi-modularity support
UI Ready component palette, manual layout work and manual data binding Ready component palette + data binding + module stubs.
Security system Self-developed, flexible Ready out of the box, end-to-end within the project
Data sources Any, manual setup Any SQL-based + model generation and OpenAPI client generation
Development speed Average, typical tasks solved manually Very high at the start, high later
Support costs High, depend on architecture solutions Low
Solution flexibility Very high, you can do everything… or almost everything:) Average, customization possible within the Jmix and Vaadin architecture

If you use Jmix, you can get a large amount of ready-made functionality. However, as a trade-off, you have to accept the rules and restrictions pertaining to its architecture.

We all know that Server-side UI requires additional resources from the server. It happens because when we create a session at the server side, a full-scale UI instance is also created. In case of Jmix, we need to allocate approximately 10 Mb of memory per session. Besides, this approach imposes limitations on the interface customization capabilities, unlike when working with React + AntDesign.

Not all the familiar Java technologies are supported by the framework. For example, if you have to use non-relational databases, you will have to describe the DataStore on your own. Besides, using the not-so-mainstream EclipseLink framework under the hood can scare someone away.

What's more, Jmix definitely won't be the best option, of you use the microservice architecture. The reason for it is that because of the server-side UI approach user sessions consume a lot of memory. Consequently, it is very difficult to serialize the entire session. Therefore, the only option for load balancing is to use sticky sessions.

Summary

On the whole, if you use only the functionality described above, you can create a good admin panel. Many of the problems well known to every developer are solved at the framework level. The framework contains a ready-made UI for CRUD development: forms, tables, and filters. A suite of add-ons solves typical problems such as audit setup, full-text search integration, or live notification system.

However, you shouldn't think that the framework is a kind of silver bullet for solving your business tasks. It has a number of limitations, which should be taken into account while analyzing the applicability of the tool.

Jmix is an open-source platform for building enterprise applications in Java