Skip to content

Commit d96263f

Browse files
committed
GH-11, GH-66, GH-67, GH-68 - Improve module canvas.
Overhauled the module canvas in various ways. By default, we now skip "empty" rows. In other words, if we do not find any published events for example, we skip the entire row. CanvasOptions.revealEmptyRows() can be used to keep those empty rows around. We now also expose all value types and Spring bean references. Refactored the dependency lookup APIs on ApplicationModule. Both DependencyType and DependencyDepth are now top-level types. Also, ApplicationModuleDependency and ApplicationModuleDependencies were introduced as explicit types. In the course of that, ApplicationModule.getDependencies(…) has got its return type changed from List<ApplicationModule> to ApplicationModuleDependencies. The internal ModuleDependency type has been renamed to QualifiedDependency.
1 parent 62ac231 commit d96263f

File tree

14 files changed

+738
-327
lines changed

14 files changed

+738
-327
lines changed

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

Lines changed: 219 additions & 251 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.model;
17+
18+
import lombok.RequiredArgsConstructor;
19+
20+
import java.util.List;
21+
import java.util.stream.Stream;
22+
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* The materialized, in other words actually present dependencies of the current module towards other modules.
27+
*
28+
* @author Oliver Drotbohm
29+
*/
30+
@RequiredArgsConstructor(staticName = "of")
31+
public class ApplicationModuleDependencies {
32+
33+
private final List<ApplicationModuleDependency> dependencies;
34+
private final ApplicationModules modules;
35+
36+
/**
37+
* Returns whether the dependencies contain the given {@link ApplicationModule}.
38+
*
39+
* @param module must not be {@literal null}.
40+
* @return will never be {@literal null}.
41+
*/
42+
public boolean contains(ApplicationModule module) {
43+
44+
Assert.notNull(module, "ApplicationModule must not be null!");
45+
46+
return dependencies.stream()
47+
.map(ApplicationModuleDependency::getTargetModule)
48+
.anyMatch(module::equals);
49+
}
50+
51+
/**
52+
* Returns whether the dependencies contain the {@link ApplicationModule} with the given name.
53+
*
54+
* @param name must not be {@literal null} or empty.
55+
* @return will never be {@literal null}.
56+
*/
57+
public boolean containsModuleNamed(String name) {
58+
59+
Assert.hasText(name, "Module name must not be null or empty!");
60+
61+
return dependencies.stream()
62+
.map(ApplicationModuleDependency::getTargetModule)
63+
.map(ApplicationModule::getName)
64+
.anyMatch(name::equals);
65+
}
66+
67+
/**
68+
* Returns all {@link DefaultMaterializedDependency} instances as {@link Stream}.
69+
*
70+
* @return will never be {@literal null}.
71+
*/
72+
public Stream<ApplicationModuleDependency> stream() {
73+
return dependencies.stream();
74+
}
75+
76+
public ApplicationModuleDependencies withType(DependencyType type) {
77+
78+
var filtered = dependencies.stream()
79+
.filter(it -> it.getDependencyType().equals(type))
80+
.toList();
81+
82+
return ApplicationModuleDependencies.of(filtered, modules);
83+
}
84+
85+
/**
86+
* Returns whether there are any dependencies at all.
87+
*
88+
* @return will never be {@literal null}.
89+
*/
90+
public boolean isEmpty() {
91+
return dependencies.isEmpty();
92+
}
93+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.model;
17+
18+
import com.tngtech.archunit.core.domain.JavaClass;
19+
20+
/**
21+
* A dependency between two {@link ApplicationModule}s.
22+
*
23+
* @author Oliver Drotbohm
24+
*/
25+
public interface ApplicationModuleDependency {
26+
27+
/**
28+
* The source java type establishing the dependency.
29+
*
30+
* @return will never be {@literal null}.
31+
*/
32+
JavaClass getSourceType();
33+
34+
/**
35+
* The dependency target type.
36+
*
37+
* @return will never be {@literal null}.
38+
*/
39+
JavaClass getTargetType();
40+
41+
/**
42+
* The type of the dependency.
43+
*
44+
* @return will never be {@literal null}.
45+
*/
46+
DependencyType getDependencyType();
47+
48+
/**
49+
* The target {@link ApplicationModule}.
50+
*
51+
* @return will never be {@literal null}.
52+
*/
53+
ApplicationModule getTargetModule();
54+
}

spring-modulith-core/src/main/java/org/springframework/modulith/model/ArchitecturallyEvidentType.java

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.util.Optional;
3333
import java.util.Set;
3434
import java.util.function.Predicate;
35-
import java.util.stream.Collectors;
3635
import java.util.stream.Stream;
3736

3837
import org.springframework.data.repository.core.RepositoryMetadata;
@@ -81,7 +80,7 @@ public static ArchitecturallyEvidentType of(JavaClass type, Classes beanTypes) {
8180
delegates.add(new SpringDataAwareArchitecturallyEvidentType(type, beanTypes));
8281
}
8382

84-
delegates.add(new SpringAwareArchitecturallyEvidentType(type));
83+
delegates.add(new SpringAwareArchitecturallyEvidentType(type, beanTypes));
8584

8685
return DelegatingType.of(type, delegates);
8786
});
@@ -135,6 +134,14 @@ public boolean isConfigurationProperties() {
135134
return false;
136135
}
137136

137+
public boolean isInjectable() {
138+
return isService() || isController() || isEventListener() || isConfigurationProperties();
139+
}
140+
141+
public boolean isValueObject() {
142+
return false;
143+
}
144+
138145
/**
139146
* Returns other types that are interesting in the context of the current {@link ArchitecturallyEvidentType}. For
140147
* example, for an event listener this might be the event types the particular listener is interested in.
@@ -195,8 +202,13 @@ static class SpringAwareArchitecturallyEvidentType extends ArchitecturallyEviden
195202
private static final Predicate<JavaMethod> IS_EVENT_LISTENER = IS_ANNOTATED_EVENT_LISTENER
196203
.or(IS_IMPLEMENTING_EVENT_LISTENER);
197204

198-
public SpringAwareArchitecturallyEvidentType(JavaClass type) {
205+
private final boolean injectable;
206+
207+
public SpringAwareArchitecturallyEvidentType(JavaClass type, Classes beanTypes) {
208+
199209
super(type);
210+
211+
this.injectable = beanTypes.contains(type);
200212
}
201213

202214
/*
@@ -253,6 +265,15 @@ public boolean isConfigurationProperties() {
253265
return Types.isAnnotatedWith(SpringTypes.AT_CONFIGURATION_PROPERTIES).test(getType());
254266
}
255267

268+
/*
269+
* (non-Javadoc)
270+
* @see org.springframework.modulith.model.ArchitecturallyEvidentType#isInjectable()
271+
*/
272+
@Override
273+
public boolean isInjectable() {
274+
return injectable || SpringTypes.isComponent().test(getType()) || super.isInjectable();
275+
}
276+
256277
/*
257278
* (non-Javadoc)
258279
* @see org.springframework.modulith.model.ArchitecturallyEvidentType#getOtherTypeReferences()
@@ -411,20 +432,35 @@ public boolean isService() {
411432
public boolean isEventListener() {
412433
return getType().getMethods().stream().anyMatch(IS_ANNOTATED_EVENT_LISTENER);
413434
}
435+
436+
/*
437+
* (non-Javadoc)
438+
* @see org.springframework.modulith.model.ArchitecturallyEvidentType#isValueObject()
439+
*/
440+
@Override
441+
public boolean isValueObject() {
442+
443+
JavaClass type = getType();
444+
445+
return Types.isAnnotatedWith(org.jmolecules.ddd.annotation.ValueObject.class).test(type)
446+
|| type.isAssignableTo(org.jmolecules.ddd.types.ValueObject.class);
447+
}
414448
}
415449

416450
static class DelegatingType extends ArchitecturallyEvidentType {
417451

418452
private final Supplier<Boolean> isAggregateRoot, isRepository, isEntity, isService, isController,
419-
isEventListener,
420-
isConfigurationProperties;
453+
isEventListener, isConfigurationProperties, isInjectable, isValueObject;
454+
421455
private final Supplier<Collection<JavaClass>> referenceTypes;
422456
private final Supplier<Collection<ReferenceMethod>> referenceMethods;
423457

424458
DelegatingType(JavaClass type, Supplier<Boolean> isAggregateRoot,
425459
Supplier<Boolean> isRepository, Supplier<Boolean> isEntity, Supplier<Boolean> isService,
426460
Supplier<Boolean> isController, Supplier<Boolean> isEventListener,
427461
Supplier<Boolean> isConfigurationProperties,
462+
Supplier<Boolean> isInjectable,
463+
Supplier<Boolean> isValueObject,
428464
Supplier<Collection<JavaClass>> referenceTypes,
429465
Supplier<Collection<ReferenceMethod>> referenceMethods) {
430466

@@ -437,43 +473,49 @@ static class DelegatingType extends ArchitecturallyEvidentType {
437473
this.isController = isController;
438474
this.isEventListener = isEventListener;
439475
this.isConfigurationProperties = isConfigurationProperties;
476+
this.isInjectable = isInjectable;
477+
this.isValueObject = isValueObject;
440478
this.referenceTypes = referenceTypes;
441479
this.referenceMethods = referenceMethods;
442480
}
443481

444482
public static DelegatingType of(JavaClass type, List<ArchitecturallyEvidentType> types) {
445483

446-
Supplier<Boolean> isAggregateRoot = Suppliers
484+
var isAggregateRoot = Suppliers
447485
.memoize(() -> types.stream().anyMatch(ArchitecturallyEvidentType::isAggregateRoot));
448486

449-
Supplier<Boolean> isRepository = Suppliers
487+
var isRepository = Suppliers
450488
.memoize(() -> types.stream().anyMatch(ArchitecturallyEvidentType::isRepository));
451489

452-
Supplier<Boolean> isEntity = Suppliers
490+
var isEntity = Suppliers
453491
.memoize(() -> types.stream().anyMatch(ArchitecturallyEvidentType::isEntity));
454492

455-
Supplier<Boolean> isService = Suppliers
493+
var isService = Suppliers
456494
.memoize(() -> types.stream().anyMatch(ArchitecturallyEvidentType::isService));
457495

458-
Supplier<Boolean> isController = Suppliers
496+
var isController = Suppliers
459497
.memoize(() -> types.stream().anyMatch(ArchitecturallyEvidentType::isController));
460498

461-
Supplier<Boolean> isEventListener = Suppliers
499+
var isEventListener = Suppliers
462500
.memoize(() -> types.stream().anyMatch(ArchitecturallyEvidentType::isEventListener));
463501

464-
Supplier<Boolean> isConfigurationProperties = Suppliers
502+
var isConfigurationProperties = Suppliers
465503
.memoize(() -> types.stream().anyMatch(ArchitecturallyEvidentType::isConfigurationProperties));
466504

505+
var isInjectable = Suppliers.memoize(() -> types.stream().anyMatch(ArchitecturallyEvidentType::isInjectable));
506+
507+
var isValueObject = Suppliers.memoize(() -> types.stream().anyMatch(ArchitecturallyEvidentType::isValueObject));
508+
467509
Supplier<Collection<JavaClass>> referenceTypes = Suppliers.memoize(() -> types.stream() //
468510
.flatMap(ArchitecturallyEvidentType::getReferenceTypes) //
469-
.collect(Collectors.toList()));
511+
.toList());
470512

471513
Supplier<Collection<ReferenceMethod>> referenceMethods = Suppliers.memoize(() -> types.stream() //
472514
.flatMap(ArchitecturallyEvidentType::getReferenceMethods) //
473-
.collect(Collectors.toList()));
515+
.toList());
474516

475517
return new DelegatingType(type, isAggregateRoot, isRepository, isEntity, isService, isController,
476-
isEventListener, isConfigurationProperties, referenceTypes, referenceMethods);
518+
isEventListener, isConfigurationProperties, isInjectable, isValueObject, referenceTypes, referenceMethods);
477519
}
478520

479521
/*
@@ -541,6 +583,24 @@ public boolean isConfigurationProperties() {
541583
return isConfigurationProperties.get();
542584
}
543585

586+
/*
587+
* (non-Javadoc)
588+
* @see org.springframework.modulith.model.ArchitecturallyEvidentType#isInjectable()
589+
*/
590+
@Override
591+
public boolean isInjectable() {
592+
return isInjectable.get();
593+
}
594+
595+
/*
596+
* (non-Javadoc)
597+
* @see org.springframework.modulith.model.ArchitecturallyEvidentType#isValueObject()
598+
*/
599+
@Override
600+
public boolean isValueObject() {
601+
return isValueObject.get();
602+
}
603+
544604
/*
545605
* (non-Javadoc)
546606
* @see org.springframework.modulith.model.ArchitecturallyEvidentType#getOtherTypeReferences()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.model;
17+
18+
public enum DependencyDepth {
19+
20+
NONE,
21+
22+
IMMEDIATE,
23+
24+
ALL;
25+
}

0 commit comments

Comments
 (0)