# How IReducer Enables Early Termination in Extension Execution Loops

> Discover how IReducer enables early termination in extension execution loops, preventing unnecessary processing. Learn when to use shouldStop() for efficient extension handling.

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

---

**The `IReducer` interface allows the `ExtensionInvocationHandler` to break out of the extension execution loop as soon as its `shouldStop()` method returns `true`, preventing unnecessary processing of remaining extensions.**

In the `funkygao/cp-ddd-framework` (CP-DDD) runtime, domain extensions are invoked sequentially via a dynamic proxy. When multiple implementations of an extension point exist, the framework uses an `IReducer` to determine whether to continue iterating or halt early based on accumulated results. This mechanism is critical for performance-sensitive operations like fail-fast validation or first-match lookups.

## The IReducer Contract: shouldStop and reduce

The `IReducer<R>` interface defines two responsibilities: aggregating final results and signaling early termination. Located 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) (lines 22–71), the contract is:

```java
public interface IReducer<R> {
    R reduce(List<R> accumulatedResults);          // produce the final value
    boolean shouldStop(List<R> accumulatedResults); // true → break the loop
}

```

- **`reduce`** is called after the loop finishes (or breaks) to transform the list of intermediate results into a single return value.
- **`shouldStop`** is consulted **after each extension execution**. If it returns `true`, the loop in `ExtensionInvocationHandler` terminates immediately, and no further extensions are invoked.

## Where Early Termination Happens: ExtensionInvocationHandler

The actual execution loop resides in [`ExtensionInvocationHandler.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/ExtensionInvocationHandler.java) (lines 64–88). After each extension is invoked via reflection, its result is appended to `accumulatedResults`. The code then checks the reducer:

```java
// Inside ExtensionInvocationHandler.invoke()
for (Object ext : extensions) {
    // ... invoke extension ...
    R result = (R) method.invoke(ext, args);
    accumulatedResults.add(result);
    
    if (reducer == null || reducer.shouldStop(accumulatedResults)) {
        break;  // ← Early termination point
    }
}

```

If `reducer` is null, the framework defaults to running all extensions. When a reducer is provided and `shouldStop` returns `true`, the `break` statement exits the `for-each` loop, skipping any remaining extension implementations.

## Built-in Reducers That Enable Early Exit

CP-DDD provides several static factory methods in [`IReducer.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/IReducer.java) for common termination strategies.

### stopOnFirstMatch

`IReducer.stopOnFirstMatch(Predicate<R>)` is the primary mechanism for early exit. It returns the last accumulated result (the "tail") and stops as soon as that tail satisfies the predicate.

Implementation (lines 50–71):

```java
public static <R> IReducer<R> stopOnFirstMatch(Predicate<R> predicate) {
    return new IReducer<R>() {
        @Override
        public R reduce(List<R> accumulatedResults) {
            return tail(accumulatedResults); // return last element
        }
        
        @Override
        public boolean shouldStop(List<R> accumulatedResults) {
            R tail = tail(accumulatedResults);
            return tail != null && predicate.test(tail); // stop when predicate matches
        }
    };
}

```

### allOf (Non-terminating)

`IReducer.allOf()` is the opposite: it aggregates all results and never triggers early termination. Its `shouldStop` always returns `false`, ensuring every extension implementation executes.

```java
public static <R> IReducer<R> allOf() {
    return new IReducer<R>() {
        @Override
        public R reduce(List<R> accumulatedResults) { return null; }
        
        @Override
        public boolean shouldStop(List<R> accumulatedResults) { return false; }
    };
}

```

## Real-World Scenarios for Early Termination

The `IReducer` pattern enables several domain-driven design patterns where short-circuiting extension execution improves performance and correctness.

### Fail-Fast Validation

In order management, multiple extensions may check if an order is eligible for shipping. If any extension returns `false`, the operation should abort immediately.

From [`OrderAllowShipExtRouter.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/OrderAllowShipExtRouter.java) (lines 16–22):

```java
public Boolean allowShipping(Order order) {
    Predicate<Boolean> stopper = allow -> Boolean.FALSE.equals(allow);
    // Stop as soon as any extension returns false
    Boolean allow = forEachExtension(order, IReducer.stopOnFirstMatch(stopper))
                        .execute(order);
    return allow != null ? allow : Boolean.TRUE;
}

```

Here, `IReducer.stopOnFirstMatch` enables early termination the moment a single extension vetoes the shipment, preventing unnecessary processing of subsequent policies.

### First-Match Lookup

When selecting a shipping carrier, you might have multiple extensions each claiming they can handle the order. You only need the first one that returns a non-null carrier.

```java
Predicate<Carrier> firstAvailable = carrier -> carrier != null;
Carrier carrier = forEachExtension(order, IReducer.stopOnFirstMatch(firstAvailable))
                      .execute(order);

```

The loop terminates as soon as the first extension returns a valid `Carrier` object, avoiding redundant invocations.

### Custom Threshold Logic

You can implement domain-specific aggregation rules, such as "stop after three successful validations" or "stop when the cumulative risk score exceeds 100."

```java
class RiskThresholdReducer implements IReducer<Integer> {
    private final int threshold;
    
    public RiskThresholdReducer(int threshold) { this.threshold = threshold; }
    
    @Override
    public Integer reduce(List<Integer> results) { 
        return results.stream().mapToInt(i -> i).sum(); 
    }
    
    @Override
    public boolean shouldStop(List<Integer> results) {
        return results.stream().mapToInt(i -> i).sum() >= threshold;
    }
}

// Usage
int risk = forEachExtension(context, new RiskThresholdReducer(100))
               .calculateRisk(context);

```

## Summary

- **`IReducer`** is the control mechanism that allows the CP-DDD framework to terminate extension execution loops early via its `shouldStop(List<R>)` method.
- Early termination occurs in **[`ExtensionInvocationHandler.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/ExtensionInvocationHandler.java)** (lines 64–88) when `reducer.shouldStop(accumulatedResults)` returns `true`, triggering a `break` statement.
- **`IReducer.stopOnFirstMatch(Predicate)`** is the built-in implementation for fail-fast scenarios, stopping the loop when the predicate matches the latest result.
- **`IReducer.allOf()`** never stops early, ensuring all extensions execute for full aggregation.
- Real-world applications include fail-fast validation (e.g., `OrderAllowShipExtRouter`), first-match lookups, and custom threshold logic.

## Frequently Asked Questions

### What is the difference between reduce() and shouldStop() in IReducer?

The `reduce()` method aggregates the final list of results into a single return value after the loop finishes or breaks. The `shouldStop()` method is checked after every extension execution to determine whether to break the loop early. While `reduce()` handles post-processing, `shouldStop()` controls runtime flow.

### How does stopOnFirstMatch work internally?

`stopOnFirstMatch` creates an anonymous `IReducer` implementation whose `shouldStop` method retrieves the last element (tail) of `accumulatedResults` and tests it against the provided predicate. If the predicate returns `true`, `shouldStop` returns `true`, causing `ExtensionInvocationHandler` to break the loop. The `reduce` method simply returns that tail element.

### Can I implement custom early termination logic?

Yes. You can implement the `IReducer` interface directly to define domain-specific stop conditions. For example, you might stop after a specific number of successful results, when a cumulative score exceeds a threshold, or when a particular pattern emerges across multiple extension results. Pass your custom instance to `BaseRouter.forEachExtension()`.

### Where is the extension execution loop located?

The loop resides in `ExtensionInvocationHandler.invoke()` within the `dddplus-runtime` module, specifically lines 64–88. This class acts as a dynamic proxy that iterates over all registered extension implementations, invokes the method via reflection, and checks the `IReducer.shouldStop()` condition after each invocation to determine whether to continue or break.