How InternalIndexer Organizes and Post-Indexes Registered Domain Artifacts in DDDplus
The InternalIndexer serves as DDDplus's central in-memory registry that automatically collects, categorizes, and exports all domain artifacts during Spring bean initialization, enabling deterministic resolution of extensions, steps, and policies at runtime.
The InternalIndexer is the backbone of artifact management in the DDDplus framework (funkygao/cp-ddd-framework), responsible for organizing registered domain artifacts into a queryable structure. This component operates during the bootstrap phase to index domains, steps, routers, extensions, patterns, partners, and policies, creating a lightweight, immutable snapshot of the domain model for downstream consumption.
How InternalIndexer Collects Domain Artifacts During Registration
During Spring context initialization, each domain artifact implements IRegistryAware and registers itself with the InternalIndexer through overloaded index() methods. This process populates distinct concurrent hash maps that isolate artifacts by type for thread-safe access.
Indexing Domain Definitions
When the Spring container instantiates a class annotated with @Domain, the DomainDef object invokes InternalIndexer.index(DomainDef) in dddplus-runtime/src/main/java/io/github/dddplus/runtime/registry/InternalIndexer.java. This stores the domain metadata in domainDefMap, mapping the domain code to its definition object.
Registering Steps and Routers
Domain steps follow a similar pattern. When a class implementing IDomainStep is annotated with @Step, the StepDef captures the activity code, step code, name, and tags before calling InternalIndexer.index(this). This populates domainStepDefMap, enabling later retrieval of steps by activity code.
Routers extending BaseRouter are indexed via InternalIndexer.index(RouterDef), which stores definitions in routerDefMap for runtime routing decisions.
Handling Extensions, Policies, and Patterns
Extensions represent the most complex indexing logic. When ExtensionDef registers itself, the indexer determines whether it belongs to a Policy, Pattern, or Partner route:
- Policies are stored in
policyDefMapandpolicyClazzMap, linking policy classes to their extension interfaces. - Patterns are accumulated in
patternDefMapbefore being sorted by priority in the post-indexing phase. - Partners are maintained in the concurrent
partnerDefMap, keyed by partner code.
Interceptors implementing IExtensionInterceptor can be registered once via index(InterceptorDef).
The Post-Indexing Phase: Sorting and Exporting Artifacts
After all Spring beans initialize, DDDBootstrap triggers InternalIndexer.postIndexing(). This method transitions the system from registration mode to runtime mode through two critical operations.
Pattern Prioritization and Cleanup
First, the indexer transfers all PatternDef objects from patternDefMap to sortedPatternMap, sorting them by their priority value. This ensures deterministic resolution where higher-priority patterns match before lower-priority ones during extension routing.
Immediately after sorting, patternDefMap is cleared to free memory, as the unsorted temporary storage is no longer needed at runtime.
Exporting to DomainArtifacts
The final post-indexing action calls DomainArtifacts.getInstance().export(). This utility reads the internal maps of InternalIndexer and constructs three immutable collections:
- Domains: A list of
Domainobjects containing codes and names. - Steps: A map of
activityCode → List<Step>with step codes, names, and tags. - Extensions: For each extension interface, a record of applicable patterns and partner implementations.
The resulting DomainArtifacts object provides a lightweight, read-only snapshot that external configuration centers and visualization dashboards can consume without accessing the mutable internal registries.
Runtime Lookup Helpers in InternalIndexer
Beyond registration and export, InternalIndexer provides static utility methods used by the DDDplus runtime to resolve artifacts dynamically:
findRouter(Class<? extends BaseRouter>)– Retrieves a router instance fromrouterDefMapfor request routing.extClazzOfPolicy(Class<? extends IPolicy>)– Resolves the extension interface associated with a given policy class usingpolicyClazzMap.findEffectiveExtensions(Class<? extends IDomainExtension>, IIdentity, boolean)– Returns concrete extension implementations applicable to a business identity, respecting the priority order: Policy → Pattern → Partner.findDomainSteps(String activityCode, List<String> stepCodeList)– Retrieves step definitions belonging to a specific domain activity fromdomainStepDefMap.
These helpers enable the framework to maintain a deterministic, hierarchical, and extensible architecture while keeping the internal registry encapsulated.
Practical Code Examples
The following examples demonstrate how domain artifacts register themselves and how applications interact with the indexer at runtime.
// Domain definition registration
@Domain(code = "WMS", name = "Warehouse Management")
public class WmsDomain {}
// Step registration with activity and step codes
@Step(name = "创建出库单", tags = {"create", "outbound"})
public class CreateOutboundStep implements IDomainStep {
@Override
public String activityCode() { return "OUTBOUND"; }
@Override
public String stepCode() { return "CREATE"; }
}
// Router registration
@Router
public class OutboundRouter extends BaseRouter {}
// Extension and Policy registration
@Policy(name = "OutboundPolicy")
public class OutboundPolicy implements IPolicy<OutboundExt, Identity> {}
@Extension
public class OutboundExtImpl implements OutboundExt {
// Implementation details
}
// Pattern registration with priority
@Pattern(code = "VIP", priority = 1)
public class VipPattern implements IExtensionPattern {}
// Partner registration
@Partner(code = "KA")
public class KaPartner implements IPartner {}
// Runtime lookup examples
BaseRouter router = InternalIndexer.findRouter(OutboundRouter.class);
List<ExtensionDef> extensions = InternalIndexer.findEffectiveExtensions(
OutboundExt.class,
new Identity("customerId", "VIP"),
false
);
List<StepDef> steps = InternalIndexer.findDomainSteps(
"OUTBOUND",
Arrays.asList("CREATE")
);
When the Spring context initializes, DDDBootstrap automatically triggers the registration process. Each annotated class invokes InternalIndexer.index() to store its metadata, followed by postIndexing() to sort patterns and export the final DomainArtifacts snapshot.
Summary
- InternalIndexer acts as the central registry in
dddplus-runtime, collecting all domain artifacts during Spring bean initialization through type-specificindex()methods. - Artifacts are stored in isolated concurrent maps (
domainDefMap,domainStepDefMap,routerDefMap,patternDefMap,partnerDefMap,policyDefMap) to ensure thread-safe registration. - The post-indexing phase sorts
PatternDefobjects by priority intosortedPatternMap, clears temporary storage, and exports an immutableDomainArtifactssnapshot for external tools. - Runtime lookup helpers like
findEffectiveExtensions()andfindDomainSteps()enable deterministic resolution of extensions and steps based on business identity and activity codes.
Frequently Asked Questions
How does InternalIndexer handle concurrent registration during Spring initialization?
InternalIndexer uses concurrent hash maps (ConcurrentHashMap) for all artifact storage, including domainDefMap, partnerDefMap, and routerDefMap. Since Spring's bean initialization is single-threaded by default, the concurrent structures primarily provide safety guarantees during runtime lookups rather than concurrent registration. Each artifact type implements IRegistryAware and synchronously invokes its respective index() method before the container finishes initialization.
What determines the priority order when multiple patterns match an extension request?
During postIndexing(), InternalIndexer transfers all PatternDef objects from patternDefMap to sortedPatternMap, sorting them by the priority field in descending order. When findEffectiveExtensions() resolves an extension for a business identity, it evaluates patterns in this sorted order. Higher priority values are checked first, ensuring deterministic resolution where the most specific pattern matches before more general ones. After sorting, the temporary patternDefMap is cleared to reduce memory footprint.
How can external systems access the indexed domain metadata without directly querying InternalIndexer?
External tools should consume the DomainArtifacts object exported during postIndexing(). When InternalIndexer.postIndexing() executes, it triggers DomainArtifacts.getInstance().export(), which reads the internal maps and constructs three immutable collections: domains (codes and names), steps (grouped by activity code), and extensions (with their applicable patterns and partners). This snapshot provides a lightweight, read-only view suitable for configuration centers, visualization dashboards, and documentation generators without exposing the mutable internal registries.
What is the difference between Policy, Pattern, and Partner routes in extension indexing?
InternalIndexer categorizes extensions into three hierarchical resolution strategies during registration. Policies (stored in policyDefMap) link a policy class directly to an extension interface, enabling rule-based selection. Patterns (stored temporarily in patternDefMap then sorted into sortedPatternMap) match extensions based on business identity attributes with priority-based ordering. Partners (stored in partnerDefMap) provide specific implementations for particular business partners or customers. At runtime, findEffectiveExtensions() resolves these in order: Policy first, then Pattern, then Partner, ensuring the most specific applicable implementation is returned.
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 →