Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KON-679 Allow To Call Layer Dependency Method On Collections #1507

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test

class EnumKonsistTest {
@Test
fun `enums consts are defined interface alphabetical order `() {
fun `enums consts are defined interface alphabetical order`() {
Konsist
.scopeFromProduction()
.classes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ interface LayerDependencies {
vararg layers: Layer,
): Unit

/**
* Adds dependencies between each layer in the collection and the specified layer.
*
* Example usage:
* ```
* val layers = setOf(domainLayer, dataLayer)
* layers.dependsOn(presentationLayer)
* ```
*
* @receiver The collection of [Layer]s that depends on other layer.
* @param layer The layer that the collection of layers depends on.
*/
fun Collection<Layer>.dependsOn(layer: Layer): Unit

/**
* Specifies that the current layer does not depend on the given list of layers.
*
Expand All @@ -46,6 +60,21 @@ interface LayerDependencies {
*/
fun Layer.doesNotDependOn(layers: Set<Layer>): Unit

/**
* Adds dependencies between each layer in the collection and the specified layers.
*
* Example usage:
* ```
* val sourceLayers = setOf(domainLayer, dataLayer)
* val targetLayers = setOf(presentationLayer, infrastructureLayer)
* sourceLayers.dependsOn(targetLayers)
* ```
*
* @receiver The collection of [Layer]s that depends on other layers.
* @param layers The set of layers that the collection of layers depends on.
*/
fun Collection<Layer>.dependsOn(layers: Set<Layer>): Unit

/**
* Specifies that the current layer does not depend on any other layer.
*
Expand All @@ -55,6 +84,49 @@ interface LayerDependencies {
*/
fun Layer.dependsOnNothing(): Unit

/**
* Specifies that none of the layers in the collection depend on any other layer.
*
* Example usage:
* ```
* val layers = setOf(domainLayer, dataLayer)
* layers.dependsOnNothing()
* ```
*
* @receiver The collection of [Layer]s that should not depend on any other layer.
* @see include
*/
fun Collection<Layer>.dependsOnNothing(): Unit

/**
* Specifies that none of the layers in the collection depend on the specified layer.
*
* Example usage:
* ```
* val layers = setOf(domainLayer, dataLayer)
* layers.doesNotDependOn(presentationLayer)
* ```
*
* @receiver The collection of [Layer]s that do not depend on the specified layer.
* @param layer The layer that none of the collection layers should depend on.
*/
fun Collection<Layer>.doesNotDependOn(layer: Layer): Unit

/**
* Specifies that none of the layers in the collection depend on any of the specified layers.
*
* Example usage:
* ```
* val sourceLayers = setOf(domainLayer, dataLayer)
* val targetLayers = setOf(presentationLayer, infrastructureLayer)
* sourceLayers.doesNotDependOn(targetLayers)
* ```
*
* @receiver The collection of [Layer]s that do not depend on the specified layers.
* @param layers The set of layers that none of the collection layers should depend on.
*/
fun Collection<Layer>.doesNotDependOn(layers: Set<Layer>): Unit

/**
* This function is used to include a Layer in the architecture without specifying any dependencies.
* It can be used in combination with dependsOnNothing() to specify that a layer does not depend on any other layer.
Expand All @@ -63,4 +135,18 @@ interface LayerDependencies {
* @return The LayerDependenciesCore instance for chaining
*/
fun Layer.include(): Unit

/**
* Includes all layers from the collection in the architecture without specifying any dependencies.
* This can be used in combination with dependsOnNothing() to specify that multiple layers do not depend on any other layer.
*
* Example usage:
* ```
* val layers = setOf(domainLayer, dataLayer)
* layers.include()
* ```
*
* @receiver The collection of [Layer]s to be included in the architecture.
*/
fun Collection<Layer>.include(): Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal class LayerDependenciesCore(
internal val dependsOnDependencies: Map<Layer, Set<Layer>>
get() =
layerDependencies
.filter { it.dependencyType == LayerDependencyType.DEPEND_ON_LAYER }
.filter { it.dependencyType == LayerDependencyType.DEPENDS_ON_LAYER }
.groupBy { it.layer1 }
.mapValues { (_, dependencies) ->
dependencies.mapNotNull { it.layer2 }.toSet()
Expand Down Expand Up @@ -87,12 +87,20 @@ internal class LayerDependenciesCore(
}

layers.forEach {
addLayerDependency(this, LayerDependencyType.DEPEND_ON_LAYER, it)
addLayerDependency(this, LayerDependencyType.DEPENDS_ON_LAYER, it)
}

layerValidatorManager.validateLayerDependencies(layerDependencies)
}

override fun Collection<Layer>.dependsOn(layer: Layer) {
forEach { it.dependsOn(layer) }
}

override fun Collection<Layer>.dependsOn(layers: Set<Layer>) {
forEach { it.dependsOn(layers) }
}

private fun getLayersMessage(layers: Set<Layer>) =
if (layers.size == 1) {
"layer '${layers.first().name}'"
Expand Down Expand Up @@ -131,6 +139,14 @@ internal class LayerDependenciesCore(
layerValidatorManager.validateLayerDependencies(layerDependencies)
}

override fun Collection<Layer>.doesNotDependOn(layer: Layer) {
forEach { it.doesNotDependOn(layer) }
}

override fun Collection<Layer>.doesNotDependOn(layers: Set<Layer>) {
forEach { it.doesNotDependOn(layers) }
}

override fun Layer.dependsOnNothing() {
val dependOnLayerDependencies = getLayersWithDependOnLayerDependency(this)
val dependOnLayers =
Expand All @@ -151,11 +167,19 @@ internal class LayerDependenciesCore(
layerValidatorManager.validateLayerDependencies(layerDependencies)
}

override fun Collection<Layer>.dependsOnNothing() {
forEach { it.dependsOnNothing() }
}

override fun Layer.include() {
layers.add(this)
layerDependencies.add(LayerDependency(this, LayerDependencyType.NONE, null))
}

override fun Collection<Layer>.include() {
forEach { it.include() }
}

private fun getLayerWithDependOnNothingDependency(layer: Layer): LayerDependency? {
val dependOnLayerDependency =
layerDependencies.firstOrNull {
Expand All @@ -167,7 +191,7 @@ internal class LayerDependenciesCore(
private fun getLayersWithDependOnLayerDependency(layer: Layer): Set<LayerDependency> {
val dependOnLayerDependency =
layerDependencies.filter {
it.layer1 == layer && it.dependencyType == LayerDependencyType.DEPEND_ON_LAYER
it.layer1 == layer && it.dependencyType == LayerDependencyType.DEPENDS_ON_LAYER
}

return dependOnLayerDependency.toSet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal data class LayerDependency(
val layer2: Layer? = null,
) {
init {
require(!((dependencyType == LayerDependencyType.DEPEND_ON_LAYER) && layer2 == null)) {
require(!((dependencyType == LayerDependencyType.DEPENDS_ON_LAYER) && layer2 == null)) {
"layer2 cannot be null when dependency type is DEPEND_ON_LAYER"
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.lemonappdev.konsist.core.architecture

internal enum class LayerDependencyType {
DEPEND_ON_LAYER,
DEPEND_ON_NOTHING,
DEPENDS_ON_LAYER,
DOES_NOT_DEPEND_ON_LAYER,
NONE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal class CircularDependencyDependenciesRule : LayerDependenciesRule {

private fun buildDependencyGraph(dependencies: Set<LayerDependency>): Map<Layer, Set<Layer>> =
dependencies
.filter { it.dependencyType == LayerDependencyType.DEPEND_ON_LAYER && it.layer2 != null }
.filter { it.dependencyType == LayerDependencyType.DEPENDS_ON_LAYER && it.layer2 != null }
.groupBy { it.layer1 }
.mapValues { (_, deps) -> deps.mapNotNull { it.layer2 }.toSet() }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal class DependOnLayerThenDependentOnNothingRule : LayerDependenciesRule {
override fun validate(layerDependencies: Set<LayerDependency>) {
val layersWithDependencies =
layerDependencies
.filter { it.dependencyType == LayerDependencyType.DEPEND_ON_LAYER }
.filter { it.dependencyType == LayerDependencyType.DEPENDS_ON_LAYER }
.map { it.layer1 }

val conflictingDependencies =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal class DependentOnNothingThenDependOnLayerDependenciesRule : LayerDepend

val conflictingDependencies =
layerDependencies
.filter { it.dependencyType == LayerDependencyType.DEPEND_ON_LAYER && independentLayers.contains(it.layer1) }
.filter { it.dependencyType == LayerDependencyType.DEPENDS_ON_LAYER && independentLayers.contains(it.layer1) }

if (conflictingDependencies.isNotEmpty()) {
val violations =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,8 @@ class LayerDependenciesCoreTest {
// then
sut.layerDependencies shouldContainAll
setOf(
LayerDependency(layer1, LayerDependencyType.DEPEND_ON_LAYER, layer2),
LayerDependency(layer1, LayerDependencyType.DEPEND_ON_LAYER, layer3),
LayerDependency(layer1, LayerDependencyType.DEPENDS_ON_LAYER, layer2),
LayerDependency(layer1, LayerDependencyType.DEPENDS_ON_LAYER, layer3),
)
}

Expand Down Expand Up @@ -818,4 +818,128 @@ class LayerDependenciesCoreTest {
sut.layerDependencies shouldContain LayerDependency(layer2, LayerDependencyType.NONE, null)
}
// endregion

// region Collection<Layer> dependencies
@Test
fun `collection dependsOn single layer creates dependencies for all layers in collection`() {
// given
val sut = LayerDependenciesCore()
val sourceLayer1 = Layer("Domain", "com.example.domain..")
val sourceLayer2 = Layer("Data", "com.example.data..")
val targetLayer = Layer("Presentation", "com.example.presentation..")
val sourceLayers = setOf(sourceLayer1, sourceLayer2)

// when
with(sut) {
sourceLayers.dependsOn(targetLayer)
}

// then
sut.layerDependencies shouldContain LayerDependency(sourceLayer1, LayerDependencyType.DEPENDS_ON_LAYER, targetLayer)
sut.layerDependencies shouldContain LayerDependency(sourceLayer2, LayerDependencyType.DEPENDS_ON_LAYER, targetLayer)
}

@Test
fun `collection dependsOn set of layers creates dependencies for all combinations`() {
// given
val sut = LayerDependenciesCore()
val sourceLayer1 = Layer("Domain", "com.example.domain..")
val sourceLayer2 = Layer("Data", "com.example.data..")
val targetLayer1 = Layer("Presentation", "com.example.presentation..")
val targetLayer2 = Layer("Infrastructure", "com.example.infrastructure..")
val sourceLayers = setOf(sourceLayer1, sourceLayer2)
val targetLayers = setOf(targetLayer1, targetLayer2)

// when
with(sut) {
sourceLayers.dependsOn(targetLayers)
}

// then
sut.layerDependencies shouldContain LayerDependency(sourceLayer1, LayerDependencyType.DEPENDS_ON_LAYER, targetLayer1)
sut.layerDependencies shouldContain LayerDependency(sourceLayer1, LayerDependencyType.DEPENDS_ON_LAYER, targetLayer2)
sut.layerDependencies shouldContain LayerDependency(sourceLayer2, LayerDependencyType.DEPENDS_ON_LAYER, targetLayer1)
sut.layerDependencies shouldContain LayerDependency(sourceLayer2, LayerDependencyType.DEPENDS_ON_LAYER, targetLayer2)
}

@Test
fun `collection dependsOnNothing creates dependencies for all layers in collection`() {
// given
val sut = LayerDependenciesCore()
val sourceLayer1 = Layer("Domain", "com.example.domain..")
val sourceLayer2 = Layer("Data", "com.example.data..")
val sourceLayers = setOf(sourceLayer1, sourceLayer2)

// when
with(sut) {
sourceLayers.dependsOnNothing()
}

// then
sut.layerDependencies shouldContain LayerDependency(sourceLayer1, LayerDependencyType.DEPEND_ON_NOTHING, null)
sut.layerDependencies shouldContain LayerDependency(sourceLayer2, LayerDependencyType.DEPEND_ON_NOTHING, null)
}

@Test
fun `collection include adds all layers to layers collection`() {
// given
val sut = LayerDependenciesCore()
val layer1 = Layer("Domain", "com.example.domain..")
val layer2 = Layer("Data", "com.example.data..")
val layers = setOf(layer1, layer2)

// when
with(sut) {
layers.include()
}

// then
sut.layers shouldContain layer1
sut.layers shouldContain layer2
sut.layerDependencies shouldContain LayerDependency(layer1, LayerDependencyType.NONE, null)
sut.layerDependencies shouldContain LayerDependency(layer2, LayerDependencyType.NONE, null)
}

@Test
fun `collection doesNotDependOn single layer creates dependencies for all layers in collection`() {
// given
val sut = LayerDependenciesCore()
val sourceLayer1 = Layer("Domain", "com.example.domain..")
val sourceLayer2 = Layer("Data", "com.example.data..")
val targetLayer = Layer("Presentation", "com.example.presentation..")
val sourceLayers = setOf(sourceLayer1, sourceLayer2)

// when
with(sut) {
sourceLayers.doesNotDependOn(targetLayer)
}

// then
sut.layerDependencies shouldContain LayerDependency(sourceLayer1, LayerDependencyType.DOES_NOT_DEPEND_ON_LAYER, targetLayer)
sut.layerDependencies shouldContain LayerDependency(sourceLayer2, LayerDependencyType.DOES_NOT_DEPEND_ON_LAYER, targetLayer)
}

@Test
fun `collection doesNotDependOn set of layers creates dependencies for all combinations`() {
// given
val sut = LayerDependenciesCore()
val sourceLayer1 = Layer("Domain", "com.example.domain..")
val sourceLayer2 = Layer("Data", "com.example.data..")
val targetLayer1 = Layer("Presentation", "com.example.presentation..")
val targetLayer2 = Layer("Infrastructure", "com.example.infrastructure..")
val sourceLayers = setOf(sourceLayer1, sourceLayer2)
val targetLayers = setOf(targetLayer1, targetLayer2)

// when
with(sut) {
sourceLayers.doesNotDependOn(targetLayers)
}

// then
sut.layerDependencies shouldContain LayerDependency(sourceLayer1, LayerDependencyType.DOES_NOT_DEPEND_ON_LAYER, targetLayer1)
sut.layerDependencies shouldContain LayerDependency(sourceLayer1, LayerDependencyType.DOES_NOT_DEPEND_ON_LAYER, targetLayer2)
sut.layerDependencies shouldContain LayerDependency(sourceLayer2, LayerDependencyType.DOES_NOT_DEPEND_ON_LAYER, targetLayer1)
sut.layerDependencies shouldContain LayerDependency(sourceLayer2, LayerDependencyType.DOES_NOT_DEPEND_ON_LAYER, targetLayer2)
}
// endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class LayerDependencyTest {
val layer2 = Layer("layer2", "package2..")

// when / Then
LayerDependency(layer1, LayerDependencyType.DEPEND_ON_LAYER, layer2) // Should not throw
LayerDependency(layer1, LayerDependencyType.DEPENDS_ON_LAYER, layer2) // Should not throw
}

@Test
Expand All @@ -34,7 +34,7 @@ class LayerDependencyTest {
val layer1 = Layer("layer1", "package1..")

// when
val func = { LayerDependency(layer1, LayerDependencyType.DEPEND_ON_LAYER, null) }
val func = { LayerDependency(layer1, LayerDependencyType.DEPENDS_ON_LAYER, null) }

// then
func shouldThrow IllegalArgumentException::class withMessage "layer2 cannot be null when dependency type is DEPEND_ON_LAYER"
Expand Down
Loading
Loading