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

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

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:

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:

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) 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. 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →