# Configuring Default Extension Fallback in DDD-Plus: A Complete Guide

> Learn how to configure default extension fallback in DDD-Plus with this complete guide. Annotate your class with @Extension(code = IDomainExtension.DefaultCode) to ensure seamless execution when no specific extension matches.

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

---

**To configure a default extension fallback in the cp-ddd-framework, annotate a class with `@Extension(code = IDomainExtension.DefaultCode)` and optionally return `null` from policy methods to trigger the fallback when no specific extension matches.**

The `funkygao/cp-ddd-framework` (DDD-Plus) provides a robust extension point mechanism for domain-driven design applications. When the framework cannot resolve a specific extension through policy, pattern, or partner lookups, it automatically falls back to a default implementation. Understanding the configuration options for setting up a default extension fallback ensures your application handles unmatched identities gracefully.

## How Extension Resolution Works in DDD-Plus

The framework resolves extensions through a three-step hierarchy defined in `InternalIndexer.findEffectiveExtensions`:

1. **Policy-driven lookup** – An `IPolicy` implementation returns a specific extension code.
2. **Pattern-based lookup** – The framework iterates through ordered `PatternDef` objects matching the identity.
3. **Partner-based lookup** – The framework checks `PartnerDef` definitions for the identity.

If all three steps return no extension, the framework invokes the **default extension fallback** mechanism.

## Configuration Options for Default Extension Fallback

The fallback behavior is controlled through two primary configuration elements: the default extension implementation itself and policy return values.

### Implementing the Default Extension Class

Register a default extension by implementing the target extension interface and annotating it with the special default code constant.

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) (lines 288-292), the registration logic stores extensions marked with `IDomainExtension.DefaultCode` separately from pattern and partner maps:

```java
package com.myapp.ext;

import io.github.dddplus.ext.Extension;
import io.github.dddplus.ext.IDomainExtension;

@Extension(code = IDomainExtension.DefaultCode)
public class DefaultOrderExt implements IOrderExt {
    @Override
    public void process(Order order) {
        // Generic processing for orders without specific extensions
        System.out.println("Processing with default extension");
    }
}

```

**Key constraints:**
- Only one default extension per extension point can be registered in the core module.
- The class is stored outside the `PatternDef` and `PartnerDef` maps, ensuring it is consulted only when primary lookups fail.

### Configuring Policy Methods to Allow Fallback

Control when the fallback triggers by returning `null` from `IPolicy.extensionCode()` methods.

In `InternalIndexer.findEffectiveExtensions` (lines 121-136), a `null` return value from the policy causes the framework to skip the policy branch and proceed to pattern/partner lookups:

```java
package com.myapp.policy;

import io.github.dddplus.annotation.Policy;
import io.github.dddplus.ext.IPolicy;
import lombok.NonNull;

@Policy
public class OrderPolicy implements IPolicy<IOrderExt, Order> {
    @Override
    public String extensionCode(@NonNull Order identity) {
        if (identity.isPriorityCustomer()) {
            return "priorityExtension";
        }
        // Return null to trigger default fallback through pattern/partner lookup
        return null;
    }
}

```

When the policy returns `null` and no patterns or partners match, the framework automatically selects the default extension registered with `IDomainExtension.DefaultCode`.

## Advanced: Intercepting Fallback Execution

For custom logging or runtime override behavior, implement `IExtensionInterceptor` to detect when the fallback is invoked. The interceptor is invoked before the extension executes, allowing you to identify default code usage:

```java
package com.myapp.interceptor;

import io.github.dddplus.runtime.interceptor.IExtensionInterceptor;
import io.github.dddplus.ext.IDomainExtension;
import io.github.dddplus.ext.IIdentity;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class FallbackLoggingInterceptor implements IExtensionInterceptor {
    @Override
    public void beforeExtension(IDomainExtension ext, IIdentity identity) {
        if (IDomainExtension.DefaultCode.equals(ext.getCode())) {
            log.info("Fallback to default extension {} for identity {}", 
                ext.getClass().getSimpleName(), identity);
        }
    }
}

```

Register the interceptor through your plugin configuration (typically [`plugin.xml`](https://github.com/funkygao/cp-ddd-framework/blob/main/plugin.xml)) so that `InternalIndexer.index(InterceptorDef)` processes it automatically.

## Summary

Configuring a default extension fallback in the cp-ddd-framework requires understanding the resolution hierarchy and two specific configuration points:

- **Annotate the default implementation** with `@Extension(code = IDomainExtension.DefaultCode)` to register it as the fallback in `InternalIndexer`.
- **Return `null` from policies** when you want to defer to the fallback mechanism rather than forcing a specific extension.
- **Optional:** Use `IExtensionInterceptor` to monitor or customize fallback behavior at runtime.

These configurations ensure that when policy, pattern, and partner lookups fail to identify a specific extension, your application gracefully falls back to a default implementation.

## Frequently Asked Questions

### What happens if no default extension is configured?

If no class is annotated with `@Extension(code = IDomainExtension.DefaultCode)`, the framework will throw an exception or return `null` when attempting to resolve an extension that has no matching policy, pattern, or partner definition. The `InternalIndexer` stores default extensions separately, and if none exists, the fallback path cannot complete successfully.

### Can I have multiple default extensions for the same extension point?

No. The core module allows only one default extension per extension point. While multiple plugins might attempt to register a default implementation, the first one loaded wins during the indexing phase in `InternalIndexer.index(ExtensionDef)`. The framework does not enforce uniqueness across plugins, so conflicting defaults result in non-deterministic behavior based on loading order.

### How do I override the default extension at runtime?

To override or customize fallback behavior at runtime, implement the `IExtensionInterceptor` interface. The interceptor's `beforeExtension` method receives the extension instance and identity, allowing you to substitute a different implementation or add logging when `IDomainExtension.DefaultCode` is detected. Register the interceptor via your plugin configuration to ensure `InternalIndexer` processes it during startup.

### Where is the fallback logic implemented in the source code?

The fallback logic resides primarily 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). Specifically, lines 288-292 handle the registration of default extensions using `IDomainExtension.DefaultCode`, while lines 121-136 in `findEffectiveExtensions` implement the lookup flow that falls back to the default when policy, pattern, and partner lookups return no results.