Broadleaf Microservices
  • v1.0.0-latest-prod

Initializr Migration Guide

Note
The Broadleaf Manifest and Initializr are compatible with Broadleaf Release Train 1.8.2 and above.

Pre-requisites

  • A standard Initializr project can be set up with Broadleaf Release Train 1.8.2 and above. However, some of the features discussed in this article have been added in later versions. Therefore, if your Initializr composition ends up significantly different from a standard one, you may find it necessary to upgrade your Release Train version. You can find more information on new Initializr features in each Release Train’s upgrade notes, where such changes are typically highlighted. Alternatively, seek advice on your specific composition with our team through the appropriate support channels.

  • General familiarity with Broadleaf Initializr concepts to the extent described in the overview article.

Overview

Here is a high-level overview of the steps to migrate from a MicroservicesDemo-style project to an Initializr project:

Generate an Initializr project

Generate an Initializr project following the Project Generation guide. This standard project will serve as the basis of all steps to follow in this guide.

Tip
If you are planning to use two or more flexpackage compositions to run your project (i.e. Mono for local development and Balanced for nonlocal deployment), select the more granular of the compositions in this step.
Important

This migration guide assumes your project will be leveraging a Spring Cloud Config Server implementation for property management. This is strongly recommended by Broadleaf.

It is possible to adopt Initializr without using Config Server (you can disable the config component in your manifest), but then you will be responsible for manually setting the right properties across the different modules in your project. For example, when using Config Server, the Initializr will prepare a config/ directory with certain property values already set (database password, HTTPS keystore password, etc) for each module. If Config Server is not in use, the Initializr will not set those properties for you, and the right approach will differ for each client on a case-by-case basis. Thus, this article does not cover that scenario.

Running with a subset of services

If your current MicroservicesDemo project uses only a subset of Broadleaf Microservices, and you want your Initializr project to reflect that as well, services can be disabled within the manifest.yml file. Simply remove the services from the list of flexUnits of your flexpackages and set their routed properties to false (See screenshot below for an example).

AdminUser Service disabled in manifest

Now, the directories for those services can be safely removed. You may also need to delete dependencies on those services from various places around the project, such as the Data Ingestion and Manifest modules, as well as the flexpackages.

Alternatively, the manifest can take care of that removal for you by re-generating the project (i.e. run mvn flex:generate -DforceClean=true). Fair warning - any customization you’ve made to the code or files except the manifest.yml file will be lost if this command is run.

Verification

You can verify this step was successful by starting up the applications. The HELP.md file in the manifest folder can assist you with the new boot-up process.

Project Structure

The standard Initializr project structure includes separate directories for each flexpackage of your composition as well as a directory for each component you are customizing. There are additional components like the Data Ingestion module that will have their own directories.

Synchronize with a version control system

The Initializr project will become the new structure to store customizations and launch the applications. Therefore, it should be checked into your version control system (VCS). We recommend maintaining a repository for each top-level directory of the project. You should be able to reuse many of your repositories from a MicroserviceDemo-style project.

Important
Contents of the security directory should NOT be stored in source control, but instead should be uploaded and managed in a secure location such as an encrypted vault.
Note
If you are running Granular as your flexpackage composition, the number of repositories needed can get out of hand. See the repository footprint reduction workaround section for a potential recommendation.

Reducing the repository footprint (workaround)

Important

This workaround causes deviation from the standard project structure that the Broadleaf starter module is aware of.

This can have side effects that prevent operations such as flex:generate and docker-compose:generate from working properly.

As a result, in most cases, we do not advise following this workaround. We only provide it here as guidance for advanced clients who may be comfortable proceeding with this approach.

Future versions of the Broadleaf starter may come with built-in support for generating and interpreting a more reduced directory structure, and this documentation will be updated at the time such a feature is made available.

Until then, do note that there are advantages to the 'flat' directory structure that is currently used by default, despite its higher repository footprint. The primary advantage is that it facilitates easy recombination. A component module itself does not need to know how it will be combined or used in different flexpackages, and the separation of directory structure encourages this.

Depending on your flexpackage composition, a transition to Initializr may actually increase the number of repositories you need to maintain. This is especially true for Granular compositions, where the number of repositories nearly doubles as services with customizations each end up with one more repository. We have found that it is possible to avoid this, and the following example describes the steps to make that happen:

As an example, let’s say your current project runs a Granular composition and contains code customizations in the AdminUser service. The skeleton Initializr project you generated should contain two folders for AdminUser: adminuser and adminuserComponent.

  • The adminuser folder is the flexpackage - it contains the runnable Application and pulls in the adminuserComponent as a Maven dependency.

  • The adminuserComponent is the component - only client code customizations are stored there, and it does not have a runnable Application, instead serving as a mere dependency for any flexpackage pulling it in. Additionally, it pulls in the Broadleaf AdminUserServices as its dependency to build on top of.

To avoid creating another VCS repository to accommodate the component, you should create two sub-folders in your current repository directory (e.g. AdminUserServices/): flexpackage and component. In the following sections, we will discuss steps of integrating Initializr concepts into your current project. The flexpackage directory should be used for files from the adminuser folder, meanwhile the component directory should contain files from adminuserComponent.

As would be the case with entirely separate directories, both sub-folders are still separate Maven modules (as proven by their pom.xml files) and thus continue to participate in the project as if they were separate. The top-level service directory now contains an orchestration pom that simply declares the sub-folders (flexpackage and optionally component) as modules. Each sub-folder contains a pom.xml file designed to handle the sub-folder’s intended responsibilities.

Verification

You can verify this step was successful by starting up the applications.

Migrate the pom.xml files

The pom.xml files are responsible for defining properties, dependencies, and plugins for each of your services. They are also getting a face-lift with the Initializr. While each service used to have a single pom to handle all responsibilities, we have divided them according to responsibilities.

The following example poms of Release Train 2.1.0-GA ContentServices (in a Granular configuration) will help us walk through all the updates:

The parent pom

The largest change comes from adopting a new parent pom - com.broadleafcommerce.microservices:broadleaf-microservices-flex-parent. This parent pom sets versions and pulls in the necessary dependencies to run a Broadleaf microservice.

Properties

The skip properties

The skip…​ properties (e.g. skip-schema) enable a developer to control the execution of the various Initializr-related Maven plugins. The generated project has the correct values set for each of the properties (default value is true) to run a standard Initializr configuration. However, if your team has decided to diverge from the standard, the HELP.md file provided in the manifest folder details the purpose of each property. For example, removing the skip-schema entry would disable automatic liquibase generation if your team decides to manually write liquibase additions.

Client name

The client name property - broadleaf-client-prefix - should be set to the name you expect to appear in changelog file names and other locations around the project. It would most likely contain the same value for all services.

Docker

Broadleaf also introduced a new path of generating and booting up docker images. There is a number of properties in your pom.xml files that control the deployment of the images (see example screenshot below). They can be removed if your team has decided not to adopt the new process.

Highlighted docker properties in pom.xml

New dependencies

The following new dependencies can be found in Initializr pom.xml files:

The manifest

The manifest is a new Initializr-only Broadleaf concept. Services should have it as a dependency to receive the benefit of dynamic configuration properties. The manifest/ directory in your project corresponds to the Maven module that produces this artifact. Here is an example of what the dependency can look like:

<dependency>
    <groupId>com.example.microservices</groupId>
    <artifactId>microservice-flexpackage-manifest</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

Broadleaf service dependencies

Client implementation of Broadleaf services depend on Broadleaf’s service (e.g. AdminUserServices) as a base. With Initializr, this dependency becomes a bit more nuanced:

When a service contains code customizations, it will require for a component to be created in the Initializr project structure.

The flexpackage will then depend on the component:

<dependency>
    <groupId>com.example.microservices</groupId>
    <artifactId>microservice-component-inventory</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

And the component will contain a dependency on the Broadleaf base service:

<dependency>
    <groupId>com.broadleafcommerce.microservices</groupId>
    <artifactId>broadleaf-inventory-services</artifactId>
</dependency>
Note
The Broadleaf base service’s version is set by the flex-parent according to the Release Train at its specified version.

Config Server

To leverage Broadleaf’s Config Server feature set, each flexpackage should reference your Config Server maven coordinates as a dependency:

<dependency>
    <groupId>com.broadleafcommerce.microservices</groupId>
    <artifactId>broadleaf-config-server-client</artifactId>
</dependency>

Compatibility Extension

One of the recent versions of Spring Cloud Stream has removed support for annotation-based configuration. Broadleaf has re-introduced the removed annotations and provided backward compatibility as an option for clients. More on this here

Dependencies that support this compatibility both in code and in tests should be included in each flexpackage and component to facilitate normal operations:

<dependency>
    <groupId>com.broadleafcommerce.microservices</groupId>
    <artifactId>broadleaf-common-extension-compatibility</artifactId>
</dependency>
<dependency>
    <groupId>com.broadleafcommerce.microservices</groupId>
    <artifactId>broadleaf-common-extension-compatibility-test</artifactId>
    <scope>test</scope>
</dependency>

Moving custom dependencies

We recommend transitioning custom dependencies to the component along with your customizations in Migrating code customizations.

Migrate liquibase and configuration properties

With Initializr, both Liquibase change-sets and configuration properties need to be split up between the Flexpackage and the Component modules for each service. The exact distinction between the two modules can seem fuzzy, so here is a good summary to begin with:

  • In short, Flexpackages are responsible for determining environment-specific configurations and contain files specific to the deployment.

  • Components are responsible for contributing your customizations. They contain properties and changesets that go with the service, regardless of the environment or flexpackage composition.

This matrix table can be used to guide you through nuanced best practices of placing change-sets and properties on either side of the Component vs Flexpackage split.

Entity Type Environment-specific Example Service has component Destination

Liquibase changeset

Seed Data

No

db/changelog/admin-user-seed-data.sql

Yes or No

Flexpackage

Liquibase changeset

Seed Data

Yes

db/changelog/qa/qa-admin-client-redirects.sql

Yes or No

Flexpackage

Liquibase changeset

Broadleaf Override

No

db/changelog/add-application-metadata-permissions-to-users.sql

Yes or No

Flexpackage

Liquibase changeset

Broadleaf Override

Yes

db/changelog/prod/admin-force-password-reset.sql

Yes or No

Flexpackage

Liquibase changeset

Generated Customization

No

db/changelog/offer.example.postgresql.changelog-master.xml

Yes

Component

Liquibase changeset

Data Migration to run before required starter data

No

db/changelog/auth.fix-seed-data.postgresql.changelog-master.xml

Yes

Component

Configuration Parameter

Broadleaf Override

No

broadleaf.adminuser.data.load.default-users

Yes

Component

Configuration Parameter

Broadleaf Override

No

broadleaf.adminuser.data.load.default-users

No

Flexpackage

Configuration Parameter

Broadleaf Override

Yes

broadleaf.adminuser.auth-properties.auth-uri

Yes or No

Flexpackage

Configuration Parameter

Spring or other non-Broadleaf override for customizations

No

spring.h2.console.enabled or spring.integration.jdbc.initialize-schema

Yes

Component

Configuration Parameter

Spring or other non-Broadleaf override for customizations

No

spring.h2.console.enabled or spring.integration.jdbc.initialize-schema

No

Flexpackage

Configuration Parameter

Spring or other non-Broadleaf override, specific to deployments

No

server.ssl.enabled or spring.application.name

Yes or No

Flexpackage

Configuration Parameter

Spring or other non-Broadleaf override

Yes

spring.cloud.gcp.pubsub.emulator-host

Yes or No

Flexpackage

Configuration Parameter

Custom client properties

No

example.notification.email.default-sender

Yes

Component

Configuration Parameter

Client properties for external connections

Yes

example.cart.external-platform.redirect-uri

Yes or No

Flexpackage

Configuration Parameter

Custom Cloud Stream Binding destinations

No

spring.cloud.stream.bindings.recordExternalRedemptionInput

Yes

Component

(Optional) Filter out default property values

A number of environment post-processors driven by your manifest have been introduced with Initializr. In practice, they dynamically determine which properties are needed by the project, significantly reducing boilerplate configuration.

To remove the redundant properties from your project, first get the Environment Report for each flexpackage from the standard Initializr project you generated in the first step of this guide. For that, you need to define the following property in each configuration file config/insecure/[flexpackageName].yml and run the respective applications:

 broadleaf:
    environment:
        report:
          disabled: false

The Environment Report will be one of the first things printed in the logs.

Tip
You can find its start by searching for "Broadleaf Environment Report"

Then, after moving over your custom configuration files, remove all property definitions that have the same values in the standard environment report. Those are default values for your manifest, and your project will inherit them automatically.

Verification

You can verify this step was successful by starting up the applications and checking the environment report.

Migrating code customizations

All code customizations should be moved into the Component side of each service. The Flexpackages should not contain any custom Java files. Configuration property files and liquibase changelogs can be placed in the Flexpackage. Consult the Split Matrix for further details

Verification

You can verify this step was successful by starting up the applications and smoke testing your application.

Common Issues

Customizations missing when running Applications

When starting up your Application, you may find that some of your custom functionality is not picked up and is missing from the running Application. This same issue may manifest itself with exceptions claiming missing autowired (injected) dependencies as well.

The most likely cause of this is your AutoConfiguration files are not declared in Spring’s new autoconfigurations file. With SpringBoot 3, Spring introduced the META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports file to replace the historic spring.factories in declaring Autoconfigurations. While this change made its way into the Broadleaf codebase with the 2.0.0-GA Release Train, client customizations and a running @SpringBootApplication being present in the classpath could have made the shift unnoticeable for some clients.

With Initializr, your code customizations live in the component and away from the @SpringBootApplication, so any AutoConfiguration files should be declared in the aforementioned file to be consumed at runtime. Find an example of such a file from our NotificationServices below:

Example of the new Spring AutoConfigurations file from Broadleaf’s NotificationServices

Liquibase conflicts between custom data & Broadleaf’s [service].starter.required.data.changelog.xml

With Initializr, there is a change in changelog file execution order when some of your changelog files are placed on the flexpackage side of the split.

Prior to Initializr, the client had the flexibility to execute any (and all) changelogs in their folder prior to the required data changelogs ([service].starter.required.data.changelog.xml) by changing the ordering within their changelog-master yml files.

Now, the master changelog file from the component ([service].component.[database].changelog-master.xml) is the only file executing ahead of the required data changelogs. All liquibase changelogs in the component should be referenced within the aforementioned file, and they will run prior to required data changelogs.

With files on the flexpackage side running after required data changelogs, it creates a potential liquibase data conflict if any of client’s custom liquibase or sql files run without conflict resolution clauses. There are two potential resolution paths: changing how your liquibase or sql files react to conflicts, or executing them prior to the required data changelogs.

It is a simple effort to have a given changelog file run in front of the required starter data. It should be simply moved to the component side of the split and added as an entry to the [service].component.[database].changelog-master.xml in the component.