Skip to content

ADR-005: Spring Boot Decorator Conventions

ADR-005: Spring Boot Decorator Conventions

Section titled “ADR-005: Spring Boot Decorator Conventions”

Status: Active
Date: 2024

TDI2 needed a decorator and lifecycle convention that would:

  • Minimize learning curve for enterprise developers
  • Provide familiar patterns from proven frameworks
  • Cover essential DI features without over-engineering
  • Support both simple and complex use cases

Enterprise teams often have developers with Spring Boot experience, and dependency injection patterns are well-established in the Java ecosystem.

Adopt Spring Boot decorator conventions adapted for TypeScript/React:

  • @Service() for service registration (equivalent to @Service, @Component)
  • @Inject() for dependency injection (equivalent to @Autowired)
  • @PostConstruct for initialization lifecycle (equivalent to @PostConstruct)
  • @PreDestroy for cleanup lifecycle (equivalent to @PreDestroy)
  • @Scope() for service lifetime management (equivalent to @Scope)
// Service registration - familiar to Spring Boot developers
@Service()
@Scope("singleton") // Default behavior, explicit for clarity
export class UserService implements UserServiceInterface {
constructor(
@Inject() private apiClient: ApiClientInterface,
@Inject() private logger: LoggerInterface
) {}
@PostConstruct
async initialize() {
// Called after all dependencies injected
await this.apiClient.authenticate();
this.logger.info('UserService initialized');
}
@PreDestroy
async cleanup() {
// Called before service destruction
await this.apiClient.disconnect();
this.logger.info('UserService destroyed');
}
}
// Scope variations
@Service()
@Scope("transient") // New instance each injection
export class RequestLogger implements LoggerInterface {
// Fresh instance per request/component
}
TDI2Spring BootPurpose
@Service()@Service, @ComponentMark class for DI registration
@Inject()@AutowiredMark dependency for injection
@PostConstruct@PostConstructInitialization after injection
@PreDestroy@PreDestroyCleanup before destruction
@Scope("singleton")@Scope("singleton")Single instance (default)
@Scope("transient")@Scope("prototype")New instance each time
  • Reduced learning curve - enterprise developers know these patterns
  • Proven conventions - battle-tested in large-scale applications
  • Clear lifecycle model - explicit initialization and cleanup phases
  • Familiar documentation - can reference Spring Boot concepts and patterns
  • Enterprise credibility - using established enterprise patterns builds trust
  • Java heritage - some patterns may feel foreign to pure JavaScript developers
  • Decorator dependency - requires TypeScript experimental decorators
  • Convention over configuration - less flexibility than custom decorator schemes
  1. Angular DI conventions - Rejected due to more complex token system

    constructor(@Inject('USER_SERVICE') userService: UserService) // More verbose
  2. Custom TDI2 conventions - Rejected due to learning curve for new patterns

    @TDIService() @TDIInject() // New concepts to learn
  3. Minimal decorator set - Rejected due to missing lifecycle management

    @Service() // Only registration, no lifecycle hooks

The Spring Boot approach provides the right balance of familiarity, functionality, and enterprise acceptance for TDI2’s target audience.