@tdi2/di-core Overview
@tdi2/di-core Overview
Section titled “@tdi2/di-core Overview”Enterprise Dependency Injection for React
Section titled “Enterprise Dependency Injection for React”The core TDI2 framework that transforms React development through dependency injection, reactive services, and automatic interface resolution.
🎯 Core Features
- Service Decorators - @Service(), @Inject(), @Configuration, @Bean
- Environment Management - @Profile() for environment-specific services
- Lifecycle Hooks - @PostConstruct, @PreDestroy, onMount, onUnmount
- Reactive State - Valtio-powered automatic reactivity
- Interface Resolution - Automatic service discovery
- Container Management - Service lifecycle and scoping
- Testing Support - Full testing framework integration
Installation
Section titled “Installation”# npmnpm install @tdi2/di-core valtio
# bunbun add @tdi2/di-core valtio
# pnpmpnpm add @tdi2/di-core valtioCore Components
Section titled “Core Components”Service Decorators
Section titled “Service Decorators”import { Service, Inject, Profile, PostConstruct, PreDestroy } from '@tdi2/di-core';
// Define service interfaceinterface ProductServiceInterface { state: { products: Product[]; loading: boolean; }; loadProducts(): Promise<void>;}
// Implement with @Service decorator@Service()@Profile("development", "production")export class ProductService implements ProductServiceInterface { state = { products: [] as Product[], loading: false };
constructor( @Inject() private productRepository: ProductRepository, @Inject() private notificationService: NotificationService ) {}
@PostConstruct async initialize(): Promise<void> { console.log('ProductService initialized'); // Pre-load any critical data }
@PreDestroy async cleanup(): Promise<void> { console.log('ProductService cleaning up'); // Clean up resources }
async loadProducts(): Promise<void> { this.state.loading = true; try { this.state.products = await this.productRepository.getProducts(); } finally { this.state.loading = false; } }}DI Container
Section titled “DI Container”import { DIContainer, DIProvider } from '@tdi2/di-core';import { DI_CONFIG } from './.tdi2/di-config'; // Auto-generated
const container = new DIContainer();container.loadConfiguration(DI_CONFIG);
function App() { return ( <DIProvider container={container}> <ProductCatalog /> {/* Services auto-injected */} </DIProvider> );}Component Integration
Section titled “Component Integration”import { Inject } from '@tdi2/di-core';
interface ProductListProps { productService: Inject<ProductServiceInterface>;}
function ProductList({ productService }: ProductListProps) { const { products, loading } = productService.state;
return ( <div> {loading ? ( <Spinner /> ) : ( products.map(product => ( <ProductCard key={product.id} product={product} /> )) )} </div> );}Service Lifecycle
Section titled “Service Lifecycle”Lifecycle Hooks
Section titled “Lifecycle Hooks”Spring Boot Style Lifecycle
Section titled “Spring Boot Style Lifecycle”@Service()export class CartService { state = { items: [] as CartItem[], total: 0 };
@PostConstruct async initialize(): Promise<void> { // Called after dependency injection is complete await this.loadPersistedCart(); this.setupAutoSave(); console.log('CartService initialized'); }
@PreDestroy async cleanup(): Promise<void> { // Called before service destruction await this.saveCartToStorage(); this.cleanupTimers(); console.log('CartService cleaned up'); }
private setupAutoSave(): void { setInterval(() => this.saveCartToStorage(), 30000); }}React Component Lifecycle
Section titled “React Component Lifecycle”@Service()export class ComponentBoundService implements OnMount, OnUnmount { state = { componentData: [] as any[] };
onMount(): void { // Called when component mounts this.startRealTimeUpdates(); }
onUnmount(): void { // Called when component unmounts this.stopRealTimeUpdates(); }}Service Scoping
Section titled “Service Scoping”// Singleton (default) - one instance per container@Service()@Scope("singleton")export class UserService {}
// Transient - new instance each time@Service()@Scope("transient")export class ApiClient {}
// Scoped - one instance per scope@Service()@Scope("scoped")export class RequestLogger {}Environment Management
Section titled “Environment Management”Profile-Based Service Activation
Section titled “Profile-Based Service Activation”// Development-only service@Service()@Profile("development")export class MockEmailService implements EmailServiceInterface { async sendEmail(to: string, subject: string): Promise<void> { console.log(`Mock email to ${to}: ${subject}`); }}
// Production service@Service()@Profile("production")export class SmtpEmailService implements EmailServiceInterface { constructor( @Inject() private smtpConfig: SmtpConfigurationInterface ) {}
async sendEmail(to: string, subject: string): Promise<void> { return this.smtpClient.send({ to, subject }); }}
// Multi-environment service@Service()@Profile("staging", "production")export class RedisCache implements CacheServiceInterface { // Only active in staging or production}Configuration Classes
Section titled “Configuration Classes”@Configuration()export class DatabaseConfiguration { @Bean() @Profile("development") createDevDatabase(): DatabaseConnection { return new SqliteConnection('dev.db'); }
@Bean() @Profile("production") createProdDatabase( @Inject() config: EnvironmentConfig ): DatabaseConnection { return new PostgresConnection({ host: config.DB_HOST, port: config.DB_PORT, database: config.DB_NAME }); }
@Bean() createConnectionPool( @Inject() database: DatabaseConnection ): ConnectionPool { return new ConnectionPool(database, { maxConnections: 20 }); }}Testing Support
Section titled “Testing Support”Service Unit Testing
Section titled “Service Unit Testing”describe('ProductService', () => { let productService: ProductService; let mockRepository: jest.Mocked<ProductRepository>;
beforeEach(() => { mockRepository = { getProducts: jest.fn(), getProduct: jest.fn() };
productService = new ProductService( mockRepository, new MockNotificationService() ); });
it('should load products correctly', async () => { const mockProducts = [ { id: '1', name: 'iPhone', price: 999 } ]; mockRepository.getProducts.mockResolvedValue(mockProducts);
await productService.loadProducts();
expect(productService.state.products).toEqual(mockProducts); expect(productService.state.loading).toBe(false); });});Component Testing with Mocks
Section titled “Component Testing with Mocks”import { render, screen } from '@testing-library/react';import { DIContainer, DIProvider } from '@tdi2/di-core';
describe('ProductList', () => { it('should render products', () => { const mockProductService = { state: { products: [{ id: '1', name: 'iPhone', price: 999 }], loading: false }, loadProducts: jest.fn() };
const testContainer = new DIContainer(); // Register mock service in container testContainer.register('ProductService', () => mockProductService);
render( <DIProvider container={testContainer}> <ProductList productService={mockProductService} /> </DIProvider> );
expect(screen.getByText('iPhone')).toBeInTheDocument(); });});Performance Features
Section titled “Performance Features”Automatic Reactivity
Section titled “Automatic Reactivity”@Service()export class CartService { state = { items: [] as CartItem[], // Computed properties are automatically reactive get total(): number { return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0); }, get itemCount(): number { return this.items.reduce((sum, item) => sum + item.quantity, 0); } };
addItem(product: Product, quantity = 1): void { // State changes automatically trigger component re-renders const existingItem = this.state.items.find(item => item.productId === product.id);
if (existingItem) { existingItem.quantity += quantity; } else { this.state.items.push({ productId: product.id, product, quantity, price: product.price }); } // No manual state synchronization needed! }}Selective Re-rendering
Section titled “Selective Re-rendering”function CartSummary({ cartService }: { cartService: Inject<CartServiceInterface> }) { // Component only re-renders when total or itemCount changes const { total, itemCount } = cartService.state;
return ( <div> <div>Items: {itemCount}</div> <div>Total: ${total.toFixed(2)}</div> </div> );}Advanced Features
Section titled “Advanced Features”Cross-Service Communication
Section titled “Cross-Service Communication”@Service()export class RecommendationService { constructor( @Inject() private userService: UserServiceInterface, @Inject() private cartService: CartServiceInterface ) { // React to user changes subscribe(this.userService.state, () => { if (this.userService.state.isAuthenticated) { this.loadPersonalizedRecommendations(); } });
// React to cart changes subscribe(this.cartService.state, () => { this.updateRecommendationsBasedOnCart(); }); }}Optional Dependencies
Section titled “Optional Dependencies”@Service()export class UserService { constructor( @Inject() private userRepository: UserRepository, @Inject() private logger?: LoggerInterface, // Optional @Inject() private analytics?: AnalyticsService // Optional ) {}
async createUser(userData: CreateUserRequest): Promise<User> { this.logger?.log('Creating user'); const user = await this.userRepository.createUser(userData); this.analytics?.track('user_created', { userId: user.id }); return user; }}Best Practices
Section titled “Best Practices”✅ Interface-First Design
Section titled “✅ Interface-First Design”Always define interfaces before implementations for better testability and flexibility.
✅ Flat State Structure
Section titled “✅ Flat State Structure”Keep service state flat and easily consumable by components.
✅ Single Responsibility
Section titled “✅ Single Responsibility”Each service should have one clear business purpose.
✅ Reactive Patterns
Section titled “✅ Reactive Patterns”Leverage automatic reactivity instead of manual state synchronization.
❌ Avoid Circular Dependencies
Section titled “❌ Avoid Circular Dependencies”Design service dependencies as a directed acyclic graph.
Package Structure
Section titled “Package Structure”@tdi2/di-core/├── decorators/ # @Service, @Inject, @Profile, @Configuration, @Bean├── container/ # DI container implementation├── context/ # React context providers├── hooks/ # React hooks for service access├── lifecycle/ # Service lifecycle management (@PostConstruct/@PreDestroy)├── configuration/ # Configuration class and bean processing├── profiles/ # Environment profile management├── testing/ # Testing utilities and mocks└── types/ # TypeScript type definitionsNext Steps
Section titled “Next Steps”Essential Reading
Section titled “Essential Reading”- Testing Guide - Complete testing framework
- Service Patterns - Design robust services
- Features Roadmap - All implemented features
Configuration
Section titled “Configuration”- Vite Plugin - Build-time transformation
- Quick Start - Complete setup tutorial
- Enterprise Guide - Large-scale deployment
Examples
Section titled “Examples”- Complete E-Commerce App - Working implementation
- Service Examples - Real service implementations
- Interactive Demos - Live examples
🎯 Key Takeaway
@tdi2/di-core provides the foundation for service-oriented React architecture. Start with simple services and gradually adopt advanced patterns as your application grows.