Comment on page
Architecture Assert
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):
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
}
The Konsist repository contains multiple architectural tests. These tests are used internally to verify Konsist API. Each test has a Konsist code and a
mermaid
diagram presenting the architecture.Create layers instances to represent project layers. Each
Layer
instance accepts the name
(used for presenting architecture violation errors) and package
used to define layers.Konsist
.scopeFromProject()
.assertArchitecture {
// Define layers
val presentation = Layer("Presentation", "com.myapp.presentation..")
val data = Layer("Data", "com.myapp.data..")
}
The inclusion of two trailing dots indicates that the layer is denoted by the
com.myapp.business
package together with all of its sub-packages.val data = Layer("Data", "com.myapp.data..") // This package only and all sub-packages
val data = Layer("Data", "com.myapp.data") // This package only
The final step is to define the relations between each layer:
koScope.assertArchitecture {
val presentation = Layer("Presentation", "com.myapp.presentation..")
val data = Layer("Data", "com.myapp.data..")
presentation.dependsOn(data)
data.dependsOnNothing()
}
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:koScope
.files
.filter { it.name.startsWith("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.
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 modified 30d ago