服务注册
Entity Engine 通过 serviceRegistry 提供了简洁而强大的服务注册系统。您可以在模块的 setupComponents 阶段注册服务,在应用的任何地方获取和使用这些服务。服务注册是 Entity Engine 依赖注入系统的核心,让模块之间能够有效解耦和协作。
基本服务注册
注册简单服务
最基本的服务注册方式是直接注册服务实例:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 注册简单的服务实例
serviceRegistry.register('EmailService', new EmailService(config.email));
// 注册配置服务
serviceRegistry.register('ConfigService', new ConfigService(config));
// 注册工具类
serviceRegistry.register('ValidationUtils', new ValidationUtils());
logger.info('Basic services registered successfully');
}注册依赖其他服务的服务
当服务需要依赖其他服务时,先获取依赖,再创建和注册服务:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 获取依赖的服务
const dbService = serviceRegistry.get('DatabaseService');
const cacheService = serviceRegistry.get('CacheService');
// 注册需要依赖的服务
serviceRegistry.register('UserRepository',
new UserRepository(dbService)
);
serviceRegistry.register('UserService',
new UserService(dbService, cacheService)
);
serviceRegistry.register('AuthService',
new AuthService(
serviceRegistry.get('UserService'),
config.auth
)
);
logger.info('Dependent services registered successfully');
}高级注册模式
工厂函数注册
使用工厂函数可以延迟服务的创建,适用于复杂的初始化逻辑:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 使用工厂函数注册服务
serviceRegistry.register('ReportService', () => {
// 在工厂函数中获取依赖
const userService = serviceRegistry.get('UserService');
const orderService = serviceRegistry.get('OrderService');
const paymentService = serviceRegistry.get('PaymentService');
// 创建复杂的服务实例
return new ReportService({
userService,
orderService,
paymentService,
templates: config.reportTemplates,
exportFormats: config.exportFormats
});
});
// 条件性创建的工厂函数
serviceRegistry.register('NotificationService', () => {
const emailService = serviceRegistry.get('EmailService');
const smsService = serviceRegistry.has('SmsService')
? serviceRegistry.get('SmsService')
: null;
return new NotificationService(emailService, smsService);
});
logger.info('Factory-based services registered');
}条件注册
根据配置或环境条件注册不同的服务实现:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
const env = process.env.NODE_ENV || 'development';
// 根据配置选择存储实现
if (config.storage.type === 'redis') {
serviceRegistry.register('CacheService',
new RedisCacheService(config.storage.redis)
);
} else if (config.storage.type === 'memory') {
serviceRegistry.register('CacheService',
new MemoryCacheService(config.storage.memory)
);
} else {
serviceRegistry.register('CacheService',
new NoCacheService()
);
}
// 根据环境注册不同的日志服务
if (env === 'production') {
serviceRegistry.register('LoggingService',
new CloudLoggingService(config.logging.cloud)
);
} else if (env === 'test') {
serviceRegistry.register('LoggingService',
new MockLoggingService()
);
} else {
serviceRegistry.register('LoggingService',
new ConsoleLoggingService()
);
}
// 功能开关控制的服务注册
if (config.features.enableAnalytics) {
serviceRegistry.register('AnalyticsService',
new AnalyticsService(config.analytics)
);
}
if (config.features.enableSearch) {
serviceRegistry.register('SearchService',
new SearchService(config.search)
);
}
logger.info(`Services registered for ${env} environment`);
}服务命名约定
标准命名模式
采用一致的命名约定有助于服务管理和调试:
async setupComponents({ serviceRegistry, configManager, logger }) {
// 按功能领域分组命名
// 数据访问层
serviceRegistry.register('UserRepository', new UserRepository());
serviceRegistry.register('OrderRepository', new OrderRepository());
serviceRegistry.register('ProductRepository', new ProductRepository());
// 业务服务层
serviceRegistry.register('UserService', new UserService());
serviceRegistry.register('OrderService', new OrderService());
serviceRegistry.register('PaymentService', new PaymentService());
// 基础设施服务
serviceRegistry.register('DatabaseService', new DatabaseService());
serviceRegistry.register('CacheService', new CacheService());
serviceRegistry.register('EmailService', new EmailService());
serviceRegistry.register('LoggingService', new LoggingService());
// 工具和辅助服务
serviceRegistry.register('ValidationUtils', new ValidationUtils());
serviceRegistry.register('DateUtils', new DateUtils());
serviceRegistry.register('EncryptionUtils', new EncryptionUtils());
logger.info('Services registered with standard naming');
}版本化服务注册
当需要支持多个版本的服务时:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 注册不同版本的 API 服务
serviceRegistry.register('PaymentService.v1',
new PaymentServiceV1(config.payment.v1)
);
serviceRegistry.register('PaymentService.v2',
new PaymentServiceV2(config.payment.v2)
);
// 根据配置选择默认版本
const defaultPaymentVersion = config.payment.defaultVersion || 'v2';
const defaultPaymentService = serviceRegistry.get(`PaymentService.${defaultPaymentVersion}`);
serviceRegistry.register('PaymentService', defaultPaymentService);
// 兼容性适配器
serviceRegistry.register('LegacyPaymentAdapter',
new LegacyPaymentAdapter(
serviceRegistry.get('PaymentService.v2')
)
);
logger.info(`Payment services registered, default version: ${defaultPaymentVersion}`);
}服务覆盖和替换
服务替换
在特定条件下替换已注册的服务:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 首先注册基础实现
serviceRegistry.register('EmailService', new BaseEmailService());
// 根据配置决定是否使用高级实现
if (config.email.provider === 'sendgrid') {
// 替换为 SendGrid 实现
serviceRegistry.register('EmailService',
new SendGridEmailService(config.email.sendgrid)
);
logger.info('Using SendGrid email service');
} else if (config.email.provider === 'mailgun') {
// 替换为 Mailgun 实现
serviceRegistry.register('EmailService',
new MailgunEmailService(config.email.mailgun)
);
logger.info('Using Mailgun email service');
}
// 开发环境可能需要模拟服务
if (process.env.NODE_ENV === 'development' && config.email.mock) {
serviceRegistry.register('EmailService', new MockEmailService());
logger.info('Using mock email service for development');
}
}服务装饰器
为现有服务添加额外功能:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 注册基础服务
serviceRegistry.register('UserService', new UserService());
// 根据配置添加缓存装饰器
if (config.cache.enabled) {
const baseUserService = serviceRegistry.get('UserService');
const cacheService = serviceRegistry.get('CacheService');
serviceRegistry.register('UserService',
new CachedUserService(baseUserService, cacheService)
);
logger.info('User service enhanced with caching');
}
// 添加日志装饰器
if (config.logging.enabled) {
const userService = serviceRegistry.get('UserService');
const loggingService = serviceRegistry.get('LoggingService');
serviceRegistry.register('UserService',
new LoggedUserService(userService, loggingService)
);
logger.info('User service enhanced with logging');
}
// 添加性能监控装饰器
if (config.monitoring.enabled) {
const userService = serviceRegistry.get('UserService');
const metricsService = serviceRegistry.get('MetricsService');
serviceRegistry.register('UserService',
new MonitoredUserService(userService, metricsService)
);
logger.info('User service enhanced with monitoring');
}
}批量服务注册
配置驱动的批量注册
使用配置文件驱动批量服务注册:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 定义服务注册配置
const serviceRegistrations = [
{
name: 'DatabaseService',
enabled: config.database.enabled,
factory: () => new DatabaseService(config.database),
dependencies: []
},
{
name: 'CacheService',
enabled: config.cache.enabled,
factory: () => new CacheService(config.cache),
dependencies: []
},
{
name: 'UserRepository',
enabled: true,
factory: () => new UserRepository(serviceRegistry.get('DatabaseService')),
dependencies: ['DatabaseService']
},
{
name: 'UserService',
enabled: true,
factory: () => new UserService(
serviceRegistry.get('UserRepository'),
serviceRegistry.get('CacheService')
),
dependencies: ['UserRepository', 'CacheService']
}
];
// 按依赖顺序注册服务
const registeredServices = [];
for (const registration of serviceRegistrations) {
if (!registration.enabled) {
continue;
}
// 检查依赖是否满足
const missingDeps = registration.dependencies.filter(
dep => !registeredServices.includes(dep)
);
if (missingDeps.length > 0) {
logger.warn(`Skipping ${registration.name}: missing dependencies ${missingDeps.join(', ')}`);
continue;
}
try {
const service = registration.factory();
serviceRegistry.register(registration.name, service);
registeredServices.push(registration.name);
logger.info(`Registered service: ${registration.name}`);
} catch (error) {
logger.error(`Failed to register ${registration.name}:`, error);
// 某些服务是关键的,注册失败应该抛出错误
if (registration.name === 'DatabaseService') {
throw error;
}
}
}
logger.info(`Batch registration completed: ${registeredServices.length} services`);
}模块化服务注册
将服务注册逻辑分解为多个功能模块:
class CoreServicesRegistrar {
static async register(serviceRegistry, config, logger) {
serviceRegistry.register('DatabaseService',
new DatabaseService(config.database)
);
serviceRegistry.register('CacheService',
new CacheService(config.cache)
);
serviceRegistry.register('LoggingService',
new LoggingService(config.logging)
);
logger.info('Core services registered');
}
}
class BusinessServicesRegistrar {
static async register(serviceRegistry, config, logger) {
const dbService = serviceRegistry.get('DatabaseService');
const cacheService = serviceRegistry.get('CacheService');
serviceRegistry.register('UserRepository',
new UserRepository(dbService)
);
serviceRegistry.register('UserService',
new UserService(serviceRegistry.get('UserRepository'), cacheService)
);
serviceRegistry.register('OrderService',
new OrderService(dbService, serviceRegistry.get('UserService'))
);
logger.info('Business services registered');
}
}
class ExternalServicesRegistrar {
static async register(serviceRegistry, config, logger) {
if (config.features.enableEmail) {
serviceRegistry.register('EmailService',
new EmailService(config.email)
);
}
if (config.features.enableSMS) {
serviceRegistry.register('SMSService',
new SMSService(config.sms)
);
}
if (config.features.enablePush) {
serviceRegistry.register('PushService',
new PushService(config.push)
);
}
logger.info('External services registered');
}
}
// 在模块中使用
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 按顺序注册不同类型的服务
await CoreServicesRegistrar.register(serviceRegistry, config, logger);
await BusinessServicesRegistrar.register(serviceRegistry, config, logger);
await ExternalServicesRegistrar.register(serviceRegistry, config, logger);
logger.info('All service registrations completed');
}服务健康检查
基础健康检查
为关键服务实现健康检查:
class DatabaseService {
constructor(private config: any) {}
async isHealthy() {
try {
await this.query('SELECT 1');
return { healthy: true, message: 'Database connection is active' };
} catch (error) {
return {
healthy: false,
message: `Database connection failed: ${error.message}`
};
}
}
async query(sql: string) {
// 数据库查询实现
}
}
class CacheService {
constructor(private config: any) {}
async isHealthy() {
try {
const testKey = '__health_check__';
const testValue = Date.now().toString();
await this.set(testKey, testValue, 1);
const retrieved = await this.get(testKey);
return {
healthy: retrieved === testValue,
message: retrieved === testValue
? 'Cache read/write operations working'
: 'Cache read/write operations failed'
};
} catch (error) {
return {
healthy: false,
message: `Cache health check failed: ${error.message}`
};
}
}
async set(key: string, value: any, ttl?: number) {
// 缓存设置实现
}
async get(key: string) {
// 缓存获取实现
}
}
// 注册服务时添加健康检查
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
const dbService = new DatabaseService(config.database);
const cacheService = new CacheService(config.cache);
serviceRegistry.register('DatabaseService', dbService);
serviceRegistry.register('CacheService', cacheService);
// 定期执行健康检查
setInterval(async () => {
const dbHealth = await dbService.isHealthy();
const cacheHealth = await cacheService.isHealthy();
if (!dbHealth.healthy) {
logger.error('Database health check failed:', dbHealth.message);
}
if (!cacheHealth.healthy) {
logger.warn('Cache health check failed:', cacheHealth.message);
}
}, 30000); // 每30秒检查一次
logger.info('Services registered with health checks');
}服务可用性监控
监控服务的可用性和性能:
class ServiceMonitor {
constructor(private serviceRegistry: any, private logger: any) {}
async checkAllServices() {
const services = [
'DatabaseService',
'CacheService',
'EmailService',
'UserService'
];
const results = {};
for (const serviceName of services) {
if (this.serviceRegistry.has(serviceName)) {
const service = this.serviceRegistry.get(serviceName);
results[serviceName] = await this.checkService(service, serviceName);
} else {
results[serviceName] = {
available: false,
message: 'Service not registered'
};
}
}
return results;
}
private async checkService(service: any, serviceName: string) {
try {
// 检查服务是否有健康检查方法
if (typeof service.isHealthy === 'function') {
const healthResult = await service.isHealthy();
return {
available: healthResult.healthy,
message: healthResult.message,
type: 'health_check'
};
}
// 基础可用性检查
return {
available: service !== null && service !== undefined,
message: 'Service instance exists',
type: 'basic_check'
};
} catch (error) {
return {
available: false,
message: `Health check error: ${error.message}`,
type: 'error'
};
}
}
}
// 在模块中使用服务监控
async setupComponents({ serviceRegistry, configManager, logger }) {
// ... 注册服务 ...
// 创建服务监控器
const serviceMonitor = new ServiceMonitor(serviceRegistry, logger);
// 定期监控服务健康状态
setInterval(async () => {
const healthResults = await serviceMonitor.checkAllServices();
const unhealthyServices = Object.entries(healthResults)
.filter(([_, result]: [string, any]) => !result.available)
.map(([name, _]) => name);
if (unhealthyServices.length > 0) {
logger.warn(`Unhealthy services detected: ${unhealthyServices.join(', ')}`);
}
// 记录详细的健康状态
logger.debug('Service health check results:', healthResults);
}, 60000); // 每分钟检查一次
serviceRegistry.register('ServiceMonitor', serviceMonitor);
logger.info('Service monitoring initialized');
}测试中的服务注册
测试用服务替换
在测试环境中替换真实服务为模拟服务:
// 模拟服务实现
class MockEmailService {
private sentEmails: any[] = [];
async send(to: string, subject: string, content: string) {
this.sentEmails.push({
to, subject, content,
sentAt: new Date()
});
return { messageId: `mock-${Date.now()}` };
}
getSentEmails() {
return [...this.sentEmails];
}
clearSentEmails() {
this.sentEmails = [];
}
async isHealthy() {
return { healthy: true, message: 'Mock email service is always healthy' };
}
}
class MockDatabaseService {
private tables = new Map<string, any[]>();
async query(sql: string) {
// 简单的模拟查询实现
if (sql === 'SELECT 1') {
return [{ result: 1 }];
}
return [];
}
async findOne(table: string, conditions: any) {
const records = this.tables.get(table) || [];
return records.find(record =>
Object.entries(conditions).every(([key, value]) => record[key] === value)
);
}
async insert(table: string, data: any) {
if (!this.tables.has(table)) {
this.tables.set(table, []);
}
const record = { ...data, id: `mock-${Date.now()}` };
this.tables.get(table)!.push(record);
return record;
}
clearAllTables() {
this.tables.clear();
}
async isHealthy() {
return { healthy: true, message: 'Mock database is always healthy' };
}
}
// 测试配置
describe('UserService Integration Tests', () => {
let serviceRegistry: any;
let mockDb: MockDatabaseService;
let mockEmail: MockEmailService;
beforeEach(() => {
// 创建测试用的服务注册表
serviceRegistry = new Map();
serviceRegistry.register = (name: string, service: any) => {
serviceRegistry.set(name, service);
};
serviceRegistry.get = (name: string) => {
return serviceRegistry.get(name);
};
serviceRegistry.has = (name: string) => {
return serviceRegistry.has(name);
};
// 注册模拟服务
mockDb = new MockDatabaseService();
mockEmail = new MockEmailService();
serviceRegistry.register('DatabaseService', mockDb);
serviceRegistry.register('EmailService', mockEmail);
// 注册被测试的服务
serviceRegistry.register('UserService',
new UserService(mockDb, mockEmail)
);
});
afterEach(() => {
mockDb.clearAllTables();
mockEmail.clearSentEmails();
});
it('should register user and send welcome email', async () => {
const userService = serviceRegistry.get('UserService');
const userData = {
username: 'testuser',
email: 'test@example.com',
password: 'password123'
};
const user = await userService.register(userData);
expect(user.username).toBe('testuser');
expect(mockEmail.getSentEmails()).toHaveLength(1);
expect(mockEmail.getSentEmails()[0].to).toBe('test@example.com');
});
});服务注册最佳实践
1. 遵循单一职责原则
每个服务应该专注于单一职责:
// 好的做法 - 职责明确的服务
class UserAuthService {
// 只处理用户认证相关逻辑
async authenticate(username: string, password: string) {}
async generateToken(user: any) {}
async validateToken(token: string) {}
}
class UserProfileService {
// 只处理用户资料相关逻辑
async getProfile(userId: string) {}
async updateProfile(userId: string, data: any) {}
async uploadAvatar(userId: string, file: any) {}
}
// 避免的做法 - 职责过于宽泛
class UserService {
// 包含了太多不同的职责
async authenticate() {} // 认证
async getProfile() {} // 资料管理
async sendEmail() {} // 邮件发送
async generateReport() {} // 报表生成
// ... 更多职责
}2. 使用接口进行抽象
通过接口定义服务契约,便于测试和替换:
// 定义服务接口
interface IEmailService {
send(to: string, subject: string, content: string): Promise<any>;
isHealthy(): Promise<{ healthy: boolean; message: string }>;
}
// 实现具体的服务
class SMTPEmailService implements IEmailService {
constructor(private config: any) {}
async send(to: string, subject: string, content: string) {
// SMTP 实现
}
async isHealthy() {
// SMTP 健康检查
}
}
class SendGridEmailService implements IEmailService {
constructor(private apiKey: string) {}
async send(to: string, subject: string, content: string) {
// SendGrid 实现
}
async isHealthy() {
// SendGrid 健康检查
}
}
// 注册时使用接口类型
async setupComponents({ serviceRegistry, configManager }) {
const config = configManager.get(this.info.name);
let emailService: IEmailService;
if (config.email.provider === 'smtp') {
emailService = new SMTPEmailService(config.email.smtp);
} else {
emailService = new SendGridEmailService(config.email.apiKey);
}
serviceRegistry.register('EmailService', emailService);
}3. 合理的服务粒度
避免服务过于细化或过于粗糙:
// 适中的服务粒度
async setupComponents({ serviceRegistry, configManager }) {
// 数据访问层 - 按实体分组
serviceRegistry.register('UserRepository', new UserRepository());
serviceRegistry.register('OrderRepository', new OrderRepository());
serviceRegistry.register('ProductRepository', new ProductRepository());
// 业务服务层 - 按业务领域分组
serviceRegistry.register('UserManagementService', new UserManagementService());
serviceRegistry.register('OrderProcessingService', new OrderProcessingService());
serviceRegistry.register('InventoryService', new InventoryService());
// 基础设施服务 - 按技术功能分组
serviceRegistry.register('NotificationService', new NotificationService());
serviceRegistry.register('PaymentGateway', new PaymentGateway());
serviceRegistry.register('FileStorageService', new FileStorageService());
}4. 错误处理和回退机制
为服务注册提供错误处理和回退机制:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 关键服务注册失败应该抛出错误
try {
serviceRegistry.register('DatabaseService',
new DatabaseService(config.database)
);
} catch (error) {
logger.error('Failed to register critical DatabaseService:', error);
throw error; // 阻止模块启动
}
// 可选服务注册失败应提供回退方案
try {
serviceRegistry.register('CacheService',
new RedisCacheService(config.redis)
);
logger.info('Redis cache service registered');
} catch (error) {
logger.warn('Failed to register Redis cache, using memory cache:', error);
serviceRegistry.register('CacheService',
new MemoryCacheService()
);
}
// 外部服务注册失败时使用模拟实现
try {
serviceRegistry.register('EmailService',
new EmailService(config.email)
);
} catch (error) {
logger.warn('Failed to register email service, using mock:', error);
serviceRegistry.register('EmailService', new MockEmailService());
}
}5. 服务配置验证
在注册服务前验证配置:
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 验证数据库配置
if (!config.database.host || !config.database.port) {
throw new Error('Database host and port are required');
}
// 验证邮件服务配置
if (config.email.enabled) {
if (!config.email.smtp.host) {
throw new Error('SMTP host is required when email is enabled');
}
serviceRegistry.register('EmailService',
new EmailService(config.email.smtp)
);
}
// 验证缓存配置
if (config.cache.type === 'redis') {
if (!config.cache.redis.url) {
logger.warn('Redis URL not configured, falling back to memory cache');
serviceRegistry.register('CacheService', new MemoryCacheService());
} else {
serviceRegistry.register('CacheService',
new RedisCacheService(config.cache.redis)
);
}
}
logger.info('All service configurations validated and registered');
}下一步
了解服务注册后,继续学习:
- 配置管理 - 学习如何管理模块配置,为服务提供灵活的配置支持
Last updated on