Skip to content

Commit b370d5a

Browse files
committed
GH-787 - Allow references between sibling sub-modules.
Previously, we rejected references between sub-modules both contained in the same parent package. Related ticket: GH-578.
1 parent 803c698 commit b370d5a

File tree

3 files changed

+35
-13
lines changed

3 files changed

+35
-13
lines changed

spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,18 +1201,17 @@ Violations isValidDependencyWithin(ApplicationModules modules) {
12011201

12021202
// Parent child relationships
12031203

1204-
return targetModule.getParentModule(modules)
1205-
.filter(it -> !it.equals(originModule))
1206-
.map(__ -> {
1207-
1208-
var violationText = INVALID_SUB_MODULE_REFERENCE
1209-
.formatted(originModule.getName(), targetModule.getName(),
1210-
FormatableType.of(source).getAbbreviatedFullName(originModule),
1211-
FormatableType.of(target).getAbbreviatedFullName(targetModule));
1212-
1213-
return violations.and(new Violation(violationText));
1214-
})
1215-
.orElse(violations);
1204+
if (!haveSameParentOrDirectParentRelationship(originModule, targetModule, modules)) {
1205+
1206+
var violationText = INVALID_SUB_MODULE_REFERENCE
1207+
.formatted(originModule.getName(), targetModule.getName(),
1208+
FormatableType.of(source).getAbbreviatedFullName(originModule),
1209+
FormatableType.of(target).getAbbreviatedFullName(targetModule));
1210+
1211+
return violations.and(new Violation(violationText));
1212+
}
1213+
1214+
return violations;
12161215
}
12171216

12181217
ApplicationModule getExistingModuleOf(JavaClass javaClass, ApplicationModules modules) {
@@ -1347,6 +1346,27 @@ private static String createDescription(JavaMember codeUnit, JavaClass declaring
13471346
private static boolean isInjectionPoint(JavaMember unit) {
13481347
return INJECTION_TYPES.stream().anyMatch(type -> unit.isAnnotatedWith(type));
13491348
}
1349+
1350+
private static boolean haveSameParentOrDirectParentRelationship(ApplicationModule source, ApplicationModule target,
1351+
ApplicationModules modules) {
1352+
1353+
var sourceParent = modules.getParentOf(source);
1354+
var targetParent = modules.getParentOf(target);
1355+
1356+
// Top-level modules
1357+
return targetParent.isEmpty()
1358+
1359+
// One is parent of the other
1360+
|| hasValue(sourceParent, target)
1361+
|| hasValue(targetParent, source)
1362+
1363+
// Same immediate parent
1364+
|| sourceParent.flatMap(it -> targetParent.filter(it::equals)).isPresent();
1365+
}
1366+
1367+
private static <T> boolean hasValue(Optional<T> optional, T expected) {
1368+
return optional.filter(expected::equals).isPresent();
1369+
}
13501370
}
13511371

13521372
private static class InjectionDependency extends QualifiedDependency {

spring-modulith-core/src/test/java/example/ni/nested/b/second/InNestedBSecond.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
package example.ni.nested.b.second;
1717

1818
import example.ni.nested.InNested;
19+
import example.ni.nested.b.first.InNestedBFirst;
1920

2021
/**
2122
* @author Oliver Drotbohm
2223
*/
2324
public class InNestedBSecond {
2425
InNested inNested;
26+
InNestedBFirst siblingReference;
2527
}

src/docs/antora/modules/ROOT/pages/fundamentals.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ In this example `inventory` is an application module as described xref:fundament
211211
The `@ApplicationModule` annotation on the `nested` package caused that to become a nested application module in turn.
212212
In that arrangement, the following access rules apply:
213213

214-
* The code in _Nested_ is only available from _Inventory_, i.e. only any of the `SomethingInventoryInternal` types can access `NestedApi`. `NestedInternal` is only accessible from `NestedApi`.
214+
* The code in _Nested_ is only available from _Inventory_ or any types exposed by sibling application modules nested inside _Inventory_.
215215
* Any code in the _Nested_ module can access code in parent modules, even internal.
216216
I.e., both `NestedApi` and `NestedInternal` can access `inventory.internal.SomethingInventoryInternal`.
217217
* Code from nested modules can also access exposed types by top-level application modules.

0 commit comments

Comments
 (0)