# How DDDplus Enables Polymorphic Extension Execution with forEachExtension()

> Discover how DDDplus uses dynamic proxies and forEachExtension() to execute polymorphic extensions sequentially. Streamline your extension logic and aggregate results efficiently.

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

---

**DDDplus enables polymorphic extension execution by using dynamic proxies to route calls through multiple extension implementations sequentially, with the `forEachExtension()` method in `BaseRouter` serving as the entry point for iterating over matching extensions while applying reduction strategies to control execution flow and aggregate results.**

In the `funkygao/cp-ddd-framework` repository, DDDplus treats extension points as first-class polymorphic collaborators. The `BaseRouter` class provides a uniform mechanism to discover and execute multiple implementations of a domain extension interface, allowing business logic to vary based on runtime identity while maintaining clean architectural boundaries.

## Understanding Extension Points and Routers

In DDDplus, a **router** represents a single extension point. Each concrete router extends `BaseRouter` and declares two generic parameters: the **extension interface** (`Ext`) and the **business identity** (`Identity`). This design allows the framework to locate all Spring beans implementing the extension interface that match the given identity at runtime.

The router acts as a coordinator. It does not contain business logic itself; instead, it delegates to extension implementations discovered by the `InternalIndexer`. This separation enables polymorphic behavior where different business scenarios execute different code paths through the same uniform interface.

## The forEachExtension() Method Overloads

`BaseRouter` exposes three overloaded versions of `forEachExtension()` in [`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), each providing different levels of control over the polymorphic execution.

### No-Reducer Execution

The simplest overload executes **all** matching extensions and returns the result of the **last** one. This is useful when you need side effects across all extensions but do not need to aggregate results.

```java
protected Ext forEachExtension(@NonNull Identity identity) {
    return forEachExtension(identity, IReducer.allOf());
}

```

This method internally delegates to the reducer-based version using `IReducer.allOf()`, which never stops early and returns `null` as the reduced result (source: [BaseRouter.java L30-L32](https://github.com/funkygao/cp-ddd-framework/blob/master/dddplus-runtime/src/main/java/io/github/dddplus/runtime/BaseRouter.java)).

### Reducer-Controlled Execution

The second overload accepts an `IReducer` to control when to stop execution and how to combine results. This enables **fail-fast** scenarios or **aggregation** patterns.

```java
protected <R> Ext forEachExtension(@NonNull Identity identity,
                                  @NonNull IReducer<R> reducer) {
    return forEachExtension(identity, 0, reducer);
}

```

The `IReducer` interface defines two methods: `reduce(List<R> accumulatedResults)` for combining results, and `shouldStop(List<R> accumulatedResults)` for determining when to halt iteration (source: [BaseRouter.java L44-L46](https://github.com/funkygao/cp-ddd-framework/blob/master/dddplus-runtime/src/main/java/io/github/dddplus/runtime/BaseRouter.java)).

### Timeout-Protected Execution

The third overload adds a `timeoutInMs` parameter, aborting the entire chain if total execution exceeds the specified milliseconds. This protects against cascading latency in polymorphic extension chains.

```java
private <R> Ext forEachExtension(@NonNull Identity identity,
                                 int timeoutInMs,
                                 @NonNull IReducer<R> reducer) {
    Class<? extends IDomainExtension> extClazz =
        InternalIndexer.getBaseRouterExtDeclaration(this.getClass());
    return findExtension((Class<Ext>) extClazz, identity,
                         reducer, defaultExtension(identity), timeoutInMs);
}

```

All three overloads eventually delegate to this private method, which uses `InternalIndexer` to resolve the extension interface class and then invokes `findExtension()` to create the dynamic proxy (source: [BaseRouter.java L59-L62](https://github.com/funkygao/cp-ddd-framework/blob/master/dddplus-runtime/src/main/java/io/github/dddplus/runtime/BaseRouter.java)).

## Dynamic Proxy and ExtensionInvocationHandler

The actual polymorphic execution relies on a **JDK dynamic proxy** created by `ExtensionInvocationHandler` in [`dddplus-runtime/src/main/java/io/github/dddplus/runtime/ExtensionInvocationHandler.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/runtime/ExtensionInvocationHandler.java).

When `findExtension()` is called, it instantiates this handler with:
- The list of effective extensions discovered by `InternalIndexer.findEffectiveExtensions()`
- The `IReducer` instance
- The timeout configuration

The handler creates a proxy implementing the extension interface. When any method on this proxy is invoked, the handler's `invoke()` method executes the following logic (source: [ExtensionInvocationHandler.java L78-L96](https://github.com/funkygao/cp-ddd-framework/blob/master/dddplus-runtime/src/main/java/io/github/dddplus/runtime/ExtensionInvocationHandler.java)):

```java
for (ExtensionDef extensionDef : effectiveExts) {
    result = invokeExtension(extensionDef, method, args);
    accumulatedResults.add(result);
    if (reducer == null || reducer.shouldStop(accumulatedResults)) {
        break;
    }
}
return reducer != null ? reducer.reduce(accumulatedResults) : result;

```

This loop enables **true polymorphism**: the same method call (`allow(order)`) executes across multiple implementations (`StockCheckExt`, `BlacklistExt`, etc.), with the reducer determining whether to continue or halt based on intermediate results.

## The IReducer Contract for Result Aggregation

The `IReducer<R>` interface in [`dddplus-runtime/src/main/java/io/github/dddplus/runtime/IReducer.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/runtime/IReducer.java) defines the contract for controlling polymorphic execution flow (source: [IReducer.java L50-L71](https://github.com/funkygao/cp-ddd-framework/blob/master/dddplus-runtime/src/main/java/io/github/dddplus/runtime/IReducer.java)):

- **`R reduce(List<R> accumulatedResults)`**: Combines all collected results into a final value.
- **`boolean shouldStop(List<R> accumulatedResults)`**: Determines whether to stop iterating over remaining extensions.

DDDplus provides two built-in reducer implementations:

| Reducer | Behavior |
|---------|----------|
| `IReducer.allOf()` | Executes **all** extensions, never stops early, returns `null` (useful for side-effects only). |
| `IReducer.stopOnFirstMatch(predicate)` | Stops as soon as the predicate matches the **latest** result; returns that result. |

Custom reducers enable complex aggregation patterns, such as collecting validation errors from all extensions or implementing voting mechanisms across multiple business rules.

## Practical Implementation Example

The `OrderAllowShipExtRouter` in [`dddplus-test/src/test/java/ddd/plus/showcase/wms/domain/order/ext/OrderAllowShipExtRouter.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-test/src/test/java/ddd/plus/showcase/wms/domain/order/ext/OrderAllowShipExtRouter.java) demonstrates real-world polymorphic extension execution (source: [OrderAllowShipExtRouter.java L11-L28](https://github.com/funkygao/cp-ddd-framework/blob/master/dddplus-test/src/test/java/ddd/plus/showcase/wms/domain/order/ext/OrderAllowShipExtRouter.java)):

```java
@Router
public class OrderAllowShipExtRouter extends BaseRouter<IOrderAllowShipExt, Order> {

    public boolean allowShip(Order order) {
        // Stop as soon as an extension returns FALSE (disallow)
        Predicate<Boolean> stopper = allow -> allow != null && !allow;
        Boolean allow = forEachExtension(order, IReducer.stopOnFirstMatch(stopper))
                         .allow(order);
        // No extension → default to true
        return allow == null ? true : allow;
    }

    @Override
    public IOrderAllowShipExt defaultExtension(@NonNull Order identity) {
        return null; // no default behavior
    }
}

```

In this example, `forEachExtension(order, IReducer.stopOnFirstMatch(stopper))` creates a dynamic proxy for `IOrderAllowShipExt`. When `.allow(order)` is invoked on the proxy, the `ExtensionInvocationHandler` iterates through all matching extension beans (such as `StockCheckExt` or `BlacklistExt`). If any extension returns `false`, the reducer stops further execution immediately, implementing a fail-fast validation pattern.

## How Polymorphism Is Achieved

DDDplus achieves polymorphic extension execution through four architectural mechanisms:

* **Interface-based contracts** – Extensions implement a common Java interface (e.g., `IOrderAllowShipExt`), ensuring type safety while allowing diverse implementations.

* **Dynamic proxy pattern** – The router never references concrete extension classes. Instead, `ExtensionInvocationHandler` creates a JDK dynamic proxy that forwards method calls to discovered beans at runtime.

* **Reducer-driven dispatch** – The same `forEachExtension` method supports multiple execution semantics—side effects, first-match, or custom aggregation—by accepting different `IReducer` implementations.

* **Runtime discovery** – `InternalIndexer` maintains a registry of extension beans and resolves effective extensions based on the business identity passed to the router, enabling true polymorphism where the execution path varies by context.

These mechanisms collectively provide a **uniform, extensible, and type-safe** approach to polymorphic extension execution in domain-driven design applications.

## Summary

- **Polymorphic extension execution** in DDDplus relies on the `BaseRouter.forEachExtension()` method to coordinate multiple implementations of a domain extension interface.
- The method supports **three overloads**: no-reducer (execute all), with-reducer (controlled execution), and with-timeout (latency protection).
- **Dynamic proxies** created by `ExtensionInvocationHandler` enable the router to invoke extension methods without knowing concrete implementation classes.
- The **`IReducer` contract** determines when to stop iterating over extensions and how to combine their results, supporting patterns like fail-first or full aggregation.
- **Runtime discovery** via `InternalIndexer` ensures that only extensions matching the current business identity participate in the polymorphic execution chain.

## Frequently Asked Questions

### How does `forEachExtension()` handle multiple extension implementations?

`forEachExtension()` uses the `ExtensionInvocationHandler` to create a dynamic proxy that iterates over all effective extension implementations discovered by `InternalIndexer`. When a method is called on the proxy, the handler invokes that method on each extension bean sequentially, collecting results in a list. The process continues until either all extensions have been executed or the provided `IReducer` signals to stop via its `shouldStop()` method.

### What is the purpose of the `IReducer` in polymorphic extension execution?

The `IReducer` serves as a control mechanism for the extension execution loop. It defines two responsibilities: determining when to stop iterating over extensions (`shouldStop()`) and how to combine multiple results into a single return value (`reduce()`). This enables different execution semantics—such as fail-fast (stop on first false), collect-all (gather all validation errors), or first-match—using the same `forEachExtension()` infrastructure.

### Can `forEachExtension()` enforce a timeout across all extension calls?

Yes. The third overload of `forEachExtension()` accepts a `timeoutInMs` parameter that applies to the entire execution chain. When specified, the `ExtensionInvocationHandler` monitors the total execution time across all extension invocations. If the cumulative execution exceeds the specified timeout, the framework aborts the remaining extensions and returns the results accumulated up to that point, protecting the system from cascading latency issues in polymorphic chains.

### How does DDDplus discover which extensions to execute for a specific identity?

Extension discovery is handled by `InternalIndexer`, which maintains a runtime registry of all `@Extension` annotated beans. When `forEachExtension()` is invoked with a business identity, `InternalIndexer.findEffectiveExtensions()` filters the registered extensions to find those whose declared identity constraints match the provided identity object. Only these matching extensions participate in the polymorphic execution, ensuring that business logic varies correctly by context while maintaining type safety through the extension interface contract.