# How the dddplus-visualization Module Generates Business Model Views from DSL Annotations

> Discover how the dddplus-visualization module uses static analysis and JavaParser to convert DSL annotations into business model diagrams with PlantUmlRenderer and PlainTextRenderer.

- Repository: [Funky Gao/cp-ddd-framework](https://github.com/funkygao/cp-ddd-framework)
- Tags: how-to-guide
- Published: 2026-03-02

---

**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:

```java
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`](https://github.com/funkygao/cp-ddd-framework/blob/main/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:

```java
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`](https://github.com/funkygao/cp-ddd-framework/blob/main/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:

```java
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:

```java
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** – `DomainModelAnalyzer` 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 Generation** – `PlantUmlRenderer` 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.