# How to Implement an IDomainExtension with Multiple Routing Modes in cp-ddd-framework

> Master IDomainExtension with cp-ddd-framework. Learn to implement multiple routing modes using all-match first-match or predicate-stop strategies for efficient selection and execution.

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

---

**Implement an `IDomainExtension` by defining an interface that extends `IDomainExtension`, annotating concrete Spring beans with `@Extension`, extending `BaseRouter` to handle selection logic, and choosing between all-match, first-match, or predicate-stop execution strategies via the router's API.**

The **cp-ddd-framework** provides a robust extension mechanism that allows business logic to vary based on identity patterns, partners, or policies. An `IDomainExtension` serves as the core extension-point interface, and by leveraging the `BaseRouter` class, you can implement extensions that support multiple routing modes—from executing all matching implementations to selecting specific ones based on runtime conditions.

## Understanding the Three Routing Modes

The `BaseRouter` class 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) provides three distinct routing strategies for invoking extensions. Each mode serves different architectural needs:

- **All-match** (`forEachExtension(identity)`): Executes **all** matching extension implementations without stopping. Use this mode for side effects, logging, or accumulating results across multiple extensions.

- **First-match** (`firstExtension(identity)`): Returns the **first** matching implementation and executes it exclusively. This mode enforces mutual exclusion when you need exactly one extension to handle a specific identity.

- **Predicate-stop** (`forEachExtension(identity, IReducer.stopOnFirstMatch(predicate))`): Executes implementations sequentially until a predicate condition is satisfied, then stops immediately. This "fail-fast" mode is ideal for validation checks or permission gates.

According to the cp-ddd-framework source code, the routing logic is handled by `ExtensionInvocationHandler`, which iterates over indexed `ExtensionDef` instances and applies the supplied `IReducer` to determine when to stop execution.

## Step-by-Step Implementation

### 1. Define the Extension Interface

Create an interface that extends `IDomainExtension`. All methods must return wrapper types (not primitives) or `void`, as enforced by `ExtensionMethodSignatureEnforcer`.

```java
// src/main/java/com/example/ext/IGreetingExt.java
import io.github.dddplus.ext.IDomainExtension;

public interface IGreetingExt extends IDomainExtension {
    /** Returns a greeting for the given user name. */
    String greet(String userName);
}

```

Reference the interface definition in [`dddplus-spec/src/main/java/io/github/dddplus/ext/IDomainExtension.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-spec/src/main/java/io/github/dddplus/ext/IDomainExtension.java).

### 2. Annotate Concrete Implementations

Create Spring beans annotated with `@Extension`. The `code` attribute must correspond to a pattern, partner, or policy code defined elsewhere in your application (e.g., `FooPattern.CODE`).

```java
// src/main/java/com/example/ext/EnglishGreetingExt.java
import io.github.dddplus.annotation.Extension;

@Extension(code = FooPattern.CODE, name = "English greeting")
public class EnglishGreetingExt implements IGreetingExt {
    @Override
    public String greet(String userName) {
        return "Hello, " + userName;
    }
}

// src/main/java/com/example/ext/ChineseGreetingExt.java
@Extension(code = FooPattern.CODE, name = "Chinese greeting")
public class ChineseGreetingExt implements IGreetingExt {
    @Override
    public String greet(String userName) {
        return "你好，" + userName;
    }
}

```

The `@Extension` annotation inherits from `@Component`, so Spring automatically discovers these beans at startup. See the annotation definition in [`dddplus-runtime/src/main/java/io/github/dddplus/annotation/Extension.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/annotation/Extension.java).

### 3. Configure the Default Extension

Provide a fallback implementation that executes when no specific extension matches the identity. Create a no-op bean or a sensible default and reference it in your router.

```java
// Default no-op implementation
@Extension(code = IGreetingExt.DefaultCode)
public class DefaultGreetingExt implements IGreetingExt {
    @Override
    public String greet(String userName) {
        return null; // Safe because String is a wrapper type
    }
}

```

### 4. Extend BaseRouter

Create an abstract router class that extends `BaseRouter<YourExtensionType, YourIdentityType>`. Implement the `defaultExtension(Identity)` method to return your fallback bean.

```java
// src/main/java/com/example/router/GreetingRouter.java
import io.github.dddplus.runtime.BaseRouter;
import io.github.dddplus.runtime.DDD;

public abstract class GreetingRouter 
        extends BaseRouter<IGreetingExt, UserIdentity> {

    @Override
    public IGreetingExt defaultExtension(@NonNull UserIdentity identity) {
        // Retrieve the default extension via DDD.usePolicy or direct injection
        return DDD.usePolicy(DefaultGreetingPolicy.class, identity);
    }
}

```

The router resolves the extension class (`extClazz`) via `InternalIndexer.getBaseRouterExtDeclaration(...)`, defined in [`dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/InternalIndexer.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/InternalIndexer.java).

### 5. Execute with Different Routing Strategies

Use the router in your service layer according to the required routing mode.

**All-match execution** (side effects only):

```java
public void logAllGreetings(String userName, UserIdentity identity) {
    GreetingRouter router = DDD.usePolicy(GreetingPolicy.class, identity);
    router.forEachExtension(identity)
          .greet(userName); // All matching extensions execute
}

```

**First-match execution** (mutually exclusive):

```java
public String getLocalizedGreeting(String userName, UserIdentity identity) {
    GreetingRouter router = DDD.usePolicy(GreetingPolicy.class, identity);
    IGreetingExt ext = router.firstExtension(identity);
    return ext.greet(userName); // Only the first matching extension runs
}

```

**Predicate-stop execution** (fail-fast):

```java
public String findFirstNonNullGreeting(String userName, UserIdentity identity) {
    GreetingRouter router = DDD.usePolicy(GreetingPolicy.class, identity);
    return router.forEachExtension(identity, 
               IReducer.stopOnFirstMatch(msg -> msg != null))
                 .greet(userName);
}

```

The `IReducer` utilities are defined 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).

## Extension Discovery and Routing Internals

The framework indexes all extensions at startup through `InternalIndexer`, which builds maps (`sortedPatternMap`, `policyDefMap`, `partnerDefMap`) keyed by the generic extension class. When you invoke a router method, `BaseRouter.findExtension` creates an `ExtensionInvocationHandler` that iterates over the indexed `ExtensionDef` instances.

The proxy invokes each bean dynamically and applies the `IReducer` to determine whether to continue iteration. If no concrete extension matches the identity, the router falls back to the `defaultExtension` implementation. Because extension methods must return wrapper types or `void`, returning `null` from a default implementation is type-safe and will not cause primitive unboxing errors.

## Summary

- **Define** an interface extending `IDomainExtension` with wrapper return types only.
- **Annotate** concrete implementations with `@Extension(code = "...")` to enable Spring discovery and indexing.
- **Extend** `BaseRouter` and implement `defaultExtension()` to provide a safe fallback.
- **Choose** `forEachExtension()` for all-match scenarios, `firstExtension()` for exclusive selection, or `forEachExtension(identity, IReducer)` for predicate-based stopping.
- **Reference** specific source files ([`BaseRouter.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/BaseRouter.java), [`InternalIndexer.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/InternalIndexer.java), [`ExtensionInvocationHandler.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/ExtensionInvocationHandler.java)) to understand the routing pipeline.

## Frequently Asked Questions

### What happens if no extension matches the identity and no default is provided?

The router returns `null` from `firstExtension()`, which will cause a `NullPointerException` if you attempt to invoke methods on it. Always implement `defaultExtension()` in your router class to return a no-op implementation or throw a meaningful business exception.

### Can I use primitive types in IDomainExtension method signatures?

No. The framework enforces that all extension methods must return wrapper types (e.g., `Integer` instead of `int`) or `void`. This requirement is validated by `ExtensionMethodSignatureEnforcer` to ensure that default extensions can safely return `null` without causing unboxing failures.

### How do I implement custom aggregation logic beyond the built-in reducers?

Implement the `IReducer` interface with custom `reduce()` and `shouldStop()` methods. Pass your implementation to `forEachExtension(identity, customReducer)` to control how results are accumulated and when the iteration should terminate. This approach supports complex scenarios like collecting partial results from multiple extensions until a specific threshold is reached.

### Why does BaseRouter require an abstract class rather than a concrete one?

`BaseRouter` uses generics to bind the extension type and identity type, and it relies on Spring's component scanning to instantiate the actual router beans. By declaring your router as an abstract class extending `BaseRouter`, you allow the framework to generate a concrete proxy that handles the `ExtensionInvocationHandler` logic while you provide only the `defaultExtension()` configuration.