22
22
import java .util .*;
23
23
import java .util .concurrent .ConcurrentHashMap ;
24
24
import java .util .function .Function ;
25
+ import java .util .function .Supplier ;
25
26
import java .util .stream .Collectors ;
26
27
import java .util .stream .Stream ;
27
28
import java .util .stream .StreamSupport ;
31
32
import org .jgrapht .graph .DefaultEdge ;
32
33
import org .jgrapht .traverse .TopologicalOrderIterator ;
33
34
import org .jmolecules .archunit .JMoleculesDddRules ;
34
- import org .springframework .core .Ordered ;
35
35
import org .springframework .core .annotation .AnnotationAwareOrderComparator ;
36
- import org .springframework .core .annotation .Order ;
37
36
import org .springframework .core .io .support .SpringFactoriesLoader ;
38
37
import org .springframework .lang .Nullable ;
39
- import org .springframework .modulith .Modulith ;
40
- import org .springframework .modulith .Modulithic ;
41
38
import org .springframework .modulith .core .Types .JMoleculesTypes ;
42
39
import org .springframework .util .Assert ;
43
40
import org .springframework .util .ClassUtils ;
41
+ import org .springframework .util .function .SingletonSupplier ;
44
42
45
43
import com .tngtech .archunit .base .DescribedPredicate ;
46
44
import com .tngtech .archunit .core .domain .JavaClass ;
@@ -86,6 +84,7 @@ public class ApplicationModules implements Iterable<ApplicationModule> {
86
84
private final Map <String , ApplicationModule > modules ;
87
85
private final JavaClasses allClasses ;
88
86
private final List <JavaPackage > rootPackages ;
87
+ private final Supplier <List <ApplicationModule >> rootModules ;
89
88
private final Set <ApplicationModule > sharedModules ;
90
89
private final List <String > orderedNames ;
91
90
@@ -112,6 +111,10 @@ protected ApplicationModules(ModulithMetadata metadata, Collection<String> packa
112
111
.map (it -> JavaPackage .of (classes , it ).toSingle ()) //
113
112
.toList ();
114
113
114
+ this .rootModules = SingletonSupplier .of (() -> rootPackages .stream ()
115
+ .map (ApplicationModules ::rootModuleFor )
116
+ .toList ());
117
+
115
118
this .sharedModules = Collections .emptySet ();
116
119
117
120
this .orderedNames = JGRAPHT_PRESENT //
@@ -121,32 +124,35 @@ protected ApplicationModules(ModulithMetadata metadata, Collection<String> packa
121
124
122
125
/**
123
126
* Creates a new {@link ApplicationModules} for the given {@link ModulithMetadata}, {@link ApplicationModule}s,
124
- * {@link JavaClasses}, {@link JavaPackage}s, shared {@link ApplicationModule}s, ordered module names and verified
125
- * flag.
127
+ * {@link JavaClasses}, {@link JavaPackage}s, root and shared {@link ApplicationModule}s, ordered module names and
128
+ * verified flag.
126
129
*
127
130
* @param metadata must not be {@literal null}.
128
131
* @param modules must not be {@literal null}.
129
132
* @param allClasses must not be {@literal null}.
130
133
* @param rootPackages must not be {@literal null}.
134
+ * @param rootModules must not be {@literal null}.
131
135
* @param sharedModules must not be {@literal null}.
132
136
* @param orderedNames must not be {@literal null}.
133
137
* @param verified
134
138
*/
135
139
private ApplicationModules (ModulithMetadata metadata , Map <String , ApplicationModule > modules , JavaClasses classes ,
136
- List <JavaPackage > rootPackages , Set < ApplicationModule > sharedModules , List <String > orderedNames ,
137
- boolean verified ) {
140
+ List <JavaPackage > rootPackages , Supplier < List <ApplicationModule >> rootModules ,
141
+ Set < ApplicationModule > sharedModules , List < String > orderedNames , boolean verified ) {
138
142
139
143
Assert .notNull (metadata , "ModulithMetadata must not be null!" );
140
144
Assert .notNull (modules , "Application modules must not be null!" );
141
145
Assert .notNull (classes , "JavaClasses must not be null!" );
142
146
Assert .notNull (rootPackages , "Root JavaPackages must not be null!" );
147
+ Assert .notNull (rootModules , "Root modules must not be null!" );
143
148
Assert .notNull (sharedModules , "Shared ApplicationModules must not be null!" );
144
149
Assert .notNull (orderedNames , "Ordered application module names must not be null!" );
145
150
146
151
this .metadata = metadata ;
147
152
this .modules = modules ;
148
153
this .allClasses = classes ;
149
154
this .rootPackages = rootPackages ;
155
+ this .rootModules = rootModules ;
150
156
this .sharedModules = sharedModules ;
151
157
this .orderedNames = orderedNames ;
152
158
this .verified = verified ;
@@ -296,7 +302,7 @@ public Optional<ApplicationModule> getModuleByType(JavaClass type) {
296
302
297
303
Assert .notNull (type , "Type must not be null!" );
298
304
299
- return modules . values (). stream () //
305
+ return allModules () //
300
306
.filter (it -> it .contains (type )) //
301
307
.findFirst ();
302
308
}
@@ -311,7 +317,7 @@ public Optional<ApplicationModule> getModuleByType(String candidate) {
311
317
312
318
Assert .hasText (candidate , "Candidate must not be null or empty!" );
313
319
314
- return modules . values (). stream () //
320
+ return allModules () //
315
321
.filter (it -> it .contains (candidate )) //
316
322
.findFirst ();
317
323
}
@@ -328,15 +334,20 @@ public Optional<ApplicationModule> getModuleByType(Class<?> candidate) {
328
334
329
335
/**
330
336
* Returns the {@link ApplicationModule} containing the given package.
331
- *
337
+ *
332
338
* @param name must not be {@literal null} or empty.
333
339
* @return will never be {@literal null}.
334
340
*/
335
341
public Optional <ApplicationModule > getModuleForPackage (String name ) {
336
342
337
343
return modules .values ().stream () //
338
344
.filter (it -> it .containsPackage (name )) //
339
- .findFirst ();
345
+ .findFirst ()
346
+ .or (() -> {
347
+ return rootModules .get ().stream ()
348
+ .filter (it -> it .hasBasePackage (name ))
349
+ .findFirst ();
350
+ });
340
351
}
341
352
342
353
/**
@@ -383,7 +394,7 @@ public Violations detectViolations() {
383
394
}
384
395
}
385
396
386
- return modules .values ().stream () //
397
+ return Stream . concat ( rootModules . get (). stream (), modules .values ().stream () ) //
387
398
.map (it -> it .detectDependencies (this )) //
388
399
.reduce (violations , Violations ::and );
389
400
}
@@ -458,7 +469,8 @@ public String toString() {
458
469
}
459
470
460
471
private ApplicationModules withSharedModules (Set <ApplicationModule > sharedModules ) {
461
- return new ApplicationModules (metadata , modules , allClasses , rootPackages , sharedModules , orderedNames , verified );
472
+ return new ApplicationModules (metadata , modules , allClasses , rootPackages , rootModules , sharedModules , orderedNames ,
473
+ verified );
462
474
}
463
475
464
476
private FailureReport assertNoCyclesFor (JavaPackage rootPackage ) {
@@ -506,6 +518,16 @@ private ApplicationModule getRequiredModule(String moduleName) {
506
518
return module ;
507
519
}
508
520
521
+ /**
522
+ * Returns of all {@link ApplicationModule}s, including root ones (last).
523
+ *
524
+ * @return will never be {@literal null}.
525
+ * @since 1.1
526
+ */
527
+ private Stream <ApplicationModule > allModules () {
528
+ return Stream .concat (modules .values ().stream (), rootModules .get ().stream ());
529
+ }
530
+
509
531
/**
510
532
* Creates a new {@link ApplicationModules} instance for the given {@link CacheKey}.
511
533
*
@@ -532,6 +554,29 @@ private static ApplicationModules of(CacheKey key) {
532
554
return modules .withSharedModules (sharedModules );
533
555
}
534
556
557
+ /**
558
+ * Creates a special root {@link ApplicationModule} for the given {@link JavaPackage}.
559
+ *
560
+ * @param javaPackage must not be {@literal null}.
561
+ * @return will never be {@literal null}.
562
+ * @since 1.1
563
+ */
564
+ private static ApplicationModule rootModuleFor (JavaPackage javaPackage ) {
565
+
566
+ return new ApplicationModule (javaPackage , true ) {
567
+
568
+ @ Override
569
+ public String getName () {
570
+ return "root:" + super .getName ();
571
+ }
572
+
573
+ @ Override
574
+ public boolean isRootModule () {
575
+ return true ;
576
+ }
577
+ };
578
+ }
579
+
535
580
public static class Filters {
536
581
537
582
public static DescribedPredicate <JavaClass > withoutModules (String ... names ) {
0 commit comments