# Architectural Differences Between BaseRouter and BaseDecideStepsRouter in DDDplus

> Explore the architectural differences between BaseRouter and BaseDecideStepsRouter in DDDplus. Understand their roles and how each handles extension-point routing for your domain-driven design.

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

---

**BaseRouter is the abstract generic foundation for all extension-point routing in DDDplus that requires subclasses to implement `defaultExtension`, while BaseDecideStepsRouter is a deprecated specialization that fixes the extension type to `IDecideStepsExt` and provides a concrete `decideSteps` convenience method with a static empty default implementation.**

The `dddplus-runtime` module in the `funkygao/cp-ddd-framework` repository provides the core plugin architecture for domain-driven design extensions. Understanding the architectural differences between BaseRouter and BaseDecideStepsRouter is essential for implementing proper extension-point routing, as one serves as the universal pluggable foundation while the other represents a legacy convenience layer for specific decision-step workflows.

## Core Architectural Distinctions

### Intent and Design Philosophy

**BaseRouter** ([`dddplus-runtime/src/main/java/io/github/dddplus/runtime/BaseRouter.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/runtime/BaseRouter.java)) serves as the universal routing engine for any extension point in the DDDplus framework. It encapsulates the generic mechanics of locating, iterating, and reducing extensions based on a business **Identity**, leaving the concrete business semantics entirely to subclasses.

**BaseDecideStepsRouter** ([`dddplus-runtime/src/main/java/io/github/dddplus/runtime/BaseDecideStepsRouter.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/runtime/BaseDecideStepsRouter.java)) is a thin convenience layer built specifically for the `IDecideStepsExt` extension point. It provides a high-level `decideSteps` method that internally delegates to `firstExtension(identity).decideSteps(...)`, hiding the generic routing details behind a domain-specific API.

### Generic Type Constraints

The generic signatures reveal the fundamental architectural split:

- **BaseRouter**: `abstract class BaseRouter<Ext extends IDomainExtension, Identity extends IIdentity>` remains fully generic on both the extension type and identity type.
- **BaseDecideStepsRouter**: `abstract class BaseDecideStepsRouter<Identity extends IIdentity> extends BaseRouter<IDecideStepsExt, Identity>` fixes the `Ext` parameter to `IDecideStepsExt`, constraining the router to only handle decision-step extensions.

### Extension Contract Implementation

In [`BaseRouter.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/BaseRouter.java) at line 100, the `defaultExtension` method is declared **abstract**, forcing every concrete subclass to provide a domain-specific fallback implementation when no matching extension is found.

Conversely, [`BaseDecideStepsRouter.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/BaseDecideStepsRouter.java) at lines 35-38 provides a **static empty implementation** (`EmptyExt`) that returns an empty list of steps. This ensures callers never receive a `null` proxy, though the class is marked **@Deprecated** at line 20 because the framework now encourages developers to use the generic `BaseRouter` directly rather than this specialized wrapper.

## Source Code Implementation Details

### BaseRouter Foundation

`BaseRouter` provides three primary routing utilities:

- `forEachExtension(identity)` – iterates all matching extensions with optional timeout and reduction strategies.
- `firstExtension(identity)` – retrieves the first matching extension.
- `defaultExtension(identity)` – **must** be implemented by subclasses (abstract method at line 100).

The class also includes built-in support for per-router timeout configuration and custom reduction logic via `IReducer`, implemented in the private method `forEachExtension(identity, timeoutInMs, reducer)` at line 59.

### BaseDecideStepsRouter Specialization

`BaseDecideStepsRouter` inherits all timeout and reduction capabilities from `BaseRouter` but exposes a simplified public API:

- `decideSteps(identity, activityCode)` – high-level business method that internally calls `firstExtension(identity).decideSteps(...)`.

Because it extends `BaseRouter<IDecideStepsExt, Identity>`, subclasses can only plug in implementations of `IDecideStepsExt`, and the decision-step logic is fixed to returning a `List<String>` of step codes.

## Practical Implementation Patterns

### Extending BaseRouter for Custom Extension Points

When implementing a router for a custom extension point, you must provide the concrete extension type and implement the abstract `defaultExtension` method:

```java
public class OrderAllowShipRouter
        extends BaseRouter<IOrderAllowShipExt, OrderIdentity> {

    @Override
    public IOrderAllowShipExt defaultExtension(@NonNull OrderIdentity identity) {
        return new DefaultOrderAllowShipExt();   // fallback logic
    }

    public boolean allowShip(@NonNull OrderIdentity id) {
        // Execute all matching extensions; stop when one returns true
        return forEachExtension(id, IReducer.firstTrue()).allowShip(id);
    }
}

```

Key implementation points:
- Declare the concrete extension type (`IOrderAllowShipExt`) in the class signature.
- Provide a **defaultExtension** implementation as required by the abstract base class.
- Use the generic `forEachExtension` with a custom `IReducer` to implement early-exit behavior.

### Legacy Usage of BaseDecideStepsRouter

While deprecated, existing code may still use this router for decision-step workflows:

```java
public class OrderStepRouter
        extends BaseDecideStepsRouter<OrderIdentity> {

    @Override
    public IDecideStepsExt defaultExtension(@NonNull OrderIdentity identity) {
        // Inherit the empty implementation or supply a real one
        return super.defaultExtension(identity);
    }
}

```

Client code interacts through the simplified API:

```java
OrderIdentity id = OrderIdentity.of(orderId);
List<String> steps = new OrderStepRouter().decideSteps(id, "CREATE_ORDER");

```

Because `BaseDecideStepsRouter` fixes the extension type, the router only needs to supply (or inherit) a default extension, and the public API exposes only the `decideSteps` method.

## Summary

- **BaseRouter** is the universal, pluggable routing engine for any DDDplus extension point, requiring subclasses to define both the extension type and default implementation.
- **BaseDecideStepsRouter** is a deprecated convenience wrapper that fixes the extension type to `IDecideStepsExt` and provides a static empty default implementation to prevent null proxies.
- **BaseRouter** exposes generic iteration utilities (`forEachExtension`, `firstExtension`) with full support for timeout and reduction strategies via `IReducer`.
- **BaseDecideStepsRouter** offers a single high-level method `decideSteps` that hides the underlying routing mechanics, but the framework recommends migrating to direct `BaseRouter` usage.
- Both classes rely on [`RouterDef.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/RouterDef.java) for runtime registry linking and participate in the same extension discovery mechanism, though `BaseDecideStepsRouter` constrains extensibility to decision-step scenarios only.

## Frequently Asked Questions

### Why is BaseDecideStepsRouter marked as deprecated?

According to the source code in [`BaseDecideStepsRouter.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/BaseDecideStepsRouter.java) at line 20, the class is annotated with **@Deprecated** because the DDDplus framework evolution favors using the generic `BaseRouter` directly for all extension points. The specialized router added unnecessary abstraction when the generic foundation already provides sufficient flexibility for decision-step routing through the standard extension mechanism.

### Can I use BaseRouter instead of BaseDecideStepsRouter for decision steps?

Yes, and this is the recommended approach. You can extend `BaseRouter<IDecideStepsExt, YourIdentity>` directly, implementing the `defaultExtension` method to return either a real implementation or an empty list. This approach provides full control over reduction strategies and timeout configurations that `BaseDecideStepsRouter` obscures behind its simplified `decideSteps` facade.

### What is the EmptyExt implementation in BaseDecideStepsRouter?

`EmptyExt` is a static inner class defined in [`BaseDecideStepsRouter.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/BaseDecideStepsRouter.java) (lines 35-38) that implements `IDecideStepsExt` and returns an empty list from its `decideSteps` method. This singleton pattern ensures that routers always have a valid extension instance to invoke, preventing `NullPointerException` when no domain-specific extension is registered, though it effectively results in a no-op execution.

### How do reduction strategies differ between these routers?

`BaseRouter` explicitly supports custom reduction via the `IReducer` interface, allowing implementations to specify early-exit conditions (such as `IReducer.firstTrue()`) when iterating extensions. `BaseDecideStepsRouter` inherits these capabilities internally but does not expose them through its public API; the `decideSteps` method simply delegates to `firstExtension`, which returns only the first matching extension without applying complex reduction logic across multiple extensions.