Architecture Assertion
Verify codebase using Konsist API
Architecture assertions are used to perform architecture verification. It is the final step of Konsist verification preceded by scope creation (Create The Scope):
Assert Architecture
As an example, this simple 2-layer architecture will be used:
The assertArchitecture
block defines architecture layer rules and verifies that the layer requirements are met.
Konsist
.scopeFromProject()
.assertArchitecture {
// Assert architecture
}
Define Layers
Create Layer class instance to represent project layers. Each Layer
instance accepts the name
(used for presenting architecture violation errors) and package
used to define architectural layer:
Konsist
.scopeFromProject()
.assertArchitecture {
// Define layers
val presentation = Layer("Presentation", "com.myapp.presentation..")
val data = Layer("Data", "com.myapp.data..")
}
Define Architecture Assertions
The final step is to define the dependencies (relations) between each layer using one of these methods:
dependsOn
dependsOnNothing
doesNotDependOn
The above methods follow up the layer definitions inside assertArchitecture
block:
Konsist
.scopeFromProject()
.assertArchitecture {
val presentation = Layer("Presentation", "com.myapp.presentation..")
val data = Layer("Data", "com.myapp.data..")
// Define dependencies
presentation.dependsOn(data)
data.dependsOnNothing()
}
Strict DependsOn
By default dependsOn
method works like does not perform strict layer validation (strict = false
). However this behaviour is controlled b ystrict
parameter:
strict = false
(default) - may depend on layerstrict = true
- have to depend on layer
e.g.
// Optional dependency - Feature layer may depend on Domain layer
featureLayer.dependsOn(domainLayer) // strict = false by default
// Required dependency - Feature layer must depend on Domain layer
featureLayer.dependsOn(domainLayer, strict = true)
Excluding Files
Architecture verification can be performed on KoScope
(as seen above) and a list containing KoFiles
. For example, you can remove a few files from the scope before performing an architectural check:
Konsist
.scopeFromProject()
.files
.withNameStartingWith("Repository")
.assertArchitecture {
val presentation = Layer("Presentation", "com.myapp.presentation..")
val data = Layer("Data", "com.myapp.data..")
presentation.dependsOn(data)
data.dependsOnNothing()
}
This approach provides more flexibility when working with complex projects, however, The desired approach is to create a dedicated scope. See Create The Scope.
Include Layer Without Defining Dependency
The include method allows to include layer in architecture verification, without defining a dependency for this layer:
private val domain = Layer("Domain", "com.domain..")
private val presentation = Layer("Presentation", "com..presentation..")
Konsist
.scopeFromProject()
scope.assertArchitecture {
// Include presentation for architectural check without defining a dependency
presentation.include()
// Include domain layer or architectural check and define no dependency (independent)
domain.doesOnNothing()
}
}
Architecture As A Variable
Architecture configuration can be defined beforehand and stored in a variable to facilitate checks for multiple scopes:
// Define architecture
val architecture = architecture {
val presentation = Layer("Presentation", "com.myapp.presentation..")
val data = Layer("Data", "com.myapp.data..")
presentation.dependsOn(data)
data.dependsOnNothing()
}
// Assert Architecture of two modules using common architecture rules
moduleFeature1Scope.assertArchitecture(architecture)
moduleFeature2Scope.assertArchitecture(architecture)
This approach may be helpful when refactoring existing applications. To facilitate readability the above checks should be expressed as two unit tests:
class ArchitectureTest {
private val architecture = architecture {
val presentation = Layer("Presentation", "com.myapp.presentation..")
val data = Layer("Data", "com.myapp.data..")
presentation.dependsOn(data)
data.dependsOnNothing()
}
@Test
fun `architecture layers of feature1 module have dependencies correct`() {
moduleFeature1Scope.assertArchitecture(architecture)
}
@Test
fun `architecture layers of feature2 module have dependencies correct`() {
moduleFeature2Scope.assertArchitecture(architecture)
}
}
Last updated