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.