From 9290dc2af5f0a2d32d037e517270ea79c42c1dd9 Mon Sep 17 00:00:00 2001 From: Artyom Tsebrov Date: Fri, 14 Apr 2023 03:29:37 +0300 Subject: [PATCH 1/5] Add misc fixes --- build.gradle.kts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 01becba..126a2c4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ import org.jetbrains.kotlin.cli.common.toBooleanLenient plugins { kotlin("jvm") - id ("org.springframework.boot") apply false + id("org.springframework.boot") apply false // IntelliJ idea @@ -59,7 +59,6 @@ diktat { debug = false } - subprojects { println("Enabling Kotlin JVM plugin in project ${project.name}...") apply(plugin = "org.jetbrains.kotlin.jvm") @@ -82,7 +81,7 @@ subprojects { reports { txt.enabled = false xml.enabled = false - html{ + html { enabled = true destination = file("${project.buildDir}/reports/kotlin-detekt-${project.name}.html") } @@ -132,7 +131,7 @@ subprojects { dependencies { // use for @ConstructorBinding implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation ("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") } -- 2.40.1 From f20818a15e0d65095d9a8225fba37338b18c1c25 Mon Sep 17 00:00:00 2001 From: Artyom Tsebrov Date: Mon, 17 Apr 2023 21:48:12 +0300 Subject: [PATCH 2/5] Make detekt constraint less strict --- detekt-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detekt-config.yml b/detekt-config.yml index 95bcf79..903dea1 100644 --- a/detekt-config.yml +++ b/detekt-config.yml @@ -295,7 +295,7 @@ style: conversionFunctionPrefix: 'to' DestructuringDeclarationWithTooManyEntries: active: true - maxDestructuringEntries: 1 + maxDestructuringEntries: 5 EqualsNullCall: active: true ExplicitItLambdaParameter: -- 2.40.1 From f6be90db46f647c6dae145aa3c0f266d9121710b Mon Sep 17 00:00:00 2001 From: Artyom Tsebrov Date: Mon, 17 Apr 2023 21:50:34 +0300 Subject: [PATCH 3/5] Add ArchUnit module --- README.md | 90 +++++++++++++++++++ build.gradle.kts | 2 + codestyle-archunit/build.gradle.kts | 8 ++ .../archunit/ClassNamingArchRules.kt | 53 +++++++++++ .../archunit/ClassPackagingArchRules.kt | 56 ++++++++++++ settings.gradle.kts | 1 + 6 files changed, 210 insertions(+) create mode 100644 codestyle-archunit/build.gradle.kts create mode 100644 codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassNamingArchRules.kt create mode 100644 codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassPackagingArchRules.kt diff --git a/README.md b/README.md index 5937339..1b564fb 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,96 @@ Reports are stored in "$pwd/build/reports/diktat-report.html". Утилиты для тестирования репозиториев +## codestyle-archunit + +Набор правил для поддержки оформления архитектуры. + +Список доступных правил: + - `ru.touchin.codestyle.archunit.ClassNamingArchRules` + - `ru.touchin.codestyle.archunit.ClassPackagingArchRules` + +Как использовать совместно с плагином [ArchUnit Gradle Plugin](https://github.com/societe-generale/arch-unit-gradle-plugin): + +Groovy DSL: +```groovy +buildscript { + dependencies { + classpath "com.societegenerale.commons:arch-unit-gradle-plugin:3.0.0" + } +} +subprojects { + dependencyManagement { + dependencies { + dependency "com.tngtech.archunit:archunit:1.0.1" + } + } + + apply plugin: "java" + apply plugin: "com.societegenerale.commons.arch-unit-gradle-plugin" + + archUnit { + mainScopePath = "/classes/kotlin/main" + testScopePath = "/classes/kotlin/test" + + var applyType = applyOn("ru.touchin", "main") + configurableRules = [ + configurableRule( + "ru.touchin.codestyle.archunit.ClassNamingArchRules", + applyType, + ), + configurableRule( + "ru.touchin.codestyle.archunit.ClassPackagingArchRules", + applyType, + ), + ] + } + + dependencies { + archUnitExtraLib "ru.touchin:codestyle-archunit" + } +} +``` + +Kotlin DSL: +```kotlin +plugins { + id("com.societegenerale.commons.arch-unit-gradle-plugin") version "3.0.0" +} +subprojects { + configure { + dependencies { + dependency("com.tngtech.archunit:archunit:1.0.1") + } + } + + apply(plugin = "java") + apply(plugin = "com.societegenerale.commons.arch-unit-gradle-plugin") + + archUnit { + mainScopePath = "/classes/kotlin/main" + testScopePath = "/classes/kotlin/test" + + configurableRules = listOf( + "ru.touchin.codestyle.archunit.ClassNamingArchRules", + "ru.touchin.codestyle.archunit.ClassPackagingArchRules" + ).map { package -> + configurableRule( + package, + applyOn("ru.touchin", "main") + ) + } + } + + dependencies { + archUnitExtraLib(project(":codestyle-archunit")) + } +} +// clear action launch for root project +tasks.checkRules.configure { + actions.clear() +} +``` + ## logger Основные компоненты логирования: diff --git a/build.gradle.kts b/build.gradle.kts index 126a2c4..3cb9bce 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -106,6 +106,8 @@ subprojects { dependency("org.junit.jupiter:junit-jupiter-params:5.4.2") dependency("org.junit.jupiter:junit-jupiter-engine:5.4.2") + dependency("com.tngtech.archunit:archunit:1.0.1") + dependency("org.liquibase:liquibase-core:4.4.0") dependency("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0") diff --git a/codestyle-archunit/build.gradle.kts b/codestyle-archunit/build.gradle.kts new file mode 100644 index 0000000..920175b --- /dev/null +++ b/codestyle-archunit/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + id("kotlin") +} + +dependencies { + implementation(project(":common")) + implementation("com.tngtech.archunit:archunit") +} diff --git a/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassNamingArchRules.kt b/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassNamingArchRules.kt new file mode 100644 index 0000000..8baa8a3 --- /dev/null +++ b/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassNamingArchRules.kt @@ -0,0 +1,53 @@ +package ru.touchin.codestyle.archunit + +import com.tngtech.archunit.lang.ArchRule +import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes +import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods +import ru.touchin.common.exceptions.CommonException + +/** + * Set of rules that defines constraint for classes naming. + */ +@Suppress("unused") +object ClassNamingArchRules { + + + val CLASSES_WHICH_HAVE_SCHEDULED_METHODS_MUST_HAVE_SUFFIX: ArchRule = methods() + .that() + .areAnnotatedWith("org.springframework.scheduling.annotation.Scheduled") + .should().beDeclaredInClassesThat().haveSimpleNameEndingWith("Job") + .allowEmptyShould(true) + .because("Classes that use 'Scheduled' annotation must have name with 'Job' suffix") + + val ANNOTATED_SERVICE_CLASSES_MUST_HAVE_SUFFIX: ArchRule = methods() + .that().areAnnotatedWith("org.springframework.stereotype.Service") + .should().beDeclaredInClassesThat().haveSimpleNameContaining("Service") + .allowEmptyShould(true) + .because("Classes that use 'Service' annotation are assignable to class with `Service` suffix") + + val ANNOTATED_ENTITY_CLASSES_MUST_HAVE_SUFFIX: ArchRule = classes() + .that().areAnnotatedWith("javax.persistence.Entity") + .should().haveSimpleNameEndingWith("Entity") + .allowEmptyShould(true) + .because("Hibernate 'Entity' classes must have name with 'Entity' suffix") + + val EXCEPTION_CLASSES_MUST_HAVE_SUFFIX: ArchRule = classes() + .that().areAssignableTo(Exception::class.java) + .or().areAssignableTo(CommonException::class.java) + .should().haveSimpleNameEndingWith("Exception") + .allowEmptyShould(true) + .because("'Exception' classes must have name with 'Exception' suffix") + + val JPAREPOSITORY_CLASSES_MUST_HAVE_SUFFIX: ArchRule = classes() + .that().areAssignableTo("org.springframework.data.jpa.repository.JpaRepository") + .should().haveSimpleNameEndingWith("Repository") + .allowEmptyShould(true) + .because("Classes that use 'JpaRepository' must have name with 'Repository' suffix") + + val WEBCLIENT_CLASSES_MUST_HAVE_SUFFIX: ArchRule = classes() + .that().haveFullyQualifiedName("org.springframework.web.reactive.valction.client.WebClient") + .should().onlyBeAccessed().byClassesThat().haveSimpleNameEndingWith("WebClient") + .allowEmptyShould(true) + .because("Classes that use Spring 'WebClient' must have name with 'WebClient' suffix") + +} diff --git a/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassPackagingArchRules.kt b/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassPackagingArchRules.kt new file mode 100644 index 0000000..76eb43b --- /dev/null +++ b/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassPackagingArchRules.kt @@ -0,0 +1,56 @@ +package ru.touchin.codestyle.archunit + +import com.tngtech.archunit.lang.ArchRule +import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes + +/** + * Set of rules that defines constraint for classes placement. + */ +@Suppress("unused") +object ClassPackagingArchRules { + + + val IMPL_CLASSES_ARE_IN_CORRESPONDING_PACKAGE: ArchRule = classes() + .that().haveSimpleNameEndingWith("Impl") + .should().resideInAPackage("..impl..") + .allowEmptyShould(true) + .because("Implementations of interfaces must reside in package 'impl'") + + + val ENTITIES_ARE_IN_CORRESPONDING_PACKAGE: ArchRule = classes() + .that().haveSimpleNameEndingWith("Entity") + .should().resideInAPackage("..models..") + .allowEmptyShould(true) + .because("'Entity' must reside in package 'models'") + + val REPOSITORIES_ARE_IN_CORRESPONDING_PACKAGE: ArchRule = classes() + .that().haveSimpleNameEndingWith("Repository") + .should().resideInAPackage("..repositories..") + .allowEmptyShould(true) + .because("'Repository' must reside in package 'repositories'") + + val CORE_SERVICES_ARE_IN_CORRESPONDING_PACKAGE: ArchRule = classes() + .that().haveSimpleNameEndingWith("CoreService") + .should().resideInAPackage("..services..") + .allowEmptyShould(true) + .because("'CoreService' must reside in package 'services'") + + val CONVERTERS_ARE_IN_CORRESPONDING_PACKAGE: ArchRule = classes() + .that().haveSimpleNameEndingWith("Converter") + .should().resideInAPackage("..converters..") + .allowEmptyShould(true) + .because("'Converter' must reside in package 'converters'") + + val EXCEPTIONS_ARE_IN_CORRESPONDING_PACKAGE: ArchRule = classes() + .that().haveSimpleNameEndingWith("Exception") + .should().resideInAPackage("..exceptions..") + .allowEmptyShould(true) + .because("'Exception' must reside in package 'exceptions'") + + val ENUM_CLASSES_ARE_IN_CORRESPONDING_PACKAGE: ArchRule = classes() + .that().areEnums() + .should().resideInAPackage("..enums..") + .allowEmptyShould(true) + .because("'Enum' must reside in package 'enums'") + +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 8fba89c..3851799 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -39,6 +39,7 @@ include("common-geo") include("common-geo-spatial4j-spring") include("common-messaging") include("captcha") +include("codestyle-archunit") include("logger") include("logger-spring") include("logger-spring-web") -- 2.40.1 From 2fce7e72b40975ccf4d659d6ca3363e19a2e928e Mon Sep 17 00:00:00 2001 From: Artyom Tsebrov Date: Mon, 17 Apr 2023 22:02:13 +0300 Subject: [PATCH 4/5] Update docs --- README.md | 40 ++++++++++++------- .../{ => rules}/ClassNamingArchRules.kt | 2 +- .../{ => rules}/ClassPackagingArchRules.kt | 2 +- 3 files changed, 27 insertions(+), 17 deletions(-) rename codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/{ => rules}/ClassNamingArchRules.kt (98%) rename codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/{ => rules}/ClassPackagingArchRules.kt (97%) diff --git a/README.md b/README.md index 1b564fb..6bee2d6 100644 --- a/README.md +++ b/README.md @@ -85,11 +85,17 @@ Reports are stored in "$pwd/build/reports/diktat-report.html". Набор правил для поддержки оформления архитектуры. -Список доступных правил: - - `ru.touchin.codestyle.archunit.ClassNamingArchRules` - - `ru.touchin.codestyle.archunit.ClassPackagingArchRules` +#### Список доступных правил + - `ru.touchin.codestyle.archunit.rules.ClassNamingArchRules` + - `ru.touchin.codestyle.archunit.rules.ClassPackagingArchRules` -Как использовать совместно с плагином [ArchUnit Gradle Plugin](https://github.com/societe-generale/arch-unit-gradle-plugin): + +#### Gradle plugin +Настройка и применение совместно с [ArchUnit Gradle Plugin](https://github.com/societe-generale/arch-unit-gradle-plugin). + +Действие `checkRules` для проверки соответствие правилам запускается при операциях сборки, по умолчанию. +Вручную можно вызвать командой ``gradle :checkRules`` для нужного модуля. +Добавить его можно следующим образом на примере установки в рутовый gradle.build проекта: Groovy DSL: ```groovy @@ -109,24 +115,24 @@ subprojects { apply plugin: "com.societegenerale.commons.arch-unit-gradle-plugin" archUnit { - mainScopePath = "/classes/kotlin/main" - testScopePath = "/classes/kotlin/test" + mainScopePath = "/classes/kotlin/main" // or "/classes/java/main" + testScopePath = "/classes/kotlin/test" // or /classes/java/test" var applyType = applyOn("ru.touchin", "main") configurableRules = [ configurableRule( - "ru.touchin.codestyle.archunit.ClassNamingArchRules", + "ru.touchin.codestyle.archunit.rules.ClassNamingArchRules", applyType, ), configurableRule( - "ru.touchin.codestyle.archunit.ClassPackagingArchRules", + "ru.touchin.codestyle.archunit.rules.ClassPackagingArchRules", applyType, ), ] } dependencies { - archUnitExtraLib "ru.touchin:codestyle-archunit" + archUnitExtraLib "ru.touchin:codestyle-archunit" // or archUnitExtraLib project(":codestyle-archunit") } } ``` @@ -147,12 +153,12 @@ subprojects { apply(plugin = "com.societegenerale.commons.arch-unit-gradle-plugin") archUnit { - mainScopePath = "/classes/kotlin/main" - testScopePath = "/classes/kotlin/test" + mainScopePath = "/classes/kotlin/main" // or "/classes/java/main" + testScopePath = "/classes/kotlin/test" // or /classes/java/test" configurableRules = listOf( - "ru.touchin.codestyle.archunit.ClassNamingArchRules", - "ru.touchin.codestyle.archunit.ClassPackagingArchRules" + "ru.touchin.codestyle.archunit.rules.ClassNamingArchRules", + "ru.touchin.codestyle.archunit.rules.ClassPackagingArchRules" ).map { package -> configurableRule( package, @@ -162,10 +168,14 @@ subprojects { } dependencies { - archUnitExtraLib(project(":codestyle-archunit")) + archUnitExtraLib("ru.touchin:codestyle-archunit") // or archUnitExtraLib(project(":codestyle-archunit")) } } -// clear action launch for root project +``` + +Отключить проверки на таске помимо конфигурирования `configurableRule` можно также таким образом: +```kotlin +// clear action launch for root project to avoid exception tasks.checkRules.configure { actions.clear() } diff --git a/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassNamingArchRules.kt b/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/rules/ClassNamingArchRules.kt similarity index 98% rename from codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassNamingArchRules.kt rename to codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/rules/ClassNamingArchRules.kt index 8baa8a3..bf4e256 100644 --- a/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassNamingArchRules.kt +++ b/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/rules/ClassNamingArchRules.kt @@ -1,4 +1,4 @@ -package ru.touchin.codestyle.archunit +package ru.touchin.codestyle.archunit.rules import com.tngtech.archunit.lang.ArchRule import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes diff --git a/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassPackagingArchRules.kt b/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/rules/ClassPackagingArchRules.kt similarity index 97% rename from codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassPackagingArchRules.kt rename to codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/rules/ClassPackagingArchRules.kt index 76eb43b..8ec8a3a 100644 --- a/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/ClassPackagingArchRules.kt +++ b/codestyle-archunit/src/main/kotlin/ru/touchin/codestyle/archunit/rules/ClassPackagingArchRules.kt @@ -1,4 +1,4 @@ -package ru.touchin.codestyle.archunit +package ru.touchin.codestyle.archunit.rules import com.tngtech.archunit.lang.ArchRule import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes -- 2.40.1 From 0cec8e614766f7ebea0d3da4502419dda07b7b6b Mon Sep 17 00:00:00 2001 From: Artyom Tsebrov Date: Mon, 17 Apr 2023 22:03:36 +0300 Subject: [PATCH 5/5] Misc fix --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6bee2d6..2b03846 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ subprojects { archUnit { mainScopePath = "/classes/kotlin/main" // or "/classes/java/main" - testScopePath = "/classes/kotlin/test" // or /classes/java/test" + testScopePath = "/classes/kotlin/test" // or "/classes/java/test" var applyType = applyOn("ru.touchin", "main") configurableRules = [ @@ -154,7 +154,7 @@ subprojects { archUnit { mainScopePath = "/classes/kotlin/main" // or "/classes/java/main" - testScopePath = "/classes/kotlin/test" // or /classes/java/test" + testScopePath = "/classes/kotlin/test" // or "/classes/java/test" configurableRules = listOf( "ru.touchin.codestyle.archunit.rules.ClassNamingArchRules", -- 2.40.1