4

We have multiple micro services dependent on a common code(common.jar) which is a separate project(repo). Currently common code(common.jar) is included as dependent jar in all micro services.

gradle.properties:
implementation ("com.boa.dart:funding-common:23.5.1.108")  --> Prod configuration
implementation files("libs/funding-common.23.5.1.108.jar") --> Local configuration

As all micro services dependent on common project, development is taking much time when there is any change required in "Common Project". First we need to do code change in "Common project", then build copy that jar to other micro services, then build each individual micro service.

But look like we can have lot of improvement in this process.

So here my question is how can I use "Common Project" in all micro services without generating common.jar.

Here are TechStack: We are using gradle as a build tool. Spring Boot for micro Services. IntelliJ as IDE.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Satya
  • 8,146
  • 9
  • 38
  • 43
  • You can [define a composite build](https://docs.gradle.org/current/userguide/composite_builds.html#defining_composite_builds) that includes both your shared library and your application. This will use the classes built from the source over the one that can be downloaded from your repository. – Joachim Sauer Apr 26 '23 at 10:28

3 Answers3

2

So here my question is how can I use "Common Project" in all micro services without generating common.jar.

You cannot avoid having common.jar, as soon as both Maven and Gradle dependency management handles each dependency as a separate jar file.

What you can do is to create a parent build.gradle and include common module into it. In one of our projects we have the following structure:

root_dir/
..common/
.....src
.....build.gradle
..sql/
.....src
.....build.gradle
..api/
.....src
.....build.gradle
..build.gradle 
..settings.gradle

Here we created settings.gradle in root directory:

rootProject.name = 'parent'
include(':common')
include(':sql')
include(':api')

Under the same directory we have parent build.gradle with properties and settings common for all submodules:

allprojects {
  group = ''
  version = '1.0-SNAPSHOT'
}

ext {
  lombokVersion = '1.18.22'
  springBootVersion = '2.6.3'
}

subprojects {
  apply plugin: 'java'
  apply plugin: 'maven-publish'

  repositories {
    mavenLocal()
    mavenCentral()
  }
  //common dependencies
  dependencies {
    compileOnly "org.projectlombok:lombok:$lombokVersion"
    annotationProcessor "org.projectlombok:lombok:$lombokVersion"

    implementation platform("org.springframework.boot:spring-boot-dependencies:$springBootVersion")
  }

  sourceCompatibility = 17

  test {
    useJUnitPlatform()
  }
}

Then we have :common and :sql modules, both are dependencies for :api module.

In the build.gradle of :api module we juwt specify dependencies:

dependencies {
  implementation project(':sql')
  implementation project(':common')
  //...
}

This structure allows IDEA to import common project as a submodule and all changes to the code of that module will be automatically picked up and analyzed. So if you refactor or rename a property in POJO in :common module IDEA finds all its usages in :api module and refactors/renames them as well.

Sergey Tsypanov
  • 3,265
  • 3
  • 8
  • 34
1

While one way is with a multi-module gradle build, this is not helpful when the individual microservices are split across multiple repos, each with their own repos and build.gradle.

In this case, there are additional options, each with their own caveats.

mavenLocal

You can publish your common.jar to your maven local with something like this.

This works well if you only have a single development machine, otherwise, each developer will have to publish to local as well. Also, for distribution, you would have to bundle common.jar into the final jar, which you are likely doing already since it is included locally.

This approach would at least prevent you from having to copy/paste.

Run a maven server

Or, of course, you could run a maven on some machine all your devices have access to. Nexus works well. There are others.

This requires a dedicated "server" of some kind to run this. Might not work in all envs, but might be something worth investing in.

Gradle Task

And, of course, as a 'low-tech' solution, if you have a common directory structure and always work out of a workspace where you have all your other repos, you can have a task in the common's Gradle build that after building the jar simply copies the build artifact to the other repos based on known paths and a list of repos to update.

While potentially brittle, you could even perform git commands on those folders and prepare commits to add the changed file. YMMV on this, though, as it has plenty of its own pitfalls.

This requires all repos to be cloned, in the same relative paths, but has the advantage of simply removing the manual effort.

Scripting

And, of course, there's no reason you have to use Gradle to do the above. If can't use a multi-module build, and you can't use any of the other tasks, consider writing a script (bash, pwsh, python, etc), and at take advantage of some automation.

Yes, it has the pitfalls of the above, but it is simpler than doing a lot of manual tasks over and over, all of which have the potential of being skipped, forgotten, etc.

User51
  • 887
  • 8
  • 17
0

I am not sure what you mean by:

then build copy that jar to other micro services

When you create a new version of common jar, you should have a pipeline that puts it in some artifactory/nexus from where other dependent micro services can pick up the new version automatically.

What you can also do is instruct the micro services to look for the latest, or the latest version of a major release of that common library when they build

e.g. com.boa.dart:funding-common:23.+ will pick the latest version with the major version 23 (as long as you maintain backwards compatibility in each major version this can work).