IIdentity and IIdentityResolver: Extension Routing in the cp-ddd-framework
In the cp-ddd-framework, IIdentity represents the business data to be routed while IIdentityResolver provides the matching logic that determines which extension implementation should handle that specific identity, together forming the core extension routing mechanism that maps domain objects to concrete extension points.
The funkygao/cp-ddd-framework implements a dynamic extension routing system that selects business logic implementations based on domain context. At the heart of this system are two key interfaces: IIdentity and IIdentityResolver. Understanding how these components collaborate enables developers to build flexible, domain-driven extension points that route requests to the appropriate business logic based on runtime identity.
The Role of IIdentity in Extension Routing
IIdentity represents a business identity—a set of domain attributes that describe a concrete business scenario such as an order, SKU, or shipment. Rather than acting as a dedicated data transfer object, this interface is typically implemented by existing domain models within your application.
The interface resides in dddplus-spec/src/main/java/io/github/dddplus/ext/IIdentity.java and provides the foundation for all routing decisions. Any domain class can implement this interface to become routable:
public class ShipmentOrder implements IIdentity {
// Domain-specific attributes and methods
}
How IIdentityResolver Implements Routing Logic
IIdentityResolver serves as a business-identity matcher or plug-in that determines whether a given identity belongs to a specific business scenario. Defined in dddplus-spec/src/main/java/io/github/dddplus/ext/IIdentityResolver.java, this interface requires implementing a single match method that evaluates the identity against business rules.
Each resolver targets a specific identity type and implements custom matching logic:
public class PresaleResolver implements IIdentityResolver<ShipmentOrder> {
@Override
public boolean match(@NonNull ShipmentOrder identity) {
return identity.isPresale(); // Business rule evaluation
}
}
Routers aggregate multiple resolvers to handle complex routing scenarios. The framework supports both exclusive and cumulative matching strategies through the resolver composition pattern.
Coordinating Identity and Resolver in the Routing Flow
The interaction between IIdentity and IIdentityResolver follows a clear execution path from domain object to extension implementation. The framework provides two primary mechanisms for this coordination: the matchAny utility method and policy-based routing through IPolicy.
The matchAny Utility Method
The IIdentity interface includes a default matchAny method that enables cumulative routing strategies. This utility iterates over provided resolvers and returns true as soon as one matches, allowing identities to be tested against multiple scenarios efficiently.
As demonstrated in dddplus-test/src/test/java/io/github/dddplus/DesignTest.java:
@Test
void matchAny() {
// task implements IIdentity
assertFalse(task.matchAny(presalePattern));
assertTrue(task.matchAny(pledgePattern));
assertTrue(task.matchAny(presalePattern, pledgePattern));
}
This method enables routers to implement logic where an identity may satisfy multiple business patterns or require exclusive matching against specific resolvers.
Router Execution and Policy Resolution
At runtime, BaseRouter (or custom router implementations) orchestrates the lookup of concrete extensions based on resolver outcomes. When a policy is employed, the framework utilizes IPolicy, which represents a single-extension routing strategy embedding the matching logic directly.
The runtime resolution occurs in dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/PolicyDef.java:
final String extensionCode = policyBean.extensionCode(identity);
if (extensionCode == null) { /* no extension */ }
return extensionDefMap.get(extensionCode);
Here, the extensionCode method evaluates the identity—potentially using matchAny with configured resolvers—and returns the identifier used to retrieve the concrete extension bean from the registry.
Practical Implementation Example
The following implementation demonstrates the complete flow from identity definition through resolver creation to router configuration:
// 1️⃣ Define a business identity
public class FooIdentity implements IIdentity {
private final String sku;
public FooIdentity(String sku) { this.sku = sku; }
public String sku() { return sku; }
}
// 2️⃣ Implement a resolver for "premium" SKUs
public class PremiumResolver implements IIdentityResolver<FooIdentity> {
@Override
public boolean match(@NonNull FooIdentity identity) {
return identity.sku().startsWith("PREM-");
}
}
// 3️⃣ Use the resolver in a router via matchAny
public class FooRouter extends BaseRouter<IFooExt, FooIdentity> {
private final PremiumResolver premiumResolver = new PremiumResolver();
@Override
public String extensionCode(@NonNull FooIdentity identity) {
// If identity belongs to the premium scenario, bind to "premium" extension
if (identity.matchAny(premiumResolver)) {
return "premium";
}
// fallback
return "default";
}
}
Summary
IIdentityencapsulates the business scenario data that drives routing decisions, typically implemented by existing domain models.IIdentityResolverprovides pluggable matching logic to identify whether an identity belongs to a specific business context.- The
matchAnymethod enables cumulative or exclusive routing strategies by iterating over multiple resolvers until a match is found. BaseRouterandPolicyDeforchestrate the final lookup of concrete extension implementations based on resolver outcomes and extension codes.
Frequently Asked Questions
What is the difference between IIdentity and IIdentityResolver?
IIdentity represents the data contract—what is being routed—containing the domain attributes that describe a business scenario. IIdentityResolver represents the decision logic—how to determine if an identity fits a specific pattern—implementing the match method that evaluates business rules against the identity's state.
How does the matchAny method work in extension routing?
The matchAny method is a default implementation in the IIdentity interface (defined in IIdentity.java) that accepts one or more IIdentityResolver instances. It iterates through the provided resolvers and returns true immediately upon the first successful match, enabling efficient short-circuit evaluation for cumulative routing scenarios.
Can multiple resolvers be used for the same identity?
Yes. The matchAny method specifically supports multiple resolvers, allowing a single identity to be evaluated against several business patterns simultaneously. This supports complex routing logic where an identity might qualify for multiple extension points or require cascading evaluation against different business rules.
What role does IPolicy play in the extension routing mechanism?
IPolicy represents a single-extension routing strategy defined in dddplus-spec/src/main/java/io/github/dddplus/ext/IPolicy.java. Unlike separate router configurations that use external resolvers, a policy implements extensionCode(IIdentity) directly to compute and return the extension code. The runtime invokes this method in PolicyDef.java to resolve the concrete extension implementation without requiring explicit resolver aggregation in the router class.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →