0%
June 22, 2024

Gradle Fundamentals: Modularization of Spring Boot Project and Dependencies Control

gradle

kotlin

springboot

Repository

Setp up Modules

Set Where to find Repositories

For every build.gradle.kts in each of modules we can add:

// build.gradle.kts

repositories {
    google()
    mavenCentral()
}
Transform Spring boot Project into a Module Called springboot-restapi
  1. Create a sprintboot-restapi folder
  2. Move src/, build.gradle.kts into that folder
  3. In settings.gradle.kts at the root level, write:
    rootProject.name = "course-catelog-service"
    
    pluginManagement {
        repositories.gradlePluginPortal()
    }
    
    dependencyResolutionManagement{
        repositories.mavenCentral()
    }
    
    include("springboot-restapi")
    • Now gradle understands there is a build task in springboot-restapi.
    • Suppose that we have a custom build task named generate in springboot-restapi/build.gradle.kts, then we can execute
      gradle :springboot-restapi:generate
      to run that build task.
Add springBoot mainClass for Modularized Spring boot Application

Now to successfully run the task bootRun and the build bootJar we still need extra configruation.

// springboot-restapi/build.gradle.kts
...
sourceSets {
    test {
        java {
            setSrcDirs(listOf("src/test/integration", "src/test/unit"))
        }
    }
}

springBoot  {
    mainClass = "com.kotlinspring.CourseCatelogServiceApplicationKt"
}

Note the extra Kt as a suffix of our main application classname.

Create Another Module Called domain
  1. In root level let's create a directory named domain/

  2. In that folder let's create a file in

    domain/src/main/kotlin/com.<root-project-name>.domain/main.kt
  3. Also, create a build.gradle.kts at the same level of domain/src/ and write

    repositories {
        google()
        mavenCentral()
    }
    
    plugins {
        kotlin("jvm") version "1.9.24"
    }
  4. Include this module in root's settings.gradle.kts

    rootProject.name = "course-catelog-service"
    
    pluginManagement {
        repositories.gradlePluginPortal()
    }
    
    dependencyResolutionManagement{
        repositories.mavenCentral()
    }
    
    include("springboot-restapi")
    include("domain")

    Now our folder icons are not just a directory, we are done:

Include domain in the springboot-restapi (i.e., restapi depends on domain) and try to run bootRun and bootJar

Add the following in springboot-restapi/build.gradle.kts:

dependencies {
    implementation(project(":domain"))
    ...
}

Now we are free to run these two gradle commands:

Try to Import and Unimport Other Modules to Check Dependencies Constraint

Let's experiment! Suppose that I have created a User class in domain.model:

We can import User from domain.model!

Suppose that I remove the dependency as follows:

Look we cannot import User from domain.model any more:

Custom Gradle Plugin

Plugin Project Sturcture

Let's create the following structure for adding plugins:

Gradle Config
Global settings.gradle.kts

Add the following in the global settings.gradle.kts

rootProject.name = "course-catelog-service"

pluginManagement {
    repositories.gradlePluginPortal()
    includeBuild("gradle/plugins")
}

dependencyResolutionManagement{
    repositories.mavenCentral()
}

include("springboot-restapi")
include("domain")
Local settings.gradle.kts in gradle/plugins/

Next create gradle/plugins/ and then gradle/plugins/settings.gradle.kts with

// gradle/plugins/settings.gradle.kts
dependencyResolutionManagement{
    repositories.gradlePluginPortal()
}

include("java-plugins")
build.gradle.kts
  1. We then aims at creating a module java-plugins with the following structure:

  2. As directory java-plugins is included as a (candidate of) module, we create gradle/plugins/java-plugins/, let's turn this java-plugins into a module by adding

    // gradle/plugins/java-plugins/build.gradle.kts
    plugins{
        `kotlin-dsl`
    }
  3. Let's add a file in this module:

    gradle/plugins/java-plugins/src/main/kotlin/custom-java-base.gradle.kts

    custom-java-base will be our new plugin name.

  4. Add the logic into this new plugin that we want to share across all modules:

    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
    
    plugins{
        id("java-library")
    }
    
    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(21)
        }
    }
  5. Finally, we import this plugin into our domain module:

    // domain/build.gradle.kts
    plugins{
        id("custom-java-base")
    }

More Simple way to Create Modules via IntelliJ

After solid understanding on how gradle works, let's investigate how intelliJ provides shortcuts for generating new modules:

  1. Look at project structure:

  2. New Module:

  3. Choose gradle with kotlin DSL, choose suitable groupId, and click create:

  4. Note that domain has been added automatically in settings.gradle.kts:

  5. Unforturnately we don't have dependency control UI among modules, we need to add

    dependencies {
        implementation(project(":module-name"))
        testImplementation(kotlin("test"))
    }

    ourselves in build.gradle.kts.