# How DDDplus Manages Step Dependencies with @Step Annotation: A Complete Guide

> Learn how DDDplus uses the @Step annotation to manage step dependencies. Discover why enforcement is optional and how to handle execution order for your DDDplus applications.

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

---

**The `@Step` annotation in DDDplus allows developers to declare step dependencies via the `dependsOn` attribute, but the framework intentionally does not enforce these dependencies at runtime, requiring manual execution order management or custom validation rules.**

The `funkygao/cp-ddd-framework` (DDDplus) provides a structured way to define domain steps using the `@Step` annotation. While this annotation includes a `dependsOn` attribute designed to document which steps must execute before others, understanding how the framework actually processes—or ignores—this metadata is critical for building reliable domain workflows.

## Understanding the @Step Annotation and dependsOn Attribute

The `@Step` annotation is defined in [`dddplus-runtime/src/main/java/io/github/dddplus/annotation/Step.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/annotation/Step.java). It includes several metadata fields, with `dependsOn` specifically intended to declare execution prerequisites:

```java
public @interface Step {
    String value() default "";
    String name() default "";
    String[] tags() default {};

    /** 该步骤依赖哪些其他步骤. 被依赖的步骤先执行，才能执行本步骤 */
    Class<? extends IDomainStep>[] dependsOn() default {};
}

```

The Javadoc explicitly states that depended-on steps must execute first. However, **the annotation is marked `@Deprecated`** because the core framework does not currently consume this information automatically. The `dependsOn` attribute exists primarily as a **design-time contract** to guide developers and support custom tooling.

## How StepDef Registers Steps Without Reading Dependencies

When Spring initializes the application context, DDDplus registers each `@Step`-annotated bean through the `StepDef` class located at [`dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/StepDef.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/StepDef.java). The registration process intentionally extracts only specific fields:

```java
public void registerBean(@NonNull Object bean) {
    Step domainStep = InternalAopUtils.getAnnotation(bean, Step.class);
    this.name = domainStep.name();
    this.tags = domainStep.tags();
    // NOTE: the `dependsOn` attribute is *not* stored in StepDef
    this.stepBean = (IDomainStep) bean;
    this.activity = this.stepBean.activityCode();
    this.code = this.stepBean.stepCode();
    InternalIndexer.index(this);
}

```

As shown in the `registerBean` method, the framework captures `name` and `tags` but **completely ignores the `dependsOn` array**. Consequently, the internal index (`domainStepDefMap`) contains no dependency graph information, making automatic reordering impossible during runtime.

## Runtime Execution: StepsExecTemplate and DDD.findSteps

The execution flow demonstrates why dependency enforcement does not occur automatically. The `DDD.findSteps` method in [`dddplus-runtime/src/main/java/io/github/dddplus/runtime/DDD.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/runtime/DDD.java) retrieves steps based on the provided activity code and step code list:

```java
public static <Step extends IDomainStep> List<Step> findSteps(
        @NonNull String activityCode,
        @NonNull List<String> stepCodeList) {
    List<StepDef> stepDefs = InternalIndexer.findDomainSteps(activityCode, stepCodeList);
    // simply returns the beans in the order requested by the caller
}

```

The method returns step implementations **exactly in the order supplied by the caller**, typically a static list defined in a `SubmitStep` implementation. The `StepsExecTemplate` then executes these steps sequentially:

```java
List<Step> steps = DDD.findSteps(activityCode, stepCodes);
// steps are executed sequentially (or asynchronously if configured)

```

No topological sorting, cycle detection, or dependency validation occurs between the `findSteps` lookup and the execution loop.

## ArchitectureEnforcer Limitations

The ArchUnit-based architectural guard in [`dddplus-enforce/src/main/java/io/github/dddplus/ArchitectureEnforcer.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-enforce/src/main/java/io/github/dddplus/ArchitectureEnforcer.java) validates structural rules but does not inspect dependency relationships:

```java
public static final ArchRule domainStepRule() {
    return classes()
        .that().implement(IDomainStep.class)
        .and().doNotHaveModifier(JavaModifier.ABSTRACT)
        .should().haveSimpleNameEndingWith("Step")
        .andShould().beAnnotatedWith(Step.class)
        .as("领域步骤的使用规范");
}

```

This rule only verifies that step classes are properly annotated with `@Step`. It does not check that the `dependsOn` graph is respected or acyclic in the execution plan.

## Practical Example: Declaring vs. Enforcing Dependencies

Consider the test examples in [`dddplus-test/src/test/java/io/github/dddplus/runtime/registry/mock/step/BarStep.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-test/src/test/java/io/github/dddplus/runtime/registry/mock/step/BarStep.java):

```java
@Step(
    tags = Steps.Submit.GoodsValidationGroup,
    dependsOn = FooStep.class    // BarStep declares it depends on FooStep
)
@Slf4j
public class BarStep extends SubmitStep {
    @Override public String stepCode() { return Steps.Submit.BarStep; }
    // ... execute logic ...
}

```

Here, `BarStep` explicitly declares that it requires `FooStep` to execute first. However, the framework accepts any execution order:

```java
// Correct order - works as intended
List<String> steps = Arrays.asList(
    Steps.Submit.FooStep,
    Steps.Submit.BarStep
);

// Incorrect order - no runtime error, but logical failure risk
List<String> wrongOrder = Arrays.asList(
    Steps.Submit.BarStep,  // Executes first despite dependency declaration
    Steps.Submit.FooStep
);

```

If you reverse the order, **no exception is thrown**; the step simply runs before its declared dependency, potentially causing logical errors such as missing data or invalid state transitions.

## How to Enforce Dependencies Manually

Since DDDplus does not provide built-in enforcement, you must implement validation yourself.

### Runtime Validation with Topological Sort

You can implement a validation utility that checks the execution list against the annotation metadata before calling `StepsExecTemplate`:

```java
public static void validateStepOrder(List<Class<? extends IDomainStep>> stepClasses) {
    Map<Class<?>, Set<Class<?>>> deps = new HashMap<>();

    // Collect dependencies from annotations
    for (Class<? extends IDomainStep> stepCls : stepClasses) {
        Step ann = stepCls.getAnnotation(Step.class);
        if (ann != null) {
            deps.put(stepCls, new HashSet<>(Arrays.asList(ann.dependsOn())));
        }
    }

    // Verify order: each step's dependencies must appear earlier
    Set<Class<?>> seen = new HashSet<>();
    for (Class<?> step : stepClasses) {
        for (Class<?> dependency : deps.getOrDefault(step, Collections.emptySet())) {
            if (!seen.contains(dependency)) {
                throw new IllegalStateException(
                    step.getSimpleName() + " depends on " + dependency.getSimpleName() +
                    " which has not been executed yet");
            }
        }
        seen.add(step);
    }
}

```

Call this method inside your custom `StepsExecTemplate` before the execution loop to achieve **runtime enforcement** without modifying the core framework.

### Compile-Time Validation with ArchUnit

For CI/CD pipelines, implement an ArchUnit rule that validates the dependency graph against your static execution lists:

```java
@AnalyzeClasses(packages = "com.myapp")
public class StepDependencyRuleTest {

    @Test
    public void stepsMustRespectDeclaredDependencies() {
        JavaClasses classes = new ClassFileImporter().importPackages("com.myapp");
        
        // Define your execution order source (e.g., router configuration)
        List<String> executionOrder = Arrays.asList("FooStep", "BarStep");
        
        classes
            .that().areAnnotatedWith(Step.class)
            .should(new ArchCondition<JavaClass>("have dependencies declared before themselves") {
                @Override
                public void check(JavaClass item, ConditionEvents events) {
                    Step stepAnn = item.reflect().getAnnotation(Step.class);
                    for (Class<? extends IDomainStep> dep : stepAnn.dependsOn()) {
                        int stepIndex = executionOrder.indexOf(item.getSimpleName());
                        int depIndex = executionOrder.indexOf(dep.getSimpleName());
                        
                        boolean satisfied = depIndex < stepIndex && depIndex != -1;
                        events.add(new SimpleConditionEvent(
                            satisfied,
                            item.getName() + " depends on " + dep.getSimpleName()
                        ));
                    }
                }
            })
            .check(classes);
    }
}

```

This approach catches ordering violations during testing rather than production.

## Summary

- **The `@Step` annotation** provides a `dependsOn` attribute in [`dddplus-runtime/src/main/java/io/github/dddplus/annotation/Step.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/annotation/Step.java) for declaring step dependencies, but this is purely documentary.
- **The `StepDef.registerBean` method** in [`dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/StepDef.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/StepDef.java) extracts `name` and `tags` while intentionally ignoring `dependsOn`, storing no dependency metadata in the registry.
- **The runtime execution** via `DDD.findSteps` and `StepsExecTemplate` executes steps in the exact order provided by the caller, performing no automatic reordering or validation.
- **The `ArchitectureEnforcer`** only verifies that steps carry the `@Step` annotation, not that dependencies are satisfied.
- **Developers must manually enforce** dependencies by either carefully maintaining execution lists or implementing custom validation utilities that perform topological sorting checks.

## Frequently Asked Questions

### Does DDDplus automatically reorder steps based on @Step dependsOn?

No, DDDplus does not automatically reorder steps. The `dependsOn` attribute is stored only in annotation metadata and is not read by `StepDef`, `InternalIndexer`, `DDD.findSteps`, or `StepsExecTemplate`. Steps execute in the exact order you provide to the execution template.

### How can I validate step dependencies before execution in DDDplus?

You can implement a validation utility that reads the `dependsOn` attribute via reflection and performs a topological sort check against your execution list. Call this validation inside your `StepsExecTemplate` implementation before invoking the step execution loop, or use ArchUnit rules to validate the dependency graph during CI/CD testing.

### Why is the dependsOn attribute deprecated in the @Step annotation?

The attribute is marked deprecated in [`Step.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/Step.java) because the framework core does not currently consume the dependency information automatically. It exists as a design-time hint for developers and custom tools, but since no automatic enforcement exists, the framework authors marked it to indicate limited runtime support.

### Where does DDDplus store step dependency information?

DDDplus does not store dependency information. The `StepDef` class in [`dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/StepDef.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/StepDef.java) explicitly ignores the `dependsOn` field during registration, and `InternalIndexer` indexes steps only by activity code and step code, creating no dependency graph structure.