GH-37 - Hide internal aggregates and components by default in module canvas.

We now only include exposed types in the module canvas rendered. This can be overridden by calling CanvasOptions.revealInternals().
This commit is contained in:
Oliver Drotbohm
2022-09-12 15:22:05 +02:00
parent 0fbf7b33b1
commit bdb90e9298
4 changed files with 40 additions and 13 deletions

View File

@@ -46,6 +46,16 @@ public class SpringBean {
return type.getFullName();
}
/**
* Returns whether the bean is considered to be an API bean, which means it is either a public type exposed in an API
* package of the module or implements a public API interface.
*
* @return
*/
public boolean isApiBean() {
return module.isExposed(type) || !getInterfacesWithinModule().isEmpty();
}
/**
* Returns all interfaces implemented by the bean that are part of the same module.
*

View File

@@ -28,11 +28,11 @@ import org.springframework.lang.Nullable;
import org.springframework.modulith.docs.ConfigurationProperties.ModuleProperty;
import org.springframework.modulith.docs.Documenter.CanvasOptions;
import org.springframework.modulith.docs.Documenter.CanvasOptions.Groupings;
import org.springframework.modulith.model.ApplicationModule;
import org.springframework.modulith.model.ApplicationModules;
import org.springframework.modulith.model.ArchitecturallyEvidentType;
import org.springframework.modulith.model.EventType;
import org.springframework.modulith.model.FormatableJavaClass;
import org.springframework.modulith.model.ApplicationModule;
import org.springframework.modulith.model.ApplicationModules;
import org.springframework.modulith.model.Source;
import org.springframework.modulith.model.SpringBean;
import org.springframework.util.Assert;
@@ -126,7 +126,7 @@ class Asciidoctor {
.map(this::toInlineCode) //
.collect(Collectors.joining(", "));
return String.format("%s implementing %s", base, interfacesAsString);
return String.format("%s (via %s)", interfacesAsString, base);
}
public String renderSpringBeans(CanvasOptions options, ApplicationModule module) {

View File

@@ -292,12 +292,15 @@ public class Documenter {
Asciidoctor asciidoctor = Asciidoctor.withJavadocBase(modules, options.getApiBase());
Function<List<JavaClass>, String> mapper = asciidoctor::typesToBulletPoints;
List<JavaClass> aggregates = options.hideInternals
? module.getAggregateRoots().stream().filter(module::isExposed).collect(Collectors.toList())
: module.getAggregateRoots();
StringBuilder builder = new StringBuilder();
builder.append(startTable("%autowidth.stretch, cols=\"h,a\""));
builder.append(writeTableRow("Base package", asciidoctor.toInlineCode(module.getBasePackage().getName())));
builder.append(writeTableRow("Spring components", asciidoctor.renderSpringBeans(options, module)));
builder.append(addTableRow(module.getAggregateRoots(), "Aggregate roots", mapper));
builder.append(addTableRow(aggregates, "Aggregate roots", mapper));
builder.append(writeTableRow("Published events", asciidoctor.renderEvents(module)));
builder.append(addTableRow(module.getEventsListenedTo(modules), "Events listened to", mapper));
builder.append(writeTableRow("Properties",
@@ -676,6 +679,7 @@ public class Documenter {
private final List<Grouping> groupers;
private final @With @Getter @Nullable String apiBase;
private final @With @Nullable String targetFileName;
private final boolean hideInternals;
public static CanvasOptions defaults() {
@@ -689,7 +693,7 @@ public class Documenter {
}
public static CanvasOptions withoutDefaultGroupings() {
return new CanvasOptions(new ArrayList<>(), null, null);
return new CanvasOptions(new ArrayList<>(), null, null, true);
}
public CanvasOptions groupingBy(Grouping... groupings) {
@@ -697,13 +701,22 @@ public class Documenter {
List<Grouping> result = new ArrayList<>(groupers);
result.addAll(Arrays.asList(groupings));
return new CanvasOptions(result, apiBase, targetFileName);
return new CanvasOptions(result, apiBase, targetFileName, hideInternals);
}
public CanvasOptions groupingBy(String name, Predicate<SpringBean> filter) {
return groupingBy(Grouping.of(name, null, filter));
}
/**
* Enables the inclusion of internal components in the module canvas.
*
* @return will never be {@literal null}.
*/
public CanvasOptions revealInternals() {
return new CanvasOptions(groupers, apiBase, targetFileName, false);
}
Groupings groupBeans(ApplicationModule module) {
List<Grouping> sources = new ArrayList<>(groupers);
@@ -734,10 +747,11 @@ public class Documenter {
return Optional.ofNullable(targetFileName);
}
private static List<SpringBean> getMatchingBeans(ApplicationModule module, Grouping filter,
private List<SpringBean> getMatchingBeans(ApplicationModule module, Grouping filter,
List<SpringBean> alreadyMapped) {
return module.getSpringBeans().stream()
.filter(it -> !hideInternals || it.isApiBean())
.filter(it -> !alreadyMapped.contains(it))
.filter(filter::matches)
.collect(Collectors.toList());

View File

@@ -20,6 +20,8 @@ import static org.assertj.core.api.Assertions.*;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.modulith.docs.Documenter.CanvasOptions;
import org.springframework.modulith.docs.Documenter.CanvasOptions.Grouping;
import org.springframework.modulith.model.ApplicationModules;
import org.springframework.modulith.model.SpringBean;
@@ -39,13 +41,14 @@ class DocumenterUnitTests {
@Test
void groupsSpringBeansByArchitecturallyEvidentType() {
Documenter.CanvasOptions.Groupings result = Documenter.CanvasOptions.defaults()
.groupingBy(Documenter.CanvasOptions.Grouping.of("Representations", Documenter.CanvasOptions.Grouping.nameMatching(".*Representations")))
.groupingBy(Documenter.CanvasOptions.Grouping.of("Interface implementations", Documenter.CanvasOptions.Grouping.subtypeOf(Stereotypes.SomeAppInterface.class)))
CanvasOptions.Groupings result = CanvasOptions.defaults()
.revealInternals() //
.groupingBy(Grouping.of("Representations", Grouping.nameMatching(".*Representations")))
.groupingBy(Grouping.of("Interface implementations", Grouping.subtypeOf(Stereotypes.SomeAppInterface.class)))
.groupBeans(modules.getModuleByName("stereotypes").orElseThrow(RuntimeException::new));
assertThat(result.keySet())
.extracting(Documenter.CanvasOptions.Grouping::getName)
.extracting(CanvasOptions.Grouping::getName)
.containsExactlyInAnyOrder("Controllers", "Services", "Repositories", "Event listeners",
"Configuration properties", "Representations", "Interface implementations", "Others");
@@ -69,8 +72,8 @@ class DocumenterUnitTests {
Documenter documenter = new Documenter(modules);
Documenter.CanvasOptions options = Documenter.CanvasOptions.defaults() //
.groupingBy(Documenter.CanvasOptions.Grouping.of("Representations", Documenter.CanvasOptions.Grouping.nameMatching(".*Representations")));
CanvasOptions options = CanvasOptions.defaults() //
.groupingBy(Grouping.of("Representations", Grouping.nameMatching(".*Representations")));
assertThatNoException().isThrownBy(() -> {
modules.forEach(it -> documenter.toModuleCanvas(it, options));