Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
350da3e
Added first draft for C2:
vlad961 Feb 21, 2023
5997389
Added first draft for C3:
vlad961 Feb 21, 2023
2a1ac45
Added first C4 draft.
vlad961 Feb 22, 2023
845c130
Added new draft for C4Layer2Logic4ComponentTest
vlad961 Feb 27, 2023
f1942b6
First commits of rule C5 - C7
NicolasVanBellen Feb 27, 2023
6bb5fb1
First commits for rules C5 - C7
NicolasVanBellen Feb 27, 2023
5cca862
Merge branch 'add-component-rules' of https://github.com/NicolasVanBe…
NicolasVanBellen Feb 27, 2023
796411d
almost final version of rules C5 - C7
NicolasVanBellen Feb 27, 2023
1245dcb
Refactored C4 Rule.
vlad961 Feb 28, 2023
c756ba7
Refactored C3:
vlad961 Feb 28, 2023
604f310
Refactored C2
vlad961 Feb 28, 2023
e3cb032
Refactoring C4 Rule + Delete C2 Rule
vlad961 Mar 3, 2023
520c8ef
Merge branch 'devonfw-sample:master' into add-component-rules
vlad961 Mar 3, 2023
8706f9b
Update ComponentRuleC4LayerService2Logic4ComponentTest.java
vlad961 Mar 6, 2023
7181db3
Merge branch 'vlad961:add-component-rules' into add-component-rules
NicolasVanBellen Mar 6, 2023
95e5e6c
Output changes
NicolasVanBellen Mar 6, 2023
f09430d
Merge pull request #2 from NicolasVanBellen/add-component-rules
vlad961 Mar 6, 2023
2ad2e67
Refactored C3 and C6
vlad961 Mar 6, 2023
b1dc513
Merge branch 'devonfw-sample:master' into add-component-rules
vlad961 Mar 13, 2023
9d3b6ad
Merged all component rules into one file.
vlad961 Mar 13, 2023
e9fb527
Update ComponentRuleTest.java
vlad961 Mar 13, 2023
4d5f159
Merge branch 'devonfw-sample:master' into add-component-rules
vlad961 Mar 14, 2023
ee69830
Reworked Component Rules
vlad961 Mar 14, 2023
6eaa74d
Merge branch 'master' into add-component-rules
hohwille Mar 17, 2023
32b26e3
Merge branch 'master' into add-component-rules
hohwille Mar 20, 2023
60c4b38
Update ComponentRuleTest.java
vlad961 Mar 20, 2023
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
284 changes: 284 additions & 0 deletions src/test/java/com/devonfw/sample/archunit/ComponentRuleTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
package com.devonfw.sample.archunit;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.Dependency;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.ConditionEvents;
import com.tngtech.archunit.lang.SimpleConditionEvent;

@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class)
public class ComponentRuleTest {

private static DescribedPredicate<JavaClass> resideInServiceLayerOfAComponent = new DescribedPredicate<JavaClass>(
"lie inside the service layer of a custom component different from the business architectures default general component") {
@Override
public boolean test(JavaClass input) {

PackageStructure inputPkg = PackageStructure.of(input);
boolean someCustomComponentServiceClass = inputPkg.isLayerService() && !inputPkg.isComponentGeneral()
&& inputPkg.isValid();
return someCustomComponentServiceClass;
}
};

private static DescribedPredicate<JavaClass> resideInLogicLayerOfAComponent = new DescribedPredicate<JavaClass>(
"lie inside the logic layer of a custom component different from the business architectures default general component") {
@Override
public boolean test(JavaClass input) {

PackageStructure inputPkg = PackageStructure.of(input);
boolean someCustomComponentLogicClass = inputPkg.isLayerLogic() && !inputPkg.isComponentGeneral()
&& inputPkg.isValid();
return someCustomComponentLogicClass;
}
};

private static DescribedPredicate<JavaClass> resideInDataaccessLayerOfAComponent = new DescribedPredicate<JavaClass>(
"lie inside the dataaccess layer of a custom component different from the business architectures default general component") {
@Override
public boolean test(JavaClass input) {

PackageStructure inputPkg = PackageStructure.of(input);
boolean someCustomComponentDataaccessClass = inputPkg.isLayerDataAccess() && !inputPkg.isComponentGeneral()
&& inputPkg.isValid();
return someCustomComponentDataaccessClass;
}
};

private static DescribedPredicate<JavaClass> resideInBatchLayerOfAComponent = new DescribedPredicate<JavaClass>(
"lie inside the batch layer of a custom component different from the business architectures default general component") {
@Override
public boolean test(JavaClass input) {

PackageStructure inputPkg = PackageStructure.of(input);
boolean someCustomComponentBatchClass = inputPkg.isLayerBatch() && !inputPkg.isComponentGeneral()
&& inputPkg.isValid();
return someCustomComponentBatchClass;
}
};

private static DescribedPredicate<JavaClass> resideInTheGeneralProjectComponent = new DescribedPredicate<JavaClass>(
"lie inside the business architectures default general component") {
@Override
public boolean test(JavaClass input) {

PackageStructure inputPkg = PackageStructure.of(input);
boolean someGeneralComponentClass = inputPkg.isComponentGeneral() && inputPkg.isValid();
return someGeneralComponentClass;
}
};

public static final ArchCondition<JavaClass> dependOnDiffCustomComponents = new ArchCondition<JavaClass>(
"depend on a different custom component") {
@Override
public void check(JavaClass sourceClass, ConditionEvents events) {

PackageStructure sourcePkg = PackageStructure.of(sourceClass);

// Check for noncompliant dependencies towards other custom components'.
for (Dependency dependency : sourceClass.getDirectDependenciesFromSelf()) {

JavaClass targetClass = dependency.getTargetClass();
PackageStructure targetPkg = PackageStructure.of(targetClass);
boolean isAllowedDependency = isDependingOnAnotherCustomComponent(sourcePkg, targetPkg);

if (!isAllowedDependency) {
String message = composeViolationMessage(sourceClass, targetClass, sourcePkg, targetPkg);
events.add(new SimpleConditionEvent(sourceClass, true, message));
}
}
}
};

public static final ArchCondition<JavaClass> dependOnDiffComponentsServiceLayerClasses = new ArchCondition<JavaClass>(
"depend on the service layer of a different custom component") {
@Override
public void check(JavaClass sourceClass, ConditionEvents events) {

PackageStructure sourcePkg = PackageStructure.of(sourceClass);

// Check for noncompliant dependencies towards other components' service layers.
for (Dependency dependency : sourceClass.getDirectDependenciesFromSelf()) {

JavaClass targetClass = dependency.getTargetClass();
PackageStructure targetPkg = PackageStructure.of(targetClass);
boolean isAllowedDependency = isDependingOnAnotherComponentsServiceLayer(sourcePkg, targetPkg);

if (!isAllowedDependency) {
String message = composeViolationMessage(sourceClass, targetClass, sourcePkg, targetPkg);
events.add(new SimpleConditionEvent(sourceClass, true, message));
}
}
}
};

public static final ArchCondition<JavaClass> dependOnDiffComponentsLogicLayer = new ArchCondition<JavaClass>(
"depend on the logic layer of a different custom component") {
@Override
public void check(JavaClass sourceClass, ConditionEvents events) {

PackageStructure sourcePkg = PackageStructure.of(sourceClass);

// Check for noncompliant dependencies towards other components' logic layers.
for (Dependency dependency : sourceClass.getDirectDependenciesFromSelf()) {

JavaClass targetClass = dependency.getTargetClass();
PackageStructure targetPkg = PackageStructure.of(targetClass);
boolean isAllowedDependency = isDependingOnAnotherComponentsLogicLayer(sourcePkg, targetPkg);

if (!isAllowedDependency) {
String message = composeViolationMessage(sourceClass, targetClass, sourcePkg, targetPkg);
events.add(new SimpleConditionEvent(sourceClass, true, message));
}
}
}
};

public static final ArchCondition<JavaClass> dependOnDiffComponentsDataaccessLayer = new ArchCondition<JavaClass>(
"depend on the dataacces layer of a different custom component") {
@Override
public void check(JavaClass sourceClass, ConditionEvents events) {

PackageStructure sourcePkg = PackageStructure.of(sourceClass);

// Check for noncompliant dependencies towards other components' dataaccess
// layers.
for (Dependency dependency : sourceClass.getDirectDependenciesFromSelf()) {

JavaClass targetClass = dependency.getTargetClass();
PackageStructure targetPkg = PackageStructure.of(targetClass);
boolean isAllowedDependency = isDependingOnAnotherComponentsDataaccessLayer(sourcePkg, targetPkg);

if (!isAllowedDependency) {
String message = composeViolationMessage(sourceClass, targetClass, sourcePkg, targetPkg);
events.add(new SimpleConditionEvent(sourceClass, true, message));
}
}
}
};

/**
* verifying that the service layer of one component does not depend on the service layer of another component.
*/
@ArchTest
public static final ArchRule noComponentsServiceLayerDependsOnTheServiceLayerOfAnotherComponent = noClasses()
.that(resideInServiceLayerOfAComponent).should(dependOnDiffComponentsServiceLayerClasses)
.as("Code from service layer of a component shall not depend on service layer of a different component.")
.allowEmptyShould(true);

/**
* verifying that the service layer of one component does not depend on the logic layer of another component.
*/
@ArchTest
public static final ArchRule noComponentsServiceLayerDependsOnTheLogicLayerOfAnotherComponent = noClasses()
.that(resideInServiceLayerOfAComponent).should(dependOnDiffComponentsLogicLayer)
.as("Code from service layer of a component shall not depend on logic layer of a different component.")
.allowEmptyShould(true);

/**
* verifying that the logic layer of a component may not depend on the dataaccess layer of another component.
*/
@ArchTest
public static final ArchRule noComponentsLogicLayerDependsOnTheDataaccessLayerOfAnotherComponent = noClasses()
.that(resideInLogicLayerOfAComponent).should(dependOnDiffComponentsDataaccessLayer)
.as("Code from logic layer of a component shall not depend on dataaccess layer of a different component.")
.allowEmptyShould(true);

// medium severity
/**
* verifying that the dataaccess layer of one component does not depend on the dataaccess layer of another component.
*/
@ArchTest
public static final ArchRule noComponentsDataaccessLayerDependsOnTheDataaccessLayerOfAnotherComponent = noClasses()
.that(resideInDataaccessLayerOfAComponent).should(dependOnDiffComponentsDataaccessLayer)
.as("Code from dataaccess layer shall not depend on dataaccess layer of a different component.")
.allowEmptyShould(true);

/**
* verifying that the batch layer of a component may not depend on the logic layer of another component.
*/
@ArchTest
public static final ArchRule noComponentsBatchLayerDependsOnTheLogicLayerOfAnotherComponent = noClasses()
.that(resideInBatchLayerOfAComponent).should(dependOnDiffComponentsLogicLayer)
.as("Code from batch layer of a component shall not depend on logic layer of a different component.")
.allowEmptyShould(true);

/**
* verifying that the business architectures default general component does not depend on any other component.
*/
@ArchTest
public static ArchRule theDefaultProjectComponentDoesNotDependOnAnyOtherComponent = noClasses()
.that(resideInTheGeneralProjectComponent).should(dependOnDiffCustomComponents)
.as("Code from the business architecture general component must not depend on any other component.")
.allowEmptyShould(true);

private static boolean isDependingOnAnotherCustomComponent(PackageStructure sourcePkg, PackageStructure targetPkg) {

boolean isAllowed = true;
if (isDifferentCustomComponent(sourcePkg, targetPkg)) {
isAllowed = false;
}
return isAllowed;
}

private static boolean isDependingOnAnotherComponentsDataaccessLayer(PackageStructure sourcePkg,
PackageStructure targetPkg) {

boolean isAllowed = true;
if (isDifferentCustomComponent(sourcePkg, targetPkg) && targetPkg.isLayerDataAccess()) {
isAllowed = false;
}
return isAllowed;
}

private static boolean isDependingOnAnotherComponentsLogicLayer(PackageStructure sourcePkg,
PackageStructure targetPkg) {

boolean isAllowed = true;
if (isDifferentCustomComponent(sourcePkg, targetPkg) && targetPkg.isLayerLogic()) {
isAllowed = false;
}
return isAllowed;
}

private static boolean isDependingOnAnotherComponentsServiceLayer(PackageStructure sourcePkg,
PackageStructure targetPkg) {

boolean isAllowed = true;
if (isDifferentCustomComponent(sourcePkg, targetPkg) && targetPkg.isLayerService()) {
isAllowed = false;
}
return isAllowed;
}

/**
* Check whether the given PackageStructures do not share the same component name and if the target package is not the
* default component.
*
* @param sourcePkg
* @param targetPkg
* @return Return {@code true} if the given {@code targetPkg} is not the default {@link PackageStructure} "general"
* component and both of the parameters do not belong to the same component. Otherwise, return {@code false}.
*/
private static boolean isDifferentCustomComponent(PackageStructure sourcePkg, PackageStructure targetPkg) {

return !sourcePkg.hasSameComponent(targetPkg) && !targetPkg.isComponentGeneral() && targetPkg.isValid();
}

private static String composeViolationMessage(JavaClass sourceClass, JavaClass targetClass,
PackageStructure sourcePkg, PackageStructure targetPkg) {

String violationMessage = String.format(
"'%s.%s' is dependend on '%s.%s'. Violated in: (%s). Dependency towards (%s)", sourcePkg.getComponent(),
sourcePkg.getLayer(), targetPkg.getComponent(), targetPkg.getLayer(), sourceClass.getDescription(),
targetClass.getDescription());
return violationMessage;
}
}
10 changes: 10 additions & 0 deletions src/test/java/com/devonfw/sample/archunit/PackageStructure.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public class PackageStructure {
/** The {@link #getScope() scope} {@value} */
public static final String SCOPE_IMPLEMENTATION = "impl";

public static final String DEFAULT_COMPONENT = "general";

private final String packageName;

private final boolean valid;
Expand Down Expand Up @@ -152,6 +154,14 @@ public boolean hasSameComponent(PackageStructure otherPkg) {
return getComponent().equals(otherPkg.getComponent());
}

/**
* @return {@code true} if this is the default architecture component.
*/
public boolean isComponentGeneral() {

return getComponent().equals(DEFAULT_COMPONENT);
}

/**
* @return the name of the layer. Will be the empty {@link String} if not {@link #isValid() valid}.
*/
Expand Down