How the dddplus-visualization Module Generates Business Model Views from DSL Annotations
The dddplus-visualization module performs a static analysis pipeline that scans Java source files, parses AST nodes with JavaParser to extract DDD-plus DSL annotations, builds a ReverseEngineeringModel, and renders the results via PlantUmlRenderer or PlainTextRenderer to generate business model diagrams.
The dddplus-visualization module in the funkygao/cp-ddd-framework repository transforms domain-driven design annotations into visual architectural documentation. By analyzing Java source code at build time, it constructs a complete reverse-engineering model of aggregates, behaviors, rules, and flows without requiring runtime instrumentation. This approach ensures that technical diagrams remain synchronized with the actual codebase through a purely annotation-driven process.
The Static Analysis Pipeline Overview
The visualization process consists of five distinct phases orchestrated by DomainModelAnalyzer. The module traverses your project's source directories, filters for relevant Java files, and delegates to specialized AST visitors that understand the DDD-plus DSL. Each visitor extracts metadata from specific annotations and populates corresponding entry objects, which are then linked into a cohesive ReverseEngineeringModel before final rendering.
Step 1: Scanning Java Source Files
The entry point DomainModelAnalyzer.scan(File…) initiates file system traversal using FileWalker. An internal ActualFilter implementation excludes build artifacts and test classes to focus purely on production domain code:
public boolean interested(int level, String path, File file) {
boolean interested = !path.contains("/target/") && path.endsWith(".java");
interested = interested && !path.endsWith("Test.java");
// ...
}
This filtering logic resides in DomainModelAnalyzer.java (lines 91-98) and ensures that only relevant .java files within source directories—not target/ folders or test suites—pass through for AST analysis.
Step 2: Parsing DSL Annotations with AST Visitors
For each accepted file, FileWalker.silentParse(file) constructs a JavaParser AST. DomainModelAnalyzer then dispatches the compilation unit to a suite of specialized visitors, each targeting a specific DDD-plus annotation:
- KeyElementAstNodeVisitor – Extracts
@KeyElementfields and their types - KeyBehaviorAstNodeVisitor – Captures
@KeyBehaviormethods including arguments and async flags - KeyRuleAstNodeVisitor – Parses
@KeyRulemethods with rule names and references - KeyFlowAstNodeVisitor – Identifies
@KeyFlowmethods with input/output parameters and remarks - KeyUsecaseAstNodeVisitor – Processes
@KeyUsecasemethods usingKeyUsecaseAnnotationParser - KeyEventAstNodeVisitor – Handles
@KeyEventclass-level annotations - KeyRelationAstNodeVisitor – Maps
@KeyRelationconnections between classes - AggregateAstNodeVisitor – Identifies
@Aggregatemarkers on packages - ClassHierarchyAstNodeVisitor – Records plain inheritance and interface implementations
Extracting Annotation Attributes
The KeyUsecaseAnnotationParser demonstrates how the module extracts structured data from annotation parameters. It parses normal annotations to capture method renaming, remarks, and event consumption:
public KeyUsecaseEntry parse(AnnotationExpr keyUsecase) {
KeyUsecaseEntry entry = new KeyUsecaseEntry(className, methodName);
entry.setJavadoc(JavaParserUtil.javadocFirstLineOf(methodDeclaration));
if (keyUsecase instanceof MarkerAnnotationExpr) return entry;
NormalAnnotationExpr normal = (NormalAnnotationExpr) keyUsecase;
for (MemberValuePair mvp : normal.getPairs()) {
switch (mvp.getNameAsString()) {
case "name":
this.methodName = AnnotationFieldParser.singleFieldValue(mvp);
entry.setMethodName(this.methodName); break;
case "remark":
entry.setRemark(AnnotationFieldParser.singleFieldValue(mvp)); break;
case "consumesKeyEvent":
entry.setKeyEvent(AnnotationFieldParser.singleFieldValue(mvp)); break;
case "in":
entry.setIn(new ArrayList<>(AnnotationFieldParser.arrayFieldValue(mvp))); break;
case "out":
entry.setOut(new ArrayList<>(AnnotationFieldParser.arrayFieldValue(mvp))); break;
}
}
return entry;
}
This parser implementation in KeyUsecaseAnnotationParser.java (lines 33-65) handles both marker annotations (empty parentheses) and full annotation expressions with member-value pairs.
Step 3: Building the ReverseEngineeringModel
After the visitor loop completes, DomainModelAnalyzer.analyze() assembles the discrete entries into a unified graph. The analyzer performs three critical linking operations:
- Aggregate Association – Attaches each
KeyModelEntryto its parentAggregateEntrybased on package structure - Behavioral Linking – Connects key models to their behaviors, rules, flows, and events through cross-report lookups
- Orphan Detection – Flags events without producers and flows without sources, placing them in special "orphan" containers for review
The resulting ReverseEngineeringModel contains typed reports for each DDD concept—keyUsecaseReport(), keyBehaviorReport(), keyFlowReport(), keyRuleReport(), keyEventReport(), and keyRelationReport()—plus coverage statistics in CoverageReport. This model serves as the immutable intermediate representation passed to renderers.
Step 4: Rendering PlantUML Diagrams
The ReverseEngineeringModel feeds into renderers that translate the structural data into visual syntax. The default PlantUmlRenderer walks the model and emits PlantUML source code:
new PlantUmlRenderer()
.withModel(model)
.title("Business Model View")
.direction(PlantUmlRenderer.Direction.TopToBottom)
.render();
The renderer performs several formatting operations:
- Package Blocks – Renders aggregates as PlantUML
packagecontainers - Class Definitions – Generates class blocks for each
KeyModelEntrywith stereotypes (<<R>>for aggregate roots,<<B>>for behaviors) - Member Rendering – Emits fields, rules, and methods using PlantUML's method syntax with optional color tags for events
- Relationship Drawing – Translates
KeyRelationEntryconnections into UML arrows (--|>for inheritance,*--for composition) - Use-case Layer – Creates "业务交互层" packages containing
KeyUsecaseEntryactors with their input/output arrows - Orphan Visualization – Generates separate packages for cross-aggregate complex flows and domain events
The PlainTextRenderer provides an alternative for console output, useful for quick CI/CD inspections without diagram generation overhead.
Complete Usage Example
To generate business model views from your codebase, instantiate DomainModelAnalyzer with source directories and chain the analysis to a renderer:
import io.github.dddplus.ast.DomainModelAnalyzer;
import io.github.dddplus.ast.view.PlantUmlRenderer;
import java.io.File;
public class VisualizeDemo {
public static void main(String[] args) throws Exception {
// Scan the source tree (e.g., src/main/java)
ReverseEngineeringModel model = new DomainModelAnalyzer()
.scan(new File("src/main/java"))
.ignoreAnnotated("Deprecated") // Skip deprecated classes
.similarityThreshold(30) // Clustering threshold for models
.analyze(); // Build the model
// Generate PlantUML output (writes .puml and optional SVG)
new PlantUmlRenderer()
.withModel(model)
.title("DDDplus Business Model")
.direction(PlantUmlRenderer.Direction.LeftToRight)
.skinParamPolyline() // Improved layout
.classDiagramSvgFilename("model.svg")
.plantUmlFilename("model.puml")
.render();
}
}
Executing this code produces model.puml containing the PlantUML source and optionally model.svg for direct embedding in documentation.
Summary
The dddplus-visualization module generates business model views through a compile-time static analysis pipeline:
- File Filtering –
DomainModelAnalyzerusesFileWalkerwithActualFilterto exclude tests and build artifacts - AST Parsing – JavaParser drives specialized visitors for each DDD-plus annotation type (
@KeyUsecase,@KeyBehavior, etc.) - Model Assembly – Extracted entries populate a
ReverseEngineeringModelwith linked aggregates, behaviors, and relationships - Diagram Generation –
PlantUmlRendererconverts the model into UML diagrams, whilePlainTextRendereroffers console output
Frequently Asked Questions
What annotations does the dddplus-visualization module recognize?
The module recognizes the complete DDD-plus DSL annotation set including @KeyUsecase, @KeyBehavior, @KeyElement, @KeyRule, @KeyFlow, @KeyEvent, @KeyRelation, and @Aggregate. Each annotation triggers a specific AST visitor that extracts attributes like in, out, remark, name, and async to build the visual model.
Can I customize the output format beyond PlantUML?
Yes. While PlantUmlRenderer is the default for generating .puml files and SVG diagrams, the module also provides PlainTextRenderer for console-friendly output. Both implement the same renderer interface and operate on the ReverseEngineeringModel, allowing you to extend the system with custom renderers for formats like Mermaid or Graphviz.
How does the module handle code that spans multiple packages or aggregates?
The DomainModelAnalyzer automatically associates key models with aggregates based on package structure during the analysis phase. For relationships that cross aggregate boundaries, KeyRelationAstNodeVisitor captures @KeyRelation metadata, and the analyzer creates explicit links in the model. The renderer then places orphan flows and events in dedicated visualization packages labeled "跨聚合复杂流程" and "领域事件" to highlight architectural boundaries.
Does this visualization require running the application?
No. The dddplus-visualization module operates entirely through static analysis of Java source files. It parses the AST at build time using JavaParser, requiring no runtime instrumentation, active application instances, or external monitoring tools. This approach guarantees that diagrams reflect the actual code structure without requiring the system to be deployed or executed.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →