Skip to content

Instantly share code, notes, and snippets.

@philipjkim
Last active December 22, 2023 00:31
Show Gist options
  • Save philipjkim/f1644f7f6acccc5e6264fed7a025f960 to your computer and use it in GitHub Desktop.
Save philipjkim/f1644f7f6acccc5e6264fed7a025f960 to your computer and use it in GitHub Desktop.
package com.github.philipjkim.demo.hexagonal
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import com.tngtech.archunit.core.importer.ClassFileImporter
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.slf4j.LoggerFactory
/*
* Layer dependency direction:
* external -> adapter -> application -> domain
*/
@DisplayName("package dependency rule")
internal class DependencyRuleTest {
@Nested
@DisplayName("for callback domain")
inner class ForCallbackDomain {
private val basePackage = "com.github.philipjkim.demo.hexagonal.callback"
@Test
@DisplayName("domain layer should not depend on any of [application, adapter, external] layers.")
fun domainLayerDoesNotDependOnOuterLayers() {
assertDependencyRulesKeptFor(
basePackage,
"domain",
listOf("application", "adapter", "external"),
)
}
@Test
@DisplayName("application layer should not depend on any of [adapter, external] layers.")
fun applicationLayerDoesNotDependOnOuterLayers() {
assertDependencyRulesKeptFor(
basePackage,
"application",
listOf("adapter", "external"),
)
}
@Test
@DisplayName("adapter layer should not depend on external layer.")
fun adapterLayerDoesNotDependOnOuterLayers() {
assertDependencyRulesKeptFor(
basePackage,
"adapter",
listOf("external"),
)
}
}
@Nested
@DisplayName("for auth domain")
inner class ForAuthDomain {
private val basePackage = "com.github.philipjkim.demo.hexagonal.auth"
@Test
@DisplayName("domain layer should not depend on any of [application, adapter, external] layers.")
fun domainLayerDoesNotDependOnOuterLayers() {
assertDependencyRulesKeptFor(
basePackage,
"domain",
listOf("application", "adapter", "external"),
)
}
@Test
@DisplayName("application layer should not depend on any of [adapter, external] layers.")
fun applicationLayerDoesNotDependOnOuterLayers() {
assertDependencyRulesKeptFor(
basePackage,
"application",
listOf("adapter", "external"),
)
}
@Test
@DisplayName("adapter layer should not depend on external layer.")
fun adapterLayerDoesNotDependOnOuterLayers() {
assertDependencyRulesKeptFor(
basePackage,
"adapter",
listOf("external"),
)
}
}
/**
* Assert that all classes in `basePackage.innerPackage`
* do not depend on any classes in `basePackage.outerPackages`.
*
* @param basePackage base package of given domain (ex: `com.github.philipjkim.demo.hexagonal.callback`)
* @param innerPackage the inner package name which is the object of the test (ex: `domain`)
* @param outerPackages outer packages from the inner package (ex: `[application, adapter, external]`)
*/
private fun assertDependencyRulesKeptFor(
basePackage: String,
innerPackage: String,
outerPackages: List<String>,
) {
noClasses()
.that()
.resideInAPackage("${basePackage}.${innerPackage}..")
.should()
.dependOnClassesThat()
.resideInAnyPackage(
*(outerPackages.map { "${basePackage}.${it}.." }.toTypedArray())
)
.check(ClassFileImporter().importPackages("${basePackage}.."))
}
companion object {
@JvmStatic
@BeforeAll
fun setUp() {
val rootLogger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger
rootLogger.level = Level.INFO
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment