Sharing entities and service beans between Full Stack and Rest API projects

In Cuba, there was a separate global and core modules which we could share between a Web project and Portal project. This allowed us to build a Vaadin UI front-end or a portal project with Rest controllers to provide APIs to the same set of entities and services and deploy on separate URL or instances. How can we archieve the same thing in Jmix 2 ? We want to have a project that exposes a set of REST APIs without the Vaadin Flow UI but share the same business logic code.

Hello,
would this help

Kind regards,
Mladen

Hi CK,

Take a look at the REST DataStore add-on.

With the upcoming new feature Sharing entities and service interfaces between server and client · Issue #4624 · jmix-framework/jmix it will offer functionality similar to CUBA’s client/middleware tiers separation.

Regards,
Konstantin

Hi. Instead of “splitting” each project into sub-modules (UI, Services, etc), can we have the packager pick up the whole add-on (UI + services) but exclude views and the flow-ui libraries when adding to a REST Service application ?

Perhaps you can instead develop 2 add-ons for each “functional module”: backend and UI. The latter depends on the former.

Then in a full-stack application you will include both, and in a REST-only app you will include only the backend.

BTW this is how most of the Jmix standard add-ons are built.

Konstantin,

For the “backend” functional module, what type of project should it be ? In the new Jmix Project options, there is only Java Add-on and Kotlin Add-on. Which type should I pick if I only want the Entities and Services without any (flow) UI components that will be included ?

Also, in the backend module, if I need to send a Global UI event or use the UiEventPublisher to trigger a UI event in the case where I want to tell the user something has happened in the backend on the screen, will it require the UI components/libraries ?

Regards
CK

Create a Java/Kotlin add-on project and just remove the UI libraries from build.gradle and UI configuration code from the Spring configuration class:

archivesBaseName = 'sample-backend'

dependencies {
    implementation 'io.jmix.core:jmix-core-starter'
    implementation 'io.jmix.data:jmix-eclipselink-starter'
//    implementation 'io.jmix.flowui:jmix-flowui-starter'
//    implementation 'io.jmix.flowui:jmix-flowui-themes'

    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'org.springframework.boot:spring-boot-starter-web'

    testRuntimeOnly 'org.hsqldb:hsqldb'
}

//configurations.implementation {
//    exclude group: 'com.vaadin', module: 'hilla'
//    exclude group: 'com.vaadin', module: 'hilla-dev'
//    exclude group: 'com.vaadin', module: 'copilot'
//}

test {
    useJUnitPlatform()
}
package com.company.samplebackend;

import io.jmix.core.annotation.JmixModule;
import io.jmix.core.impl.scanning.AnnotationScanMetadataReaderFactory;
import io.jmix.eclipselink.EclipselinkConfiguration;
//import io.jmix.flowui.FlowuiConfiguration;
//import io.jmix.flowui.sys.ActionsConfiguration;
//import io.jmix.flowui.sys.ViewControllersConfiguration;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import java.util.Collections;

@Configuration
@ComponentScan
@ConfigurationPropertiesScan
@JmixModule(dependsOn = {EclipselinkConfiguration.class/*, FlowuiConfiguration.class*/})
@PropertySource(name = "com.company.samplebackend", value = "classpath:/com/company/samplebackend/module.properties")
public class SmplbckConfiguration {

//    @Bean("smplbck_SmplbckViewControllers")
//    public ViewControllersConfiguration screens(final ApplicationContext applicationContext,
//                                                final AnnotationScanMetadataReaderFactory metadataReaderFactory) {
//        final ViewControllersConfiguration viewControllers
//                = new ViewControllersConfiguration(applicationContext, metadataReaderFactory);
//        viewControllers.setBasePackages(Collections.singletonList("com.company.samplebackend"));
//        return viewControllers;
//    }
//
//    @Bean("smplbck_SmplbckActions")
//    public ActionsConfiguration actions(final ApplicationContext applicationContext,
//                                        final AnnotationScanMetadataReaderFactory metadataReaderFactory) {
//        final ActionsConfiguration actions
//                = new ActionsConfiguration(applicationContext, metadataReaderFactory);
//        actions.setBasePackages(Collections.singletonList("com.company.samplebackend"));
//        return actions;
//    }
}

Send a regular ApplicationEvent in the backend module. Listen to it in the UI module and re-send using UiEventPublisher. To avoid the loop in the listener, check the event source and ignore if is this.

Regards,
Konstantin

Thank Konstantin.

For the backend module, I have removed the other add-on libraries which has “flowui” in the package name e.g. “io.jmix.reports:jmix-reports-flowui-starter” in the gradle file and this works.

And for the UI module, I have kept the “flowui” libraries and removed the other starter packages and kept the *flowui-starter only in the cradle file. Is this correct ?

I am having trouble login in via the UI with this configuration, it complains that there is something wrong with my User table:
Exception Description: Problem compiling [select e from synergy$user e where e.username = :username].
[14, 26] The abstract schema type ‘synergy$user’ is unknown.
[35, 45] The state field path ‘e.username’ cannot be resolved to a valid type.

In the UI module, you can keep all dependencies as is, unless you are going to use it without backend (e.g. via REST DataStore).

The error you are mentioning is usually caused by the wrong configuration of custom modules. Check out this doc: Creating Add-ons :: Jmix Documentation. Pay attention to the application log output starting with Using Jmix modules:.

If the problem persists, attach all build.gradle, main Java configuration files with @JmixModule and log output here.