53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
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<void> {
|
|
try {
|
|
await this.indexer.reindexProduct(productId);
|
|
} catch (error) {
|
|
this.logger.error(
|
|
`Reindex failed for ${productId} after ${event}: ${
|
|
error instanceof Error ? error.message : String(error)
|
|
}`,
|
|
);
|
|
}
|
|
}
|
|
}
|