Skip to main content

Functional Registration Engine Design Document

1. Overview​

1.1 Design Goals​

This design implements an annotation-driven business-template registration engine, automatically binding business implementation classes and corresponding processing templates at startup through the @UseTemplate annotation, implementing the decoupling of business logic and processing flow.

1.2 Core Features​

  • ✅ Annotation-driven automatic registration mechanism
  • ✅ Generic type safety check
  • ✅ Functional programming style
  • ✅ Spring Boot integration
  • ✅ Flexible templated processing flow

2. Core Component Design​

2.1 RegisterEnginV3 - Registration Engine​

Responsibilities:

  • Scan all Business implementation classes at startup
  • Parse @UseTemplate annotation, bind business and template
  • Perform generic type consistency check
  • Provide unified business execution entry

Key Code:

@Service
@AllArgsConstructor
public class RegisterEnginV3<T, S extends Business<T>> {

@PostConstruct
public void init() {
businessesMap.forEach((txcode, businessService) -> {
// 1. Parse @UseTemplate annotation
UseTemplate ann = businessService.getClass()
.getAnnotation(UseTemplate.class);

// 2. Get corresponding Template Bean
Template<T, S> templateToUse =
(Template<T, S>) applicationContext
.getBean(ann.value());

// 3. Generic type check
checkConsistentGenericType(businessService, templateToUse);

// 4. Register to execution registry
registry.put(txcode,
param -> templateToUse.handler(txcode, param, businessService));
});
}

public void run(String businessType, T params) {
registry.get(businessType).accept(params);
}
}

Generic Parameter Description:

  • T: Business processing parameter type (usually BusinessContext<I, O>)
  • S: Concrete Business implementation type

2.2 Template - Processing Template Interface​

Design Concept: Adopt functional interface design to define unified business processing flow.

@FunctionalInterface
public interface Template<T, S extends Business<T>> {
void handler(String txcode, T param, S businessService);
}

Implementation Example:

@Component
public class TemplateImpl3<I, O> implements
Template<BusinessContext<I, O>, BusinessType3<I, O>> {

@Override
public void handler(String txcode,
BusinessContext<I, O> param,
BusinessType3<I, O> businessService) {
// Pre-processing
// ...

// Execute business logic
businessService.doBusiness2(param);

// Post-processing
// ...
}
}

2.3 Business - Business Interface Hierarchy​

Interface Hierarchy Design:

Business<T> (Marker Interface)
↓
BusinessType3<I, O> extends Business<BusinessContext<I, O>>
↓
Business3 implements BusinessType3<InputDto3, OutputDto3>

Business Basic Interface:

public interface Business<T> {
// Marker interface, used for type constraint
}

Business Type Interface:

public interface BusinessType3<I, O>
extends Business<BusinessContext<I, O>> {

void doBusiness2(BusinessContext<I, O> businessContext);
}

Concrete Business Implementation:

@Service
@UseTemplate(TemplateImpl3.class) // 👈 Key Annotation
public class Business3 implements
BusinessType3<InputDto3, OutputDto3> {

@Override
public void doBusiness2(BusinessContext<InputDto3, OutputDto3> ctx) {
OutputDto3 output = new OutputDto3();
output.setField2("result from Business3");
output.setField3("xxx");
ctx.setOutput(output);
}
}

2.4 @UseTemplate Annotation​

Definition:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseTemplate {
Class<?> value(); // Specify used Template implementation class
}

Usage Scenarios: Annotate on Business implementation class to specify the processing template used by this business.


3. Data Flow Design​

3.1 BusinessContext Context​

@Data
public class BusinessContext<I, O> {
private I input; // Input parameters
private O output; // Output result
private OtherDto3 otherDto; // Other context information
}

3.2 Execution Flow​

Client Call
↓
RegisterEnginV3.run(businessType, params)
↓
Get Consumer<T> from Registry
↓
Template.handler(txcode, param, businessService)
↓
BusinessService.doBusiness(param)
↓
Return Result (via BusinessContext)

4. Type Safety Mechanism​

4.1 Generic Type Check​

private static <T, S extends Business<T>> void
checkConsistentGenericType(S businessService,
Template<T, S> templateToUse) {

Class<?> templateGeneric = getGeneric(templateToUse, Template.class);
Class<?> businessGeneric = getGeneric(businessService, Business.class);

if (businessGeneric != null && templateGeneric != null &&
!templateGeneric.isAssignableFrom(businessGeneric)) {
throw new IllegalStateException("❌ Template type mismatch");
}
}

4.2 Type Mismatch Example​

// ❌ Error Example
@UseTemplate(TemplateImpl1.class) // Expect BusinessContext<A, B>
public class Business3 implements
BusinessType3<InputDto3, OutputDto3> { // Actual BusinessContext<InputDto3, OutputDto3>
// IllegalStateException will be thrown at startup
}

5. Bean Naming Strategy​

5.1 UniquePackageBeanNameGenerator​

Problem Background: When there are businesses with the same name under multiple packages, the default Spring naming strategy will cause Bean name conflicts.

Solution:

public class UniquePackageBeanNameGenerator
extends AnnotationBeanNameGenerator {

@Override
public String generateBeanName(BeanDefinition definition,
BeanDefinitionRegistry registry) {
String beanClassName = definition.getBeanClassName();
String originalBeanName = super.generateBeanName(definition, registry);

if (beanClassName != null) {
try {
Class<?> clazz = Class.forName(beanClassName);
if (Business.class.isAssignableFrom(clazz)) {
return "NEW" + originalBeanName; // Add prefix
}
} catch (ClassNotFoundException e) {
return originalBeanName;
}
}
return originalBeanName;
}
}

5.2 Configuration Method​

@SpringBootApplication
@ComponentScan(nameGenerator = UniquePackageBeanNameGenerator.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

6. Usage Example​

6.1 Define DTO​

@Data
public class InputDto3 {
private String field1;
}

@Data
public class OutputDto3 {
private String field2;
private String field3;
}

6.2 Implement Business Logic​

@Service
@UseTemplate(TemplateImpl3.class)
public class Business3 implements
BusinessType3<InputDto3, OutputDto3> {

@Override
public void doBusiness2(BusinessContext<InputDto3, OutputDto3> ctx) {
InputDto3 input = ctx.getInput();

OutputDto3 output = new OutputDto3();
output.setField2("Processing Result: " + input.getField1());
output.setField3("Extra Info");

ctx.setOutput(output);
}
}

6.3 Call Business​

@RestController
@AllArgsConstructor
public class BusinessController {

private final RegisterEnginV3<BusinessContext<?, ?>, ?> registerEngine;

@PostMapping("/execute/{businessType}")
public BusinessContext<?, ?> execute(
@PathVariable String businessType,
@RequestBody InputDto3 input) {

BusinessContext<InputDto3, OutputDto3> context =
new BusinessContext<>();
context.setInput(input);

registerEngine.run(businessType, context);

return context;
}
}

7. Advantages and Extensibility​

7.1 Design Advantages​

FeatureDescription
Low CouplingBusiness logic and processing flow are completely separated
High CohesionSame type businesses share the same template
Type SafetyDouble type checking at compile time + runtime
Easy ExtensionNew business only needs to implement interface and add annotation
MaintainableUnified registration and execution mechanism

8. Exception Handling​

  1. Define system internal exceptions and custom communication exceptions.
  2. Controller or Service unifies handling of custom exceptions and unknown exceptions.
  3. If there are scenarios that do not need a response, define a non-response exception.
  4. If there are multiple remote calls, handle them according to custom exceptions of multiple remote calls.

9. Summary​

This design implements flexible registration and execution of business logic through annotation-driven way. Main features:

✅ Declarative Configuration: Bind relationships through @UseTemplate annotation

✅ Type Safety: Generics + Reflection implement double checking at compile time and runtime

✅ High Decoupling: Business logic and execution flow are completely separated

✅ Easy to Extend: New business requires no modification to registration engine code

Applicable Scenarios:

  • Unified processing framework for multiple business types
  • Systems requiring dynamic business extension
  • Scenarios with standardized business processes

Agreement
The code part of this work is licensed under Apache License 2.0 . You may freely modify and redistribute the code, and use it for commercial purposes, provided that you comply with the license. However, you are required to:
  • Attribution: Retain the original author's signature and code source information in the original and derivative code.
  • Preserve License: Retain the Apache 2.0 license file in the original and derivative code.
The documentation part of this work is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . You may freely share, including copying and distributing this work in any medium or format, and freely adapt, remix, transform, and build upon the material. However, you are required to:
  • Attribution: Give appropriate credit, provide a link to the license, and indicate if changes were made.
  • NonCommercial: You may not use the material for commercial purposes. For commercial use, please contact the author.
  • ShareAlike: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.