Micro Frontends for Java Microservices
Tags: java, microservices, microfrontends, jhipster
Published: 2023-01-20 15:47:47

---
title: Micro Frontends for Java Microservices
published: true
description: This tutorial explains what micro frontends are and how you can use them with Java microservices.
tags: java, microservices, microfrontends, jhipster
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uzb4z91zxice4ik3dqee.jpg
canonical_url: https://auth0.com/blog/micro-frontends-for-java-microservices/
---

Microservices have been quite popular in the Java ecosystem ever since Spring Boot and Spring Cloud made them easy to build and deploy. Things have gotten even easier in recent years with the proliferation of new Java frameworks built specifically for microservices: MicroProfile, Micronaut, Quarkus, and Helidon. Not only do these frameworks provide an excellent developer experience, but they also tend to have built-in Docker support. They even work with GraalVM, so you can compile your apps to native code!

_To be fair, MicroProfile isn't a framework; it's a spec with many implementations. In fact, both Quarkus and Helidon have MicroProfile flavors you can use._

Today, I'll show you how to build a Java microservices architecture that leverages micro frontends for the UI. The backend will use Spring Boot and Spring Cloud, while the frontend will use React. You can also use Angular or Vue if you'd like.

This tutorial is also available [as a screencast](https://youtu.be/haTQ1xJKQQ8).

{% youtube haTQ1xJKQQ8 %}

NOTE: This video uses JHipster 7.9.3. All the steps will still work with JHipster 8.1.0, but you have to use Java 17 and Node 18.

**Prerequisites:**

- [Java](https://sdkman.io/) 17+
- [Node.js](https://nodejs.com/) 18+
- [Docker Desktop](https://www.docker.com/products/docker-desktop/)
- [JHipster](https://www.jhipster.tech/installation/) 8.1.0

You can install JHipster with npm:

```shell
npm i -g generator-jhipster@8
```

## Java microservices with Spring Boot and JHipster

JHipster is an application generator that creates a Spring Boot backend. You can configure it to use SQL or NoSQL databases, plain ol' Spring MVC, or reactive with WebFlux. It also generates a UI for your REST API and offers you the choice of Angular, React, or Vue. Last year, I showed you how to build [reactive Java microservices with Spring Boot and JHipster](https://developer.okta.com/blog/2021/01/20/reactive-java-microservices). In this tutorial, you'll build a gateway with Spring Cloud Gateway. Then, you create a blog microservice and a store microservice, each with its own database.

![JHipster microservices architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ioi1fkmrcid5wujypo2v.png)

This microservices architecture had one major flaw: the UI for it is a monolith, with all its files on the gateway. This isn't good from a loose-coupling point of view because changes in a microservice might require changes in the UI. Instead of being able to deploy the microservice independently, you have to deploy the gateway too.

Today, I'm proud to show you how you can solve this problem with micro frontends. JHipster recently added support for micro-frontends. Microfrontends provide a way for you to remotely load and execute code at runtime so your microservice's UI can live in the same artifact without being coupled to the gateway!

![JHipster microfrontends architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rcwgh7ad81rs8dfz3vkl.png)

In the previous paragraph, you might notice I spelled micro frontends three different ways. The current literature is all over the place on this one!

{% embed https://twitter.com/mraible/status/1499098253920460802 %}

I'm going to use "micro frontends" for the remainder of this post since that's what [Cam Jackson](https://twitter.com/thecamjackson) used in his [Micro Frontends article](https://martinfowler.com/articles/micro-frontends.html) on Martin Fowler's blog.

## A quick introduction to Module Federation

Webpack's [Module Federation](https://webpack.js.org/concepts/module-federation/) is one of the best-known implementations for micro frontends. Its [`ModuleFederationPlugin`](https://webpack.js.org/plugins/module-federation-plugin) allows a build to provide or consume modules with other independent builds at runtime. It even allows you to share libraries between frontends to reduce the size of remote bundles.

[Zack Jackson](https://twitter.com/ScriptedAlchemy) is the creator of Module Federation and recently [collaborated](https://twitter.com/ScriptedAlchemy/status/1564411584851505153) with [Manfred Steyer](https://twitter.com/ManfredSteyer) to create Native Federation. This means you can use micro frontend concepts with any build tool, not just webpack.

{% embed https://twitter.com/ManfredSteyer/status/1564312149580582912 %}

## Why should Java developers care?

I think micro frontends are a fascinating architectural concept. Microservices weave everything together on the backend with protocols like HTTP and gRPC. With micro frontends, it's all HTTP. You can see your app get stitched together by watching your browser's network console and seeing remote modules load.

I've encountered quite a few monolith UIs in my time as a consultant. The backend was a beautiful microservice architecture, but it was all tightly coupled on the frontend. There's a good chance many Java developers don't care about the UI because they just work on the beautiful backends. However, if you consider yourself a Java _web_ developer, micro frontends are as revolutionary as HTML5!

And that's the beauty of this tutorial; you don't have to write any micro frontends. JHipster can create them for you!

## Micro frontends in action with JHipster

I used a JDL (JHipster Domain Language) file named `reactive-ms.jdl` to create the reactive Java microservices tutorial I mentioned earlier. You can see this file online in the [JDL samples repository](https://github.com/jhipster/jdl-samples/blob/main/reactive-ms.jdl).

I copied this file to `reactive-mf.jdl` and changed a few things for this tutorial:

1. Enabled micro frontends by adding `microfrontends [blog, store]` to the gateway's definition.
2. Changed the client framework from Vue to React.
3. Added `clientFramework react` and Cypress to each microservice.
4. Changed the service discovery to [Consul](https://www.consul.io/), since this is the default in JHipster 8.
5. Added a deployment section for Kubernetes.

**CAUTION**: JHipster's JDL allows you to specify different client frameworks, but micro frontends will only work if you set the same one for all your apps.

Run the following command to create a new directory for your micro frontends project:

```shell
take micro-frontends-jhipster
# mkdir micro-frontends-jhipster && cd micro-frontends-jhipster if you don't have take installed
```

Download both the `-ms` and `-mf` JDLs and compare them in IntelliJ IDEA. You'll need IDEA's [Command-line Launcher](https://www.jetbrains.com/help/idea/working-with-the-ide-features-from-command-line.html) for the `idea` command to work.

```shell
jhipster download reactive-ms.jdl
jhipster download reactive-mf.jdl
idea diff reactive-ms.jdl reactive-mf.jdl
```

![Reactive microservices refactored to micro frontends](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zwza4yl2q8w4vc8s1wni.png)

![Additional K8s deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ojxazc8v25gx1jbx60xm.png)

**TIP**: The [JHipster JDL Plugin](https://plugins.jetbrains.com/plugin/19697-jhipster-jdl) is a handy tool for working with JDL files.

### Micro frontend options: Angular, React, and Vue

JHipster has support for the big three JavaScript frameworks: Angular, React, and Vue. All are implemented using TypeScript, and a newly generated app should have around 70% code coverage, both on the backend and frontend.

There is also a [Svelte blueprint](https://github.com/jhipster/generator-jhipster-svelte), but it does not support micro frontends at the time of this writing.

### Build Java microservices with Spring Boot and WebFlux

To generate a microservices architecture with micro frontend support, run the following command:

```shell
jhipster jdl reactive-mf.jdl --monorepository --workspaces
```

The last two arguments are optional, but I expect you to use them for this tutorial. Without the `monorepository` flag, the gateway and microservices would have their own Git repos. The `workspaces` flag enables [npm workspaces](https://docs.npmjs.com/cli/v8/using-npm/workspaces), which are kinda like having an aggregator `pom.xml` that allows you to execute commands across projects. It also makes it so there's only one `node_modules` in the root directory. To learn more, I recommend egghead's [Introduction to Monorepos with NPM Workspaces](https://egghead.io/courses/introduction-to-monorepos-with-npm-workspaces-c03f500b).

If you want to use Angular, append `--client-framework angular` to the command above to override the JDL value:

```shell
--client-framework angular
```

If you'd rather try out Vue, use the following:

```shell
--client-framework vue
```

### Run your reactive Spring Boot microservices

When the process is complete, cd into the `gateway` directory and start Consul, Keycloak, and PostgreSQL using Docker Compose.

```shell
cd gateway
docker compose -f src/main/docker/consul.yml up -d
docker compose -f src/main/docker/keycloak.yml up -d
docker compose -f src/main/docker/postgresql.yml up -d
```

Then, run `./gradlew` (or `npm run app:start` if you prefer npm commands). When the startup process completes, open your favorite browser to `http://localhost:8080`, and log in with the credentials displayed on the page.

You'll be redirected back to the gateway, but the **Entities** menu won't have any links because the micro frontends it tries to load are unavailable.

![The gateway's entities are unavailable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ewtgycilhzwfp1utg3vl.png)

Start the `blog` by opening a terminal and navigating to its directory. Then, start its database with Docker and the app with Gradle.

```shell
npm run docker:db:up
./gradlew
```

Open a new terminal and do the same for the `store` microservice.

You can verify everything is started using Consul at `http://localhost:8500`.

![Consul services](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/es5bt0tky3pt644xkom6.png)

Refresh the gateway app; you should see menu items to navigate to the microservices now.

![Gateway entities available](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afhai7815q2tyga2a0nl.png)

### Zero turnaround development that sparks joy

At this point, I've only shown you how to run the Spring Boot backends with their packaged React apps. What if you want to work on the UI and have zero turnaround that sparks joy? ✨🤗

In the gateway app, run `npm start`. This command will run the UI on a web server, open a browser window to `http://localhost:9000`, and use [Browsersync](https://browsersync.io/) to keep your browser in sync with your code.

Modify the code in `gateway/src/main/webapp/app/modules/home/home.tsx` to make a quick change. For example, add the following HTML below the `<h1>`.

```html
<h2 className="text-primary">
  Hi, I'm a quick edit!
</h2>
```

You'll see this change immediately appear within your browser.

![Gateway quick edit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfto5k80mfplisndi3of.png)

Remove it, and it'll disappear right away too.

Now, open another terminal and navigate into the `store` directory. Run `npm start`, and you'll have a similar zero-turnaround experience when modifying files in the `store` app. The app will start a webserver on `http://localhost:9002`, and there will only be one menu item for product. Modify files in the `store/src/main/webapp/app/entities/store/product` directory, and you'll see the changes in your browser immediately. For example, change the wrapper `<div>` in `product.tsx` to have a background color:

```html
<div className="bg-info">
```

The UI will change before you can <kbd>Command+Tab</kbd> back to your browser.

![Store edit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5026sm9ff5xepnxc5xq3.png)

The backend has quick turnaround abilities too, thanks to [Spring Boot devtools](https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.devtools). If you modify a backend class, recompiling it will cause Spring Boot to reload your component lickety-split. It's pretty slick!

### A look under the hood of micro frontends

When you're learning concepts like micro frontends, it's often helpful to look at the code that makes things work.

The gateway's `webpack.microfrontend.js` handles specifying the shared dependencies and components between apps. The `src/main/webapp/app/shared/layout/menus/entities.tsx` file contains the menu items for each micro frontend.

```js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

const packageJson = require('../package.json');
// Microfrontend api, should match across gateway and microservices.
const apiVersion = '0.0.1';

const sharedDefaults = { singleton: true, strictVersion: true, requiredVersion: apiVersion };
const shareMappings = (...mappings) => Object.fromEntries(mappings.map(map => [map, { ...sharedDefaults, version: apiVersion }]));

const shareDependencies = ({ skipList = [] } = {}) =>
  Object.fromEntries(
    Object.entries(packageJson.dependencies)
      .filter(([dependency]) => !skipList.includes(dependency))
      .map(([dependency, version]) => [dependency, { ...sharedDefaults, version, requiredVersion: version }]),
  );

module.exports = ({ serve }) => {
  return {
    optimization: {
      moduleIds: 'named',
      chunkIds: 'named',
      runtimeChunk: false,
    },

    plugins: [
      new ModuleFederationPlugin({
        shareScope: 'default',
        shared: {
          ...shareDependencies(),
          ...shareMappings(
            'app/config/constants',
            'app/config/store',
            'app/shared/error/error-boundary-routes',
            'app/shared/layout/menus/menu-components',
            'app/shared/layout/menus/menu-item',
            'app/shared/reducers',
            'app/shared/reducers/locale',
            'app/shared/reducers/reducer.utils',
            'app/shared/util/date-utils',
            'app/shared/util/entity-utils',
          ),
        },
      }),
    ],
    output: {
      publicPath: 'auto',
    },
  };
};
```

The blog's `webpack.microfrontend.js` looks similar, except that it exposes its `remoteEntry.js`, menu items, and routes.

```js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const { DefinePlugin } = require('webpack');

const packageJson = require('../package.json');
// Microfrontend api, should match across gateway and microservices.
const apiVersion = '0.0.1';

const sharedDefaults = { singleton: true, strictVersion: true, requiredVersion: apiVersion };
const shareMappings = (...mappings) => Object.fromEntries(mappings.map(map => [map, { ...sharedDefaults, version: apiVersion }]));

const shareDependencies = ({ skipList = [] } = {}) =>
  Object.fromEntries(
    Object.entries(packageJson.dependencies)
      .filter(([dependency]) => !skipList.includes(dependency))
      .map(([dependency, version]) => [dependency, { ...sharedDefaults, version, requiredVersion: version }]),
  );

module.exports = ({ serve }) => {
  return {
    optimization: {
      moduleIds: 'named',
      chunkIds: 'named',
      runtimeChunk: false,
    },

    plugins: [
      new ModuleFederationPlugin({
        name: 'blog',
        filename: 'remoteEntry.js',
        shareScope: 'default',
        exposes: {
          './entities-menu': './src/main/webapp/app/entities/menu',
          './entities-routes': './src/main/webapp/app/entities/routes',
        },
        shared: {
          ...shareDependencies(),
          ...shareMappings(
            'app/config/constants',
            'app/config/store',
            'app/shared/error/error-boundary-routes',
            'app/shared/layout/menus/menu-components',
            'app/shared/layout/menus/menu-item',
            'app/shared/reducers',
            'app/shared/reducers/locale',
            'app/shared/reducers/reducer.utils',
            'app/shared/util/date-utils',
            'app/shared/util/entity-utils',
          ),
        },
      }),
      new DefinePlugin({
        BLOG_I18N_RESOURCES_PREFIX: JSON.stringify(''),
      }),
    ],
    output: {
      publicPath: 'auto',
    },
  };
};
```

## Build and run with Docker

To build Docker images for each application, run the following command from the root directory.

```shell
npm run java:docker
```

The command is slightly different if you're using a Mac with Apple Silicon.

```shell
npm run java:docker:arm64
```

**TIP**: You can see all npm scripts with `npm run`.

Then, navigate to the `docker-compose` directory, stop the existing containers, and start all the containers.

```shell
cd docker-compose
docker stop $(docker ps -a -q);
docker compose up
```

This command will start and run all the apps, their databases, Consul, and Keycloak. To make Keycloak work, you must add the following line to your hosts file (`/etc/hosts` on Mac/Linux, `c:\Windows\System32\Drivers\etc\hosts` on Windows).

```
127.0.0.1  keycloak
```

This is because you will access your application with a browser on your machine (where the name is localhost, or `127.0.0.1`), but inside Docker it will run in its own container, where the name is `keycloak`.

If you want to prove everything works, ensure everything is started at `http://localhost:8500`, then run `npm run e2e -ws` from the root project directory. This command will run the Cypress tests that JHipster generates in your browser.

## Switch identity providers

JHipster ships with Keycloak when you choose OAuth 2.0 / OIDC as the authentication type. However, you can easily change it to another identity provider, like Auth0!

First, you'll need to register a regular web application. Log in to your Auth0 account (or [sign up](https://auth0.com/signup) if you don't have an account). You should have a unique domain like `dev-xxx.us.auth0.com`.

Select **Create Application** in the [Applications section](https://manage.auth0.com/#/applications). Use a name like `Micro Frontends`, select **Regular Web Applications**, and click **Create**.

Switch to the **Settings** tab and configure your application settings:

- Allowed Callback URLs: `http://localhost:8080/login/oauth2/code/oidc`
- Allowed Logout URLs: `http://localhost:8080/`

Scroll to the bottom and click **Save Changes**.

In the [roles](https://manage.auth0.com/#/roles) section, create new roles named `ROLE_ADMIN` and `ROLE_USER`.

Create a new user account in the [users](https://manage.auth0.com/#/users) section. Click the **Role** tab to assign the roles you just created to the new account.

_Make sure your new user's email is verified before attempting to log in!_

Next, head to **Actions** > **Flows** and select **Login**. Create a new action named `Add Roles` and use the default trigger and runtime. Change the `onExecutePostLogin` handler to be as follows:

```js
exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://www.jhipster.tech';
  if (event.authorization) {
    api.idToken.setCustomClaim('preferred_username', event.user.email);
    api.idToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
    api.accessToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
  }
}
```

This code adds the user's roles to a custom claim (prefixed with `https://www.jhipster.tech/roles`). This claim is mapped to Spring Security authorities in `SecurityUtils.java` in the gateway app.

Select **Deploy** and drag the `Add Roles` action to your Login flow.

Edit `docker-compose/central-server-config/application.yml` and append the following YAML block to add your Auth0 settings.

```yaml
jhipster:
  security:
    oauth2:
      audience: https://<your-auth0-domain>/api/v2/

spring:
  security:
    oauth2:
      client:
        provider:
          oidc:
            issuer-uri: https://<your-auth0-domain>/ # make sure to include the trailing slash!
        registration:
          oidc:
            client-id: <your-client-id>
            client-secret: <your-client-secret>
```

NOTE: Want to have all these steps automated for you? Vote for [issue #351](https://github.com/auth0/auth0-cli/issues/351) in the Auth0 CLI project.

Stop all your Docker containers with <kbd>Ctrl+C</kbd> and start them again.

```shell
docker compose up
```

Now, Spring Security will be configured to use Auth0, and Consul will distribute these settings to all your microservices. When everything is started, navigate to `http://localhost:8080` and click **sign in**. You will be prompted for your Auth0 credentials.

![Auth0 login](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r43ne0n7qoptf5v7gzw1.png)

After entering your credentials, you'll be redirected back to the gateway, and your username will be displayed.

![Auth0 login success](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/755ktrwpg8mcw1xh465g.png)

You should be able to add, edit, and delete blogs, posts, tags, and products, proving that your microservices and micro frontends can talk to each other.

If you'd like to use Okta for your identity provider, see [JHipster's documentation](https://www.jhipster.tech/security/#okta).

**TIP**: You can configure JHipster quickly with the [Okta CLI](https://cli.okta.com):

```shell
okta apps create jhipster
```

## Deploy with Kubernetes

The JDL you used to generate this microservices stack has a section at the bottom for deploying to Kubernetes.

```
deployment {
  deploymentType kubernetes
  appsFolders [gateway, blog, store]
  clusteredDbApps [store]
  kubernetesNamespace demo
  kubernetesUseDynamicStorage true
  kubernetesStorageClassName ""
  serviceDiscoveryType consul
  dockerRepositoryName "mraible"
}
```

The `jhipster jdl` command generates a `kubernetes` directory with this information and configures all your apps, databases, and Consul to be Kubernetes-ready. If you have a Kubernetes cluster created, you can deploy to its `demo` namespace using the following command.

```shell
./kubectl-apply.sh -f
```

It also generates files for Kustomize and Skaffold if you'd prefer to use those tools. See the [`kubernetes/K8S-README.md`](https://github.com/oktadev/auth0-micro-frontends-jhipster-example/blob/main/kubernetes/K8S-README.md) file for more information.

I won't go into the nitty-gritty details of deploying a JHipster microservices stack to cloud providers with K8s, mainly because it's covered in previous blog posts. The first post below shows how to run Minikube locally, encrypt your secrets, and deploy to Google Cloud.

- [Kubernetes to the Cloud with Spring Boot and JHipster](https://developer.okta.com/blog/2021/06/01/kubernetes-spring-boot-jhipster)
- [Kubernetes Microservices on Azure with Cosmos DB](https://developer.okta.com/blog/2022/05/05/kubernetes-microservices-azure)
- [Run Microservices on DigitalOcean with Kubernetes](https://developer.okta.com/blog/2022/06/06/microservices-digitalocean-kubernetes)
- [JHipster Microservices on AWS with Amazon Elastic Kubernetes Service](https://developer.okta.com/blog/2022/07/11/kubernetes-jhipster-aws)
- [CI/CD Java Microservices with CircleCI and Spinnaker](https://developer.okta.com/blog/2022/08/12/ci-cd-circleci-spinnaker-microservices)

The JHipster team also has a blog you can follow at [dev.to/jhipster](https://dev.to/jhipster).

## Learn more about micro frontends and microservices

I hope you enjoyed this overview of how to use micro frontends within a Java microservices architecture. I like how micro frontends allow each microservice application to be self-contained and deployable, independent of the other microservices. It's also pretty neat how JHipster generates Docker and Kubernetes configuration for you. Cloud-native FTW!

You can find the source code for this example on GitHub, in the [@oktadev/auth0-micro-frontends-jhipster-example](https://github.com/oktadev/auth0-micro-frontends-jhipster-example) repository.

If you'd like to learn more about micro frontends and microservices, I recommend these posts:

- [Micro Frontends with Angular, Module Federation, and Auth0](https://auth0.com/blog/micro-frontends-with-angular-module-federation-and-auth0/)
- [How to Build Micro Frontends Using Module Federation in Angular](https://developer.okta.com/blog/2022/05/17/angular-microfrontend-auth)
- [Secure and Deploy Micro Frontends with Angular](https://developer.okta.com/blog/2022/05/19/angular-microfrontend-deploy)
- [Reactive Java Microservices with Spring Boot and JHipster](https://developer.okta.com/blog/2021/01/20/reactive-java-microservices)
- [What are Microservices?](https://www.okta.com/blog/2021/02/microservices/)

Please follow [@auth0](https://twitter.com/auth0) and [@oktadev](https://twitter.com/oktadev) on Twitter. We also have YouTube channels that you might enjoy at [youtube.com/auth0](https://www.youtube.com/auth0) and [youtube.com/oktadev](https://www.youtube.com/oktadev). If you have any questions, please leave a comment below!