--- description: Service layer patterns for Evolution API globs: - "src/api/services/**/*.ts" - "src/api/integrations/**/services/*.ts" alwaysApply: false --- # Evolution API Service Rules ## Service Structure Pattern ### Standard Service Class ```typescript export class ExampleService { constructor(private readonly waMonitor: WAMonitoringService) {} private readonly logger = new Logger('ExampleService'); public async create(instance: InstanceDto, data: ExampleDto) { await this.waMonitor.waInstances[instance.instanceName].setData(data); return { example: { ...instance, data } }; } public async find(instance: InstanceDto): Promise { try { const result = await this.waMonitor.waInstances[instance.instanceName].findData(); if (Object.keys(result).length === 0) { throw new Error('Data not found'); } return result; } catch (error) { return null; // Evolution pattern - return null on error } } } ``` ## Dependency Injection Pattern ### Constructor Pattern ```typescript // CORRECT - Evolution API pattern constructor( private readonly waMonitor: WAMonitoringService, private readonly prismaRepository: PrismaRepository, private readonly configService: ConfigService, ) {} // INCORRECT - Don't use constructor(waMonitor, prismaRepository, configService) {} // ❌ No types ``` ## Logger Pattern ### Standard Logger Usage ```typescript // CORRECT - Evolution API pattern private readonly logger = new Logger('ServiceName'); // Usage this.logger.log('Operation started'); this.logger.error('Operation failed', error); // INCORRECT console.log('Operation started'); // ❌ Use Logger ``` ## WAMonitor Integration Pattern ### Instance Access Pattern ```typescript // CORRECT - Standard pattern public async operation(instance: InstanceDto, data: DataDto) { await this.waMonitor.waInstances[instance.instanceName].performAction(data); return { result: { ...instance, data } }; } // Instance validation const waInstance = this.waMonitor.waInstances[instance.instanceName]; if (!waInstance) { throw new NotFoundException('Instance not found'); } ``` ## Error Handling Pattern ### Try-Catch Pattern ```typescript // CORRECT - Evolution API pattern public async find(instance: InstanceDto): Promise { try { const result = await this.waMonitor.waInstances[instance.instanceName].findData(); if (Object.keys(result).length === 0) { throw new Error('Data not found'); } return result; } catch (error) { this.logger.error('Find operation failed', error); return null; // Return null on error (Evolution pattern) } } ``` ## Cache Integration Pattern ### Cache Service Usage ```typescript export class CacheAwareService { constructor( private readonly cache: CacheService, private readonly chatwootCache: CacheService, private readonly baileysCache: CacheService, ) {} public async getCachedData(key: string): Promise { const cached = await this.cache.get(key); if (cached) return cached; const data = await this.fetchFromSource(key); await this.cache.set(key, data, 300); // 5 min TTL return data; } } ``` ## Integration Service Patterns ### Chatbot Service Base Pattern ```typescript export class ChatbotService extends BaseChatbotService { constructor( waMonitor: WAMonitoringService, prismaRepository: PrismaRepository, configService: ConfigService, ) { super(waMonitor, prismaRepository, 'ChatbotService', configService); } protected async processBot( waInstance: any, remoteJid: string, bot: BotType, session: any, settings: any, content: string, ): Promise { // Implementation } } ``` ### Channel Service Pattern ```typescript export class ChannelService extends ChannelStartupService { constructor( configService: ConfigService, eventEmitter: EventEmitter2, prismaRepository: PrismaRepository, cache: CacheService, chatwootCache: CacheService, baileysCache: CacheService, ) { super(configService, eventEmitter, prismaRepository, cache, chatwootCache, baileysCache); } public readonly logger = new Logger('ChannelService'); public client: WASocket; public readonly instance: wa.Instance = {}; } ``` ## Service Initialization Pattern ### Service Registration ```typescript // In server.module.ts pattern export const templateService = new TemplateService( waMonitor, prismaRepository, configService, ); export const settingsService = new SettingsService(waMonitor); ``` ## Async Operation Patterns ### Promise Handling ```typescript // CORRECT - Evolution API pattern public async sendMessage(instance: InstanceDto, data: MessageDto) { const waInstance = this.waMonitor.waInstances[instance.instanceName]; return await waInstance.sendMessage(data); } // INCORRECT - Don't use .then() public sendMessage(instance: InstanceDto, data: MessageDto) { return this.waMonitor.waInstances[instance.instanceName] .sendMessage(data) .then(result => result); // ❌ Use async/await } ``` ## Configuration Access Pattern ### Config Service Usage ```typescript // CORRECT - Evolution API pattern const serverConfig = this.configService.get('SERVER'); const authConfig = this.configService.get('AUTHENTICATION'); const dbConfig = this.configService.get('DATABASE'); // Type-safe configuration access if (this.configService.get('CHATWOOT').ENABLED) { // Chatwoot logic } ``` ## Event Emission Pattern ### EventEmitter2 Usage ```typescript // CORRECT - Evolution API pattern this.eventEmitter.emit(Events.INSTANCE_CREATE, { instanceName: instance.name, status: 'created', }); // Chatwoot event pattern if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled) { this.chatwootService.eventWhatsapp( Events.STATUS_INSTANCE, { instanceName: this.instance.name }, { instance: this.instance.name, status: 'created', }, ); } ``` ## Service Method Naming ### Standard Method Names - `create()` - Create new resource - `find()` - Find single resource - `findAll()` - Find multiple resources - `update()` - Update resource - `delete()` - Delete resource - `fetch*()` - Fetch from external API - `send*()` - Send data/messages - `process*()` - Process data ## Service Testing Pattern ### Unit Test Structure ```typescript describe('ExampleService', () => { let service: ExampleService; let waMonitor: jest.Mocked; let prismaRepository: jest.Mocked; beforeEach(() => { const mockWaMonitor = { waInstances: { 'test-instance': { performAction: jest.fn(), }, }, }; service = new ExampleService( mockWaMonitor as any, prismaRepository, configService, ); }); it('should perform action successfully', async () => { const instance = { instanceName: 'test-instance' }; const data = { test: 'data' }; const result = await service.create(instance, data); expect(result).toBeDefined(); expect(waMonitor.waInstances['test-instance'].performAction).toHaveBeenCalledWith(data); }); }); ```