-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bb59011
commit b58a090
Showing
7 changed files
with
385 additions
and
1 deletion.
There are no files selected for viewing
100 changes: 100 additions & 0 deletions
100
api/src/test/kotlin/com/few/api/domain/ApiDomainArchitectureSpec.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package com.few.api.domain | ||
|
||
import com.tngtech.archunit.core.domain.JavaClasses | ||
import com.tngtech.archunit.core.importer.ClassFileImporter | ||
import com.tngtech.archunit.library.Architectures.layeredArchitecture | ||
import org.junit.jupiter.api.BeforeAll | ||
import org.junit.jupiter.api.Test | ||
|
||
class ApiDomainArchitectureSpec { | ||
companion object { | ||
var apiDomainClasses: JavaClasses? = null | ||
|
||
@BeforeAll | ||
@JvmStatic | ||
fun setup() { | ||
apiDomainClasses = ClassFileImporter().importPackages( | ||
"com.few.api.domain.article", | ||
"com.few.api.domain.log", | ||
"com.few.api.domain.member", | ||
"com.few.api.domain.problem", | ||
"com.few.api.domain.subscription", | ||
"com.few.api.domain.workbook" | ||
) | ||
} | ||
} | ||
|
||
@Test | ||
fun `Controller 레이어는 다른 레이어에서 접근할 수 없어야 한다`() { | ||
val rule = layeredArchitecture() | ||
.layer("controller").definedBy("..controller..") | ||
.whereLayer("controller").mayNotBeAccessedByAnyLayer() | ||
|
||
rule.check(apiDomainClasses) | ||
} | ||
|
||
@Test | ||
fun `Usecase 레이어는 Controller 레이어에서만 접근 가능하다`() { | ||
val rule = layeredArchitecture() | ||
.layer("usecase").definedBy("..usecase..") | ||
.layer("controller").definedBy("..controller..") | ||
.whereLayer("usecase").mayOnlyBeAccessedByLayers("controller") | ||
|
||
rule.check(apiDomainClasses) | ||
} | ||
|
||
@Test | ||
fun `Service 레이어는 Usecase와 Event 레이어에서만 접근 가능하다`() { | ||
val rule = layeredArchitecture() | ||
.layer("service").definedBy("..service..") | ||
.layer("usecase").definedBy("..usecase..") | ||
.layer("event").definedBy("..event..") | ||
.whereLayer("service").mayOnlyBeAccessedByLayers("usecase", "event") | ||
|
||
rule.check(apiDomainClasses) | ||
} | ||
|
||
@Test | ||
fun `Repo 레이어는 Usecase, Service, Event 레이어에서만 접근 가능하다`() { | ||
val rule = layeredArchitecture() | ||
.layer("repo").definedBy("..repo..") | ||
.layer("usecase").definedBy("..usecase..") | ||
.layer("service").definedBy("..service..") | ||
.layer("event").definedBy("..event..") | ||
.whereLayer("repo").mayOnlyBeAccessedByLayers("usecase", "service", "event") | ||
|
||
rule.check(apiDomainClasses) | ||
} | ||
|
||
@Test | ||
fun `Event 레이어는 Usecase 레이어에서만 접근 가능하다`() { | ||
val rule = layeredArchitecture() | ||
.layer("event").definedBy("..event..") | ||
.layer("usecase").definedBy("..usecase..") | ||
.whereLayer("event").mayOnlyBeAccessedByLayers("usecase") | ||
|
||
rule.check(apiDomainClasses) | ||
} | ||
|
||
@Test | ||
fun `Email 레이어는 UseCase, Service 레이어에서만 접근 가능하다`() { | ||
val rule = layeredArchitecture() | ||
.layer("email").definedBy("..email..") | ||
.layer("usecase").definedBy("..usecase..") | ||
.layer("service").definedBy("..service..") | ||
.whereLayer("email").mayOnlyBeAccessedByLayers("usecase", "service") | ||
|
||
rule.check(apiDomainClasses) | ||
} | ||
|
||
@Test | ||
fun `Client 레이어는 UseCase, Event 레이어에서만 접근 가능하다`() { | ||
val rule = layeredArchitecture() | ||
.layer("client").definedBy("..client..") | ||
.layer("usecase").definedBy("..usecase..") | ||
.layer("event").definedBy("..event..") | ||
.whereLayer("client").mayOnlyBeAccessedByLayers("usecase", "event") | ||
|
||
rule.check(apiDomainClasses) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
repo/src/test/kotlin/architecture/RepoModuleArchitectureSpec.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package architecture | ||
|
||
import com.tngtech.archunit.core.domain.JavaClasses | ||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes | ||
import org.junit.jupiter.api.Test | ||
import org.springframework.context.annotation.Configuration | ||
import com.tngtech.archunit.core.importer.ClassFileImporter | ||
import com.tngtech.archunit.lang.ArchRule | ||
import org.junit.jupiter.api.BeforeAll | ||
|
||
class RepoModuleArchitectureSpec { | ||
companion object { | ||
var repoClasses: JavaClasses? = null | ||
|
||
@BeforeAll | ||
@JvmStatic | ||
fun setup() { | ||
repoClasses = ClassFileImporter().importPackages("repo") | ||
} | ||
} | ||
|
||
@Test | ||
fun `repo 모듈의 설정 클래스는 config 패키지에 존재합니다`() { | ||
val rule: ArchRule = classes() | ||
.that() | ||
.resideInAPackage("repo") | ||
.and().haveNameNotMatching(".*Companion*.") | ||
.and().areAnnotatedWith(Configuration::class.java) | ||
.should() | ||
.resideInAPackage("repo.config") | ||
|
||
rule.check(repoClasses) | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
security/src/test/kotlin/architecture/SecurityModuleArchitectureSpec.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package architecture | ||
|
||
import com.tngtech.archunit.core.domain.JavaClasses | ||
import com.tngtech.archunit.core.importer.ClassFileImporter | ||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes | ||
import org.junit.jupiter.api.BeforeAll | ||
import org.junit.jupiter.api.Test | ||
import org.springframework.context.annotation.Configuration | ||
|
||
class SecurityModuleArchitectureSpec { | ||
companion object { | ||
var securityClasses: JavaClasses? = null | ||
|
||
@BeforeAll | ||
@JvmStatic | ||
fun setup() { | ||
securityClasses = ClassFileImporter().importPackages("security") | ||
} | ||
} | ||
|
||
@Test | ||
fun `security 모듈의 설정 클래스는 config 패키지에 존재합니다`() { | ||
val rule = classes() | ||
.that() | ||
.resideInAPackage("security") | ||
.and().haveNameNotMatching(".*Companion*.") | ||
.and().areAnnotatedWith(Configuration::class.java) | ||
.should() | ||
.resideInAPackage("security.config") | ||
|
||
rule.check(securityClasses) | ||
} | ||
} |
154 changes: 154 additions & 0 deletions
154
storage/src/test/kotlin/architecture/StorageModuleArchitectureSpec.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
package architecture | ||
|
||
import com.tngtech.archunit.base.DescribedPredicate | ||
import com.tngtech.archunit.core.domain.JavaClasses | ||
import com.tngtech.archunit.core.importer.ClassFileImporter | ||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes | ||
import org.junit.jupiter.api.BeforeAll | ||
import org.junit.jupiter.api.Nested | ||
import org.junit.jupiter.api.Test | ||
import org.springframework.context.annotation.Configuration | ||
import storage.GetPreSignedObjectUrlProvider | ||
import storage.PutObjectProvider | ||
import storage.RemoveObjectProvider | ||
|
||
class StorageModuleArchitectureSpec { | ||
companion object { | ||
var storageClasses: JavaClasses? = null | ||
|
||
@BeforeAll | ||
@JvmStatic | ||
fun setup() { | ||
storageClasses = ClassFileImporter().importPackages("storage") | ||
} | ||
} | ||
|
||
@Test | ||
fun `storage 모듈의 설정 클래스는 config 패키지에 존재합니다`() { | ||
val rule = classes() | ||
.that() | ||
.resideInAPackage("storage") | ||
.and().haveNameNotMatching(".*Companion*.") | ||
.and().resideOutsideOfPackages("storage.document", "storage.image") | ||
.and().areAnnotatedWith(Configuration::class.java) | ||
.should() | ||
.resideInAPackage("storage.config") | ||
|
||
rule.check(storageClasses) | ||
} | ||
|
||
@Test | ||
fun `storage 모듈의 object의 url을 제공하기 위한 provider는 GetPreSignedObjectUrlProvider를 구현해야합니다`() { | ||
val rule = classes() | ||
.that() | ||
.resideInAPackage("storage.document") | ||
.or().resideInAPackage("storage.image") | ||
.and().haveSimpleNameStartingWith("Get") | ||
.and().haveSimpleNameEndingWith("Provider") | ||
.and().haveNameNotMatching(".*Companion*.") | ||
.and().areNotInterfaces() | ||
.should() | ||
.implement( | ||
DescribedPredicate.describe( | ||
"GetPreSignedObjectUrlProvider를 구현해야합니다" | ||
) { clazz -> | ||
clazz.interfaces.javaClass.interfaces.contains(GetPreSignedObjectUrlProvider::class.java) | ||
} | ||
) | ||
|
||
rule.check(storageClasses) | ||
} | ||
|
||
@Test | ||
fun `storage 모듈의 object를 수정하기 위한 provider는 PutObjectProvider를 구현해야합니다`() { | ||
val rule = classes() | ||
.that() | ||
.resideInAPackage("storage.document") | ||
.or().resideInAPackage("storage.image") | ||
.and().haveSimpleNameStartingWith("Put") | ||
.and().haveSimpleNameEndingWith("Provider") | ||
.and().haveNameNotMatching(".*Companion*.") | ||
.and().areNotInterfaces() | ||
.should() | ||
.implement( | ||
DescribedPredicate.describe( | ||
"PutObjectProvider를 구현해야합니다" | ||
) { clazz -> | ||
clazz.interfaces.javaClass.interfaces.contains(PutObjectProvider::class.java) | ||
} | ||
) | ||
|
||
rule.check(storageClasses) | ||
} | ||
|
||
@Test | ||
fun `storage 모듈의 object를 삭제하기 위한 provider는 RemoveObjectProvider를 구현해야합니다`() { | ||
val rule = classes() | ||
.that() | ||
.resideInAPackage("storage.document") | ||
.or().resideInAPackage("storage.image") | ||
.and().haveSimpleNameStartingWith("Remove") | ||
.and().haveSimpleNameEndingWith("Provider") | ||
.and().haveNameNotMatching(".*Companion*.") | ||
.and().areNotInterfaces() | ||
.should() | ||
.implement( | ||
DescribedPredicate.describe( | ||
"RemoveObjectProvider를 구현해야합니다" | ||
) { clazz -> | ||
clazz.interfaces.javaClass.interfaces.contains(RemoveObjectProvider::class.java) | ||
} | ||
) | ||
|
||
rule.check(storageClasses) | ||
} | ||
|
||
@Test | ||
fun `client 패키지의 client 클래스는 provider 패키지의 provider 클래스에서만 사용되어야 합니다`() { | ||
val rule = classes() | ||
.that() | ||
.resideInAPackage("storage.*.client") | ||
.and().haveSimpleNameEndingWith("Client") | ||
.and().haveNameNotMatching(".*Companion*.") | ||
.should() | ||
.onlyBeAccessed().byAnyPackage( | ||
"storage.*.client", | ||
"storage.*.provider.*", | ||
"storage.*.config" | ||
) | ||
|
||
rule.check(storageClasses) | ||
} | ||
|
||
@Nested | ||
inner class DocumentArchitectureSpec { | ||
@Test | ||
fun `document 패키지의 설정 클래스는 config 패키지에 존재합니다`() { | ||
val rule = classes() | ||
.that() | ||
.resideInAPackage("storage.document") | ||
.and().haveNameNotMatching(".*Companion*.") | ||
.and().areAnnotatedWith(Configuration::class.java) | ||
.should() | ||
.resideInAPackage("storage.document.config") | ||
|
||
rule.check(storageClasses) | ||
} | ||
} | ||
|
||
@Nested | ||
inner class ImageArchitectureSpec { | ||
@Test | ||
fun `image 패키지의 설정 클래스는 config 패키지에 존재합니다`() { | ||
val rule = classes() | ||
.that() | ||
.resideInAPackage("storage.image") | ||
.and().haveNameNotMatching(".*Companion*.") | ||
.and().areAnnotatedWith(Configuration::class.java) | ||
.should() | ||
.resideInAPackage("storage.image.config") | ||
|
||
rule.check(storageClasses) | ||
} | ||
} | ||
} |
Oops, something went wrong.