import { Injectable, Logger, type OnModuleInit } from '@nestjs/common'; import { DOMAIN_EVENTS } from '@/infrastructure/events/domain-event'; import { EventBusService } from '@/infrastructure/events/event-bus.service'; import { SearchIndexerService } from './search-indexer.service'; /** * Keeps the index in step with the catalog, by subscription rather than by call. * * This is the direction the dependency has to run: SearchModule knows about * products, products knows nothing about search. When this module is extracted, * this file is the only thing that changes — an in-process subscription becomes * a queue consumer, and the producer never learns the difference. * * Failures are logged, not rethrown. A search index that missed one update is a * degraded search; an exception escaping here would take down the write that * triggered it, which is a far worse trade. */ @Injectable() export class SearchIndexSubscriber implements OnModuleInit { private readonly logger = new Logger(SearchIndexSubscriber.name); constructor( private readonly events: EventBusService, private readonly indexer: SearchIndexerService, ) {} onModuleInit(): void { for (const event of [ DOMAIN_EVENTS.PRODUCT_UPDATED, DOMAIN_EVENTS.PRODUCT_PUBLISHED, DOMAIN_EVENTS.PRODUCT_ARCHIVED, ]) { this.events.on<{ productId: string }>(event).subscribe((message) => { void this.reindex(message.payload.productId, event); }); } } private async reindex(productId: string, event: string): Promise { try { await this.indexer.reindexProduct(productId); } catch (error) { this.logger.error( `Reindex failed for ${productId} after ${event}: ${ error instanceof Error ? error.message : String(error) }`, ); } } }