nxrocks icon indicating copy to clipboard operation
nxrocks copied to clipboard

[Question] Spring Boot app and lib usage

Open while1618 opened this issue 4 years ago • 8 comments

Are there any examples of how apps and libs work together in nx-spring-boot?

I don't see the point of libs right now, it's like a separated app. In my case, I have an app with the following packages: "admin, auth, user, shared". I want to divide them into separated libs and use them in the app, but I can't find a way in doing that. I can't even import the class from lib to app.

Also, is there a way to have just one pom.xml in the main app, and that libs will read from it, like in angular for example? You have one main package.json, and all libs and apps can read from that one package.json.

while1618 avatar Jul 20 '21 08:07 while1618

Hi @while1618,

Sorry for the late answer.

The idea of library projects was to allow reusing them in your boot applications (by adding the dependency in applications build.gradle or pom.xml). They are not meant to be executable like boot jars (I recently enhanced that with #75, based on this guide on spring.io). Just like they don't need to be embedded inside a parent project (multi-module) per se.

Regarding the second point, Spring Initializr doesn't support multi-module projects out-of-the-box (https://github.com/spring-io/initializr/issues/663), so doesn't this plugin.

But with the support for Nx's dependency graph api, this is not a big deal, as one could have library projects built first, when running a command on the application that uses it (#66, work in progress)

tinesoft avatar Aug 21 '21 16:08 tinesoft

Hi @tinesoft ,

Thanks to your comment, I was able to create a "@nxrocks/nx-spring-boot" library and import it in an app. I updated the build target of the lib with the additional argument install.

    "build": {
      "executor": "@nxrocks/nx-spring-boot:build",
      "options": {
        "root": "libs/my-lib",
        "args": [
          "install"
        ]
      }
    },

Currently the library is installed in the directory ~/.m2/repository. One improvement I'm seeking to make is to install the library to a folder inside the Nx workspace so that Nx can cache the build artifact. Any idea on what the best way to achieve this would be? Thanks!

tschaffter avatar Jun 25 '22 00:06 tschaffter

After giving it more thought, it's preferable to keep installing Maven packages to the default location (e.g. ~/.m2/repository) to keep the environment as standard as possible. This means that the "install" operation should not be part of a nx-cacheable task like "build" because Nx is not caching ~/.m2/repository.

Here is the solution I came up with:

  1. Add an "install" task to the library project.
    "install": {
      "executor": "@nrwl/workspace:run-commands",
      "options": {
        "command": "./mvnw install",
        "cwd": "libs/shared/data-access-java"
      },
      "dependsOn": [
        {
          "target": "build",
          "projects": "self"
        }
      ]
    },
  1. In the app that imports the library, specify that the task "build" depends on the task "install" of dependencies.
    "build": {
      "executor": "@nxrocks/nx-spring-boot:build",
      "options": {
        "root": "apps/challenge-core-service",
        "args": []
      },
      "dependsOn": [
        {
          "target": "install",
          "projects": "dependencies"
        }
      ]
    },

@tinesoft What do you think of this approach? If it were to be kept, then adding to this plugin a task like @nxrocks/nx-spring-boot:install that run ./mvnw install under the hood would enable to make the library project.json more compact than when using @nrwl/workspace:run-commands as I do.

tschaffter avatar Jun 26 '22 18:06 tschaffter

Hi @tschaffter

Sorry, I didn't get to you sooner...

Indeed, the packages will always be installed in the default location (e.g. ~/.m2/repository). this is what Maven does by default, and there is no need to change it for the Nx caching to work. (the cache not working right now, is another issue that you spotted in https://github.com/tinesoft/nxrocks/issues/111, I will fix that)

As for your approach (task dependency) it is totally valid, in fact, I plan to integrate that out-of-the-box in the plugin, see https://github.com/tinesoft/nxrocks/issues/66

So stay tuned!

tinesoft avatar Jun 26 '22 21:06 tinesoft

Hi @tinesoft

I want to propose a different strategy for installing a library so that it can be imported in an app. The motivation is to enable the app to be dockerized. Dockerizing the app when the library is installed to the local repository (~/.m2/repository) is not possible in a simple way.

Instead, the strategy I'm exploring is to copy the jar library to the app folder when running the install target of the library. Then, the idea is that the following Dockerfile could be used to dockerize the Spring Boot app. See the line COPY lib /workspace/lib where I copy the lib folder that includes the jar file of the library.

FROM maven:3.8.4-openjdk-17 AS build
RUN mkdir -p /workspace
WORKDIR /workspace
COPY pom.xml /workspace
COPY src /workspace/src
COPY lib /workspace/lib
RUN mvn -f pom.xml clean package -DskipTests --no-transfer-progress

FROM openjdk:17-alpine
COPY --from=build /workspace/target/*.jar app.jar
ENTRYPOINT ["java","-jar","app.jar"]

I have created a GithHub repository (tschaffter/nx-spring-boot-example) that we could share to illustrate one recommended way to organize apps and libs generated with @nxrocks/nx-spring-boot.

I currently have an issue where I cannot build the app because it can not successfully import the library. If you have the time, do you mind having a look at this broken feature branch to see what I'm doing wrong? My Java is a bit rusty so we can assume that I'm doing something dumb. 😉

tschaffter avatar Jun 28 '22 22:06 tschaffter

I found the solution to the above issue, see #113.

tschaffter avatar Jun 28 '22 23:06 tschaffter

I want to propose a different strategy for installing a library so that it can be imported in an app. The motivation is to enable the app to be dockerized. Dockerizing the app when the library is installed to the local repository (~/.m2/repository) is not possible in a simple way.

Instead, the strategy I'm exploring is to copy the jar library to the app folder when running the install target of the library. Then, the idea is that the following Dockerfile could be used to dockerize the Spring Boot app. See the line COPY lib /workspace/lib where I copy the lib folder that includes the jar file of the library.

FROM maven:3.8.4-openjdk-17 AS build RUN mkdir -p /workspace WORKDIR /workspace COPY pom.xml /workspace COPY src /workspace/src COPY lib /workspace/lib RUN mvn -f pom.xml clean package -DskipTests --no-transfer-progress

FROM openjdk:17-alpine COPY --from=build /workspace/target/*.jar app.jar ENTRYPOINT ["java","-jar","app.jar"] I have created a GithHub repository (tschaffter/nx-spring-boot-example) that we could share to illustrate one recommended way to organize apps and libs generated with @nxrocks/nx-spring-boot.

@tschaffter Note that Spring-boot has built-in support for generating an optimised Dockerfile to package and run the application. This is supported by the plugin as well, via the command nx buildImage bootapp, so I would recommend to use that instead.

Your can customise some options of that command (like setting the docker Image name for e.g) by adding it in the args of related target in project.json

{
  "version": 1,
  "projects": {
    "you-boot-app": {
      "projectType": "application",
      "root": "apps/your-boot-app",
      "sourceRoot": "apps/your-boot-app/src",
      "targets": {
        "buildImage": {
          "executor": "@nxrocks/nx-spring-boot:buildImage",
          "options": {
            "root": "apps/your-boot-app",
            "args": ["-Dspring-boot.build-image.imageName=yourcompany/your-boot-app-docker"]
          }
        }
      }
    }},
  "cli": {
    "defaultCollection": "@nrwl/workspace"
  }
}

tinesoft avatar Jul 11 '22 04:07 tinesoft

Thanks for pointing out the executor @nxrocks/nx-spring-boot:buildImage. I can confirm that using it to build an image of a Spring app that depends on a local library installed in ~/.m2/repositories works (as described in this comment).

tschaffter avatar Jul 11 '22 21:07 tschaffter