Replace 12 dynamic `await import()` calls with domain events emitted through the existing systemEvents bus, breaking circular dependencies between services (economy/inventory/leveling -> quest, * -> dashboard). - Add `emitAsync` to SystemEventEmitter for sequential listener awaiting, preserving DB transaction atomicity for quest progress tracking - Add DOMAIN event constants (BALANCE_CHANGED, XP_GAINED, ITEM_COLLECTED, ITEM_USED, TRANSFER_COMPLETED, DAILY_CLAIMED, TRIVIA_*, EXAM_PASSED) - Create shared/lib/eventWiring.ts to register all domain event listeners - Convert quest event calls to `await systemEvents.emitAsync()` (5 calls) - Convert dashboard event calls to `systemEvents.emit()` fire-and-forget (5 calls) - Convert exam.service.ts userService import to static import (1 call) - Convert dashboard.service.ts events import to static import (1 call) - Leave inventory.service.ts validateAndExecuteEffect import unchanged (Task 3) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
73 lines
2.8 KiB
TypeScript
73 lines
2.8 KiB
TypeScript
import { systemEvents, EVENTS } from "@shared/lib/events";
|
|
import { questService } from "@shared/modules/quest/quest.service";
|
|
import { dashboardService } from "@shared/modules/dashboard/dashboard.service";
|
|
|
|
/**
|
|
* Registers all domain event listeners.
|
|
* Must be called once at startup before any domain events are emitted.
|
|
*
|
|
* This wiring replaces dynamic imports that were previously used to avoid
|
|
* circular dependencies between services (e.g., economy -> quest -> economy).
|
|
*/
|
|
export function registerDomainEventListeners() {
|
|
// --- Quest progress tracking (awaited via emitAsync to preserve tx atomicity) ---
|
|
|
|
systemEvents.on(EVENTS.DOMAIN.BALANCE_CHANGED, async ({ userId, type, tx }) => {
|
|
await questService.handleEvent(userId, type, 1, tx);
|
|
});
|
|
|
|
systemEvents.on(EVENTS.DOMAIN.XP_GAINED, async ({ userId, amount, tx }) => {
|
|
await questService.handleEvent(userId, 'XP_GAIN', amount, tx);
|
|
});
|
|
|
|
systemEvents.on(EVENTS.DOMAIN.ITEM_COLLECTED, async ({ userId, itemId, quantity, tx }) => {
|
|
await questService.handleEvent(userId, `ITEM_COLLECT:${itemId}`, quantity, tx);
|
|
});
|
|
|
|
systemEvents.on(EVENTS.DOMAIN.ITEM_USED, async ({ userId, itemId, tx }) => {
|
|
await questService.handleEvent(userId, `ITEM_USE:${itemId}`, 1, tx);
|
|
});
|
|
|
|
// --- Dashboard event recording (fire-and-forget) ---
|
|
|
|
systemEvents.on(EVENTS.DOMAIN.TRANSFER_COMPLETED, async ({ username, amount, toUserId }) => {
|
|
await dashboardService.recordEvent({
|
|
type: 'info',
|
|
message: `${username} transferred ${amount.toLocaleString()} AU to User ID ${toUserId}`,
|
|
icon: '💸'
|
|
});
|
|
});
|
|
|
|
systemEvents.on(EVENTS.DOMAIN.DAILY_CLAIMED, async ({ username, amount }) => {
|
|
await dashboardService.recordEvent({
|
|
type: 'success',
|
|
message: `${username} claimed daily reward: ${amount.toLocaleString()} AU`,
|
|
icon: '☀️'
|
|
});
|
|
});
|
|
|
|
systemEvents.on(EVENTS.DOMAIN.TRIVIA_STARTED, async ({ username, difficulty }) => {
|
|
await dashboardService.recordEvent({
|
|
type: 'info',
|
|
message: `${username} started a trivia game (${difficulty})`,
|
|
icon: '🎯'
|
|
});
|
|
});
|
|
|
|
systemEvents.on(EVENTS.DOMAIN.TRIVIA_WON, async ({ username, reward }) => {
|
|
await dashboardService.recordEvent({
|
|
type: 'success',
|
|
message: `${username} won ${reward.toLocaleString()} AU from trivia!`,
|
|
icon: '🎉'
|
|
});
|
|
});
|
|
|
|
systemEvents.on(EVENTS.DOMAIN.EXAM_PASSED, async ({ username, reward }) => {
|
|
await dashboardService.recordEvent({
|
|
type: 'success',
|
|
message: `${username} passed their exam: ${reward.toLocaleString()} AU`,
|
|
icon: '🎓'
|
|
});
|
|
});
|
|
}
|