Best architecture for multiple front-ends (e.g., Admin vs. Client) sharing one backend?

Hi everyone,

I’m trying to find the best architectural solution for a Jmix application where I need to have a single backend (data, entities, services) but two (or more) different front-end “entry points”.

My goal is to have a setup like this:

  1. office.myapp (The Admin/Back-office App)

    • This would be the “main” Jmix application.
    • It defines the full data model, all entities, and includes all the standard Jmix UI screens for managing the entire system.
    • Users here are administrators or back-office staff ad see all tentants data.
  2. client.myapp (The Client-facing App)

    • This would be a separate front-end application, running on a different port or URL.
    • It’s intended for external users (clients/tenants).
    • Users logging in here should only see a very specific, limited set of screens and be able to perform only a few actions (e.g., input data, update entities ecc) on a subset of the entities.

My key requirement is to physically separate these front-ends. I want to avoid having one single, monolithic UI where I have to manage complex role-based logic to hide/show 90% of the application for client users. I’d prefer two distinct applications that share the same backend logic and database, if is it possible

I’ve been considering two potential approaches and would love your advice on the “Jmix-idiomatic” way to do this:

Option 1: The Composite Project Approach
Is using a composite project the right way to go?
For example, I could have:

  • A core module (add-on) with all entities and services.
  • An office-ui module (add-on) with the full admin UI.
  • A client-ui module (add-on) with the limited client-facing UI.
    Would this allow me to deploy two different runnable “apps” (office and client) that both depend on the core module? i don’t find best practice to configure this solution in a good way, i don’t know if there is documentation about this use of composite projects

Option 2: Main App + REST API Approach
Is the recommended solution to build office.myapp as a standard, complete Jmix application, and then build client.myapp as a completely separate application (e.g., a simple Jmix app with its own UI) that consumes the data from the main app via its RestDatasource?

I feel like Option 2 might be cleaner, but I’m not sure if it’s the most efficient way or how data sharing works best in that scenario (especially if the client app is also a Jmix app).

What is the best practice for achieving this “split front-end” architecture?

Thanks in advance for any guidance!

Hello,

https://docs.jmix.io/jmix/separate-tiers-guide/index.html
https://docs.jmix.io/jmix/rest-ds/index.html

The architecture depends on many factors, some are:

  • number of users
  • need for future scalability
  • number of database tables and rows
  • security considerations
  • performance
  • existing systems

You can go several ways:

  • can build office app, then copy paste it and gut it to have client-only interface, make 2 jmix applications with their own databases, have a external datastore to read data from each other, and it would work

  • but you are aware of making the maintenance easier so you consider Option 1 - according to your specifications there would be one “core” application without UI (possible) and then you would upgrade it with add-on to have desired effect - they would look at the same database , remember that in order for jmix to work, it needs admin rights to its database, so you would use Jmix security with roles, which is quite good - but user must not be exposed without control to portions of UI that have unconstrained datamanager or entity manager, or can write sql, jpql, bpel, groovy and such

  • You need to make an experiment with this and see if this fits you, have a few tables, construct entities, make the composite project - I’m not using it myself, but I know people do

Option 2 seems better to me, call me old-fashioned, but I like separation

  • but be aware that REST call to access data is 3-17x slower in average than talking directly to the database (so if you have a gargantuan amount of data, make a shared database)
  • The advantage of REST is that you can then connect other things to the services and business logic you have - maybe some future mobile app
  • This doesn’t prevent you from having a core app without UI (or with admin UI) with data and logic, an office app, and a client app, like in Option 1
  • With this, you don’t need to give access to the core module source to other developers, just the REST API specifications
  • be aware to know how to secure these calls, use repositories …

I also have a requirement to develop the customer-facing app soon, so Option 2 is the way I will likely go - there could be some “modules” that another ecosystem’s developers can do by calling REST services

Kind regards,
Mladen

Based on your requirements, I would go for Option 1. You will get 2 modular monoliths sharing some core model and logic, and connecting to the same database. Both are standard Jmix full-stack apps, so they can include any Jmix add-ons and all Jmix features will work out-of-the-box.

You can create just three modules:

  • Core add-on (using Add-On template)
  • Main app (using Full-stack App template), depends on core add-on
  • Client app (using Full-stack App template), depends on core add-on

If both apps are developed by a single team, you can do it in a single composite project, so the friction for modularization will be minimal. The modules can also be moved to separate projects when needed.

This approach is definitely simpler and more “straightforward Jmix” than having a UI-only Client app connecting to the Main app through the REST DataStore. In general, I think that UI-only apps (as in Separating Application Tiers :: Jmix Documentation guide) should be considered only if you have a strong motivation to have no connection to the database for these apps.

The limitation of separate apps connecting to the same database is the inability to synchronize in-memory state for shared features like entity/query caches, pessimistic locking, notifications, etc. Basically all features described in Cluster Communication :: Jmix Documentation should be used with care.

Regards,
Konstantin

1 Like

Thanks a lot for your answers — they have been extremely helpful.
Based on your suggestions, I’ve decided to go with Option 1 (modular monolith): a core add-on shared by both applications, plus two separate full-stack Jmix apps (Office and Client).
Later on, if needed, I may open the door to Option 2 (REST/API layer) for mobile clients or external integrations.

Before I proceed, I’d like to clarify a few points and get some confirmation about the recommended structure and setup.

Planned module structure

Core Add-on

  • All entities (including User, Tenant, and security-related entities)
  • All services and business logic
  • Liquibase changelogs
  • Possibly shared roles
  • Shared UI fragments if needed

Office App

  • Screens, controllers and services specific to the back-office
  • Multitenancy add-on
  • Reports add-on
  • Administrative user management
  • Full data visibility across tenants (for back-office users)

Client App

  • Screens, controllers and services specific to external client users
  • Multitenancy add-on
  • Reports add-on
  • Restricted roles and client-facing operations (data input, simple workflows such as Kanban views, etc.)
  • Access limited to a single tenant

One question here:
Would you recommend keeping User management only in the core, or is it acceptable to extend/customize users also inside the client app? I’d prefer to centralize users/tenants in the core to avoid divergence, but I’d like to confirm this is the idiomatic Jmix approach.

Liquibase and database ownership

Since both applications will connect to the same database, I want to avoid any risk of schema drift or accidental modification from the Client App.

Is there any best practice or documentation on how to structure:

  • Liquibase changelogs only in the core (so both apps share exactly the same database model)
  • Module-specific tables (screens, UI state, app-level configuration) that belong only to the Office or Client app
  • Whether it’s recommended to disable Liquibase in the Client App entirely
    (e.g., jmix.liquibase.enabled=false) to prevent accidental DDL operations
  • Whether I should restrict the DB permissions for the Client App user (DML only, no DDL)

Any guidance or references for this kind of “core + multiple apps” setup with Liquibase would be great.

Functional goal

The final architecture I’m aiming at is:

  • A multitenant system managed entirely from the Office App (tenant setup, user setup, global configuration, etc.)
  • A strictly isolated Client App where tenant users can only:
    • work inside their own tenant
    • perform limited data input
    • access restricted dashboards / Kanban-like workflows
    • generate some reports
    • never access Office App screens or logic

As soon as I have the architecture working, I’ll update this thread with the final structure and some feedback on how it went.

Thanks again for your help — your insights have been invaluable.

It depends on your requirements. Looks like the proper solution for you is to place the User entity and DatabaseUserRepository in the core add-on to provide login functionality for both applications, but user management views should be located in the Office application.

See this project for an example of implementing the users addon: GitHub - jmix-projects/sample-composite-project-2

The general approach is to generate and keep Liquibase changelogs for entities in modules that contain those entities. So if the Core add-on provides the data model, it should contain also changelogs for creating the database schema.

See how to manage data stores and changelogs in composite projects at https://docs.jmix.io/jmix/studio/composite-projects.html#managing-data-stores and in the aforementioned sample project.

The alternative approach for you is to generate Liquibase changelogs for the whole model in the Office application. Studio will do this automatically if it doesn’t find tables for the entities from add-ons.

You can do it by specifying this application property:

main.liquibase.enabled=false

Regards,
Konstantin