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 @KeyElement fields and their types
  • KeyBehaviorAstNodeVisitor – Captures @KeyBehavior methods including arguments and async flags
  • KeyRuleAstNodeVisitor – Parses @KeyRule methods with rule names and references
  • KeyFlowAstNodeVisitor – Identifies @KeyFlow methods with input/output parameters and remarks
  • KeyUsecaseAstNodeVisitor – Processes @KeyUsecase methods using KeyUsecaseAnnotationParser
  • KeyEventAstNodeVisitor – Handles @KeyEvent class-level annotations
  • KeyRelationAstNodeVisitor – Maps @KeyRelation connections between classes
  • AggregateAstNodeVisitor – Identifies @Aggregate markers 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:

  1. Aggregate Association – Attaches each KeyModelEntry to its parent AggregateEntry based on package structure
  2. Behavioral Linking – Connects key models to their behaviors, rules, flows, and events through cross-report lookups
  3. 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 package containers
  • Class Definitions – Generates class blocks for each KeyModelEntry with 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 KeyRelationEntry connections into UML arrows (--|> for inheritance, *-- for composition)
  • Use-case Layer – Creates "业务交互层" packages containing KeyUsecaseEntry actors 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 FilteringDomainModelAnalyzer uses FileWalker with ActualFilter to 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 ReverseEngineeringModel with linked aggregates, behaviors, and relationships
  • Diagram GenerationPlantUmlRenderer converts the model into UML diagrams, while PlainTextRenderer offers 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →