Types

MFEEventBus

Bus eventi globale per comunicazione cross-MFE. API minimale emit/on/off con payload typed.

Contratto

interface MFEEventBus {
  emit(eventName: string, payload?: unknown): void
  on(eventName: string, handler: (payload?: unknown) => void): void
  off(eventName: string, handler: (payload?: unknown) => void): void
}

Quando usarlo

L'EventBus è il canale di comunicazione fire-and-forget tra MFE. Tipico per:

  • notifiche di stato ('user-logged-out', 'theme-changed')
  • broadcast di eventi business ('cliente-creato', 'ordine-confermato')
  • refresh coordinato ('data-invalidated' → ogni MFE ricarica le proprie liste)
Non usare per:
  • richieste request/response (usa ctx.http o uno store condiviso)
  • dati persistenti (usa API server-side)
  • accoppiamento stretto tra MFE (introduce dipendenze nascoste)

Pattern d'uso

Publish

import { useMfeContext } from '@pzeta/mfe-contracts/composables'

const ctx = useMfeContext()

async function creaCliente(data: CreateClienteRequest) {
  const cliente = await ctx.http.post<Cliente>('/api/clienti', data)
  ctx.eventBus?.emit('cliente-creato', cliente.data)
}

Subscribe

import { onMounted, onBeforeUnmount } from 'vue'
import { useMfeContext } from '@pzeta/mfe-contracts/composables'

const ctx = useMfeContext()

function onClienteCreato(payload: unknown) {
  const cliente = payload as Cliente
  // ricarica lista clienti
}

onMounted(() => {
  ctx.eventBus?.on('cliente-creato', onClienteCreato)
})

onBeforeUnmount(() => {
  ctx.eventBus?.off('cliente-creato', onClienteCreato)
})
Ricordati sempre off() in onBeforeUnmount. I listener orfani causano memory leak e bug di "doppia esecuzione" dopo unmount/remount del MFE.

Tipizzare il payload

La signature unknown è intenzionale per disaccoppiare i MFE. Per ottenere type safety, definisci un registro eventi condiviso in un pacchetto comune:

// @pzeta/shell-events (pacchetto condiviso shell + MFE)
export interface ShellEvents {
  'cliente-creato': Cliente
  'cliente-eliminato': { id: string }
  'user-logged-out': void
}
// Wrapper tipizzato nel MFE
import type { ShellEvents } from '@pzeta/shell-events'

function emit<K extends keyof ShellEvents>(name: K, payload: ShellEvents[K]) {
  ctx.eventBus?.emit(name, payload)
}

function on<K extends keyof ShellEvents>(
  name: K,
  handler: (payload: ShellEvents[K]) => void,
) {
  ctx.eventBus?.on(name, handler as (p?: unknown) => void)
}

Implementazione lato shell

La shell deve fornire un'implementazione concreta di MFEEventBus. Esempio minimale basato su EventTarget:

class ShellEventBus implements MFEEventBus {
  private readonly target = new EventTarget()
  private readonly wrappers = new Map<
    (payload?: unknown) => void,
    EventListener
  >()

  emit(eventName: string, payload?: unknown): void {
    this.target.dispatchEvent(new CustomEvent(eventName, { detail: payload }))
  }

  on(eventName: string, handler: (payload?: unknown) => void): void {
    const wrapper: EventListener = (ev) =>
      handler((ev as CustomEvent).detail)
    this.wrappers.set(handler, wrapper)
    this.target.addEventListener(eventName, wrapper)
  }

  off(eventName: string, handler: (payload?: unknown) => void): void {
    const wrapper = this.wrappers.get(handler)
    if (wrapper) {
      this.target.removeEventListener(eventName, wrapper)
      this.wrappers.delete(handler)
    }
  }
}

In alternativa: mitt, nanoevents, RxJS Subject, o store globale Pinia.

Tipi correlati