Konsist: Protect Kotlin Multiplatform projects from architecture guidelines violations.
Step-by-step instructions on integrating Konsist with Kotlin Multiplatform Mobile.
Let’s say you have created a project architecture and you expect your team members to follow the architecture guidelines. But there might be a developer who does not follow the guidelines strictly always. Depending on the scope of the project, developers may get reassigned, or teams may be divided into multiple small teams where they must focus on specific modules. So most of the team members will not have a holistic view of the project and with time, things can get messy.
And here comes the Linter tool to save your day. Even though it won’t solve your personal debt and problems, it certainly prevents you from accumulating technical debt and also provides visibility into your code health.
So to have consistency through the project structure and to test whether the project architecture guideline hasn’t been violated we can use an architectural linter called Konsist. Konsist is a Kotlin static code analyzer that supports Kotlin-based projects including Android, Kotlin Multiplatform, and Spring. Although this library is in its early stages of development, it covers the problems and usecases we see every day today.
Konsist offers two types of checks: Declaration Checks, which test for declaration-level violations, and Architectural Checks, which test for architectural guidelines and boundaries.
Without further delay, let’s dive right in.
Prerequisites
For this example, I’m using a Multi-Module Kotlin Multiplatform Mobile project with the following structure. Alternatively, you can use Kotlin Multiplatform Mobile’s default structure.
Configuration
As a first step, we’ll create a new module for the Konsist Test that separates the konsist test from the unit test to improve readability.
- Right click on the shared module.
- Select New then Module to open the Create New Module window.
- Select the Kotlin Multiplatform Shared Module Template
- Enter your desired module name. Ex: :shared:konsisttest.
- Name your package. The rest is the same as the following image, so click Finish.
You will then be able to see your new module on the project as follows.
Dependency
Add mavenCentral repository
repositories {
//...
mavenCentral()
//...
}
Next, we will configure the Gradle file for :shared:konsisttest.
- Remove the Android Gradle plugin from the :shared:konsisttest module’s Gradle, along with its corresponding ‘android()’ Kotlin Target
- Add the ‘jvm()’ Kotlin Target and ‘jvmTest’ source set.
- Add the Konsist dependency to ‘jvmTest’ in the :shared:konsisttest module’s Gradle.
val jvmTest by getting {
dependencies {
implementation("com.lemonappdev:konsist:{VERSION}")
}
}
Following these steps, create a file in your ‘jvmTest’ source set that will be used to write test methods.
- Select New then Directory from the right-click menu on the module (:shared:konsisttest)
- Create the source set directory (src/jvmTest/kotlin)
- Create your package (Ex: com.stargatex.mt.lahiru.konsisttest)
- Create a file (Ex: AppKonsistTest)
Implementation
First let’s take a look at the steps for ‘declaration check’.
- As a first step, we need to determine the scope, which includes files from an entire project, a module, a package, or just one Kotlin file. (Create the scope)
// Scope for Kotlin files availble in the project
Konsist.scopeFromProject()
// Scope for Kotlin files availble in the 'app' module
Konsist.scopeFromModule("app")
// Scope for Kotlin files availble in the 'test' source set
Konsist.scopeFromSourceSet("test")
// Scope for Kotlin files availble for production code
Konsist.scopeFromProduction()
- We then query all items in the scope and filter them according to our needs. (Query and Filter)
- Finally, we define the assertion. (Assert)
Now let’s examine the steps for ‘architecture check’.
- As a first step, we need to determine the scope.
- Next, we define layers of the project and assertions using the ‘assertArchiteture’ method
Konsist
.scopeFromProduction() // Define the scope
.assertArchitecture { // Assert architecture
// Define layers
val domain = Layer("Domain", "com.stargatex.mt.kmm.lahiru.shared.domain..")
// Define the relations between each layer for architecture assertions
domain.dependsOnNothing()
}
Lastly, both ‘declaration check’ and ‘architecture check’ of Konsist test code will be executed as unit tests.
Use cases
Here are some examples of use cases you might need to test in a project.
For more examples, They have provided good examples that you can use as inspiration on how this can be used (snippets).
All right, that’s it. Using this library correctly could mean you don’t accumulate technical debt resulting from architectural violations and those violations might be found in reviews rather than going unnoticed. I have begun integrating this library into my current organization’s Kotlin Android and Kotlin Multiplatform Mobile projects. It was a pleasant experience thanks to good documentation, seamless integration abilities, and extensibility for custom needs.
I hope you found this useful. Until next time 🙏 👋