A Gradle plugin to constrain a multi-module build dependency graph.
Meant to enforce an API/Implementation modularisation pattern in a multi-module monolith project:
Note
Credits of the picture to this good reference
article Slaying the monolith: API/Implementation modularisation pattern in Android development
by Sean Coyle
Example of the failure when, for instance, implementation tries to depend on another implementation module:

Apply the plugin at the root project:
plugins {
java
id("io.github.gmazzo.modulekind") version "<latest>"
}Then each subproject must be configured with as api or implementation:
moduleKind = "api" // or "implementation" or "monolith"The plugin introduces a new io.github.gmazzo.modulekind attribute that:
- Is requested on key resolvable
Configurations for known plugins (javaandandroid) - Decorates module's
Outgoing Variants. - Is used by an
AttributeCompatibilityRuleto determine if is a legal dependency for its consumer
It's recommended to read the Understanding Variant Selection chapter in Gradle's manual for a better understanding.
By default, the plugin will provide the following compatibility table (which can be printed by running
moduleKindConstraints task):
moduleKind |
api | implementation | monolith |
|---|---|---|---|
| api | ❌ | ❌ | ❌ |
| implementation | ✅ | ❌ | ❌ |
| monolith | ✅ | ✅ | ✅ |
These restrictions are fully configurable through the moduleKindConstraints DSL.
The following is the equivalent configuration of the table above:
moduleKindConstraints {
"implementation" compatibleWith "api"
"monolith" compatibleWith "implementation"
"monolith" compatibleWith "api" // redundant, since compatibilities are transitive
}Or in groovy:
moduleKindConstraints {
compatibility("implementation", "api")
compatibility("monolith", "implementation")
compatibility("monolith", "api") // redundant, since compatibilities are transitive
}Note
Compatibles are transitive. So, if monolith can depend on implementation and implementation on api,
then monolith can also depend on api.
Like the demo project, imagine you want to have an extra deep in the 4th levels dependency graph where:
appis the monolithimplementationandapiare the usual setupapican also ondomainmodules, meant to share domain objects betweenapis
moduleKindConstraints {
"api" compatibleWith "domain"
"implementation" compatibleWith "api"
"app" compatibleWith "implementation"
}