⚡ Domain Events (CQRS Pattern)

FVT'nin {entity}:{action} naming pattern'i ile event-driven mimari

🎯 4 Domain Event

EventTetikleyiciSubscribers
dividend:changedTemettü dağıtım günü (cron job)Wallet (gelir kayıt), Portföy (dividends update), Bildirim
portfolio:changedPozisyon değişti (trade, fiyat, dividend)Portföy UI, Sidebar widget, Cüzdan, Genel Varlık
target:changedHedef dağılım güncellendi veya rebalancePortföy Analiz, Hedef Dağılım sekmesi
trade:changedYeni al/sat işlemiİşlem geçmişi, Wallet, Analiz

📐 Pattern

{entity}:{action}

CQRS / Event Sourcing benzeri pattern. Namespaced events.

🔄 Event Flow Detayı

1. trade:changed → Multi-cascade

[POST /api/portfolio/trade] │ ▼ [Backend DB transaction] │ ▼ [Event: trade:changed] │ ▼ [Subscribers:] ├─ Portfolio service → portfolio:changed emit ├─ Wallet service → wallet:transaction emit ├─ Balance service → balance:changed emit └─ Analytics → PostHog event capture

2. portfolio:changed → Frontend Sync

[Backend emit: portfolio:changed] │ ▼ [WS Gateway broadcast] │ ├─→ Tab 1 → React Query invalidate ['portfolio', id] ├─→ Tab 2 → React Query invalidate (BroadcastChannel) └─→ Tab 3 → Same │ ▼ UI eş zamanlı refresh

3. dividend:changed → Auto Income

[Temettü cron job (örn THYAO ₺3.4420/lot)] │ ▼ [Portföydeki kullanıcılar query: WHERE sembol='THYAO' AND adet > 0] │ ▼ [Net hesap: Brüt × (1 - 0.15)] │ ▼ [INSERT wallet_transactions (gelir kayıt)] │ ▼ [Event: dividend:changed + wallet:transaction] │ ▼ [Bildirim: "₺X temettü tahsil edildi"] │ ▼ [Frontend WS event receive → UI refresh]

4. target:changed → Rebalance Suggest

[Kullanıcı: Hedef Dağılım > "Hisse %50, Tahvil %30, Döviz %20" set] │ ▼ [PUT /api/portfolio/{id}/target-allocation] │ ▼ [Event: target:changed] │ ▼ [Backend: Mevcut dağılım vs Hedef karşılaştır] │ ▼ [Frontend: Rebalance modal göster] │ ▼ [Kullanıcı onaylar → POST /rebalance]

📡 Backend Implementation (Tahmini)

// NestJS örneği
@Injectable()
export class PortfolioService {
  constructor(private eventBus: EventBus) {}

  async addTrade(dto: TradeDto) {
    await this.db.transaction(async (tx) => {
      // 1. Trade insert
      const trade = await tx.trades.create(dto);
      // 2. Position update (avg cost recalc)
      await tx.positions.upsert({ avgCost: newAvg, adet: newAdet });
      // 3. Wallet transaction
      await tx.walletTransactions.create({ user, amount: -dto.adet * dto.fiyat });
    });

    // 4. Domain events emit
    this.eventBus.emit('trade:changed', { tradeId, portfolioId });
    this.eventBus.emit('portfolio:changed', { portfolioId });
    this.eventBus.emit('wallet:transaction', { userId, amount });
  }
}

// Event handlers
@EventsHandler('portfolio:changed')
class PortfolioChangedHandler {
  handle(event) {
    this.wsGateway.broadcast(event.userId, 'portfolio:changed', event);
  }
}

🌐 Event Bus Mimari

[NestJS app] │ ▼ [Event Bus (in-memory)] │ ▼ [Redis Pub/Sub] ← Multi-server için │ ├─→ WS Gateway → tarayıcıya push ├─→ Notification Service → DB INSERT ├─→ Analytics → PostHog └─→ Audit Log → DB

🎁 Faydaları

🚨 Failure Senaryoları

SenaryoRiskMitigation
Event bus downWS update gelmezPage reload manuel refetch
Redis cluster failMulti-server sync bozukSingle-server failover
WS disconnectReal-time kayıpReconnect_attempt + polling fallback
Race conditionİki tab eş zamanlı tradeDB unique constraint + optimistic lock