R
Interview Deckreact · next.js · senior/staff
0·0·122
System Design

System Design — Camada Cliente (Event SDK)

Event SDK, dataLayer (GA4), batching + sendBeacon, Pixel App da VTEX IO. Onde o evento nasce.

A camada cliente é onde o evento é capturado. Componentes: (1) Storefront VTEX IO — React/SSR via Store Framework. (2) Event SDK / Pixel App — biblioteca que escuta cliques, scroll, page views e monta o objeto do evento já com event_id (UUID). (3) dataLayer / GTM — padroniza num schema único (GA4) antes de sair do browser. (4) Batching + sendBeacon — agrupa em lote, mantém fila local com retry, envia com navigator.sendBeacon que sobrevive ao fechamento da aba.

Camada Cliente — captura ao envio
eventbatch 20Usuárioclick PDPStorefrontPDPVTEX IOEvent SDKmonta eventoevent_id UUIDQueuebatchinglocalStoragesendBeacon→ /collectCDN edge
Problema
Cada página instrumentando do seu jeito gera schema inconsistente. Eventos perdidos quando o usuário fecha a aba antes do request terminar. fetch() bloqueia o unload da página. Volume de requests excessivo se enviar um por evento.
Solução
SDK centralizado padroniza captura. dataLayer padroniza schema (GA4). sendBeacon não bloqueia unload — sobrevive ao fechamento. Batching reduz requests (10-30 eventos por lote). Fila local com backoff exponencial garante entrega mesmo se a rede oscilar.
Tip
Na VTEX IO o conceito nativo é Pixel App — uma extensão que escuta eventos da storefront. Use o Pixel App em vez de instrumentar manualmente. event_id (UUID) gerado no cliente é crítico para idempotência depois.
// Event SDK — instrumentação na storefront VTEX IO
class EventSDK {
  private queue: Event[] = []
  private timer: ReturnType<typeof setTimeout> | null = null

  track(name: string, params: Record<string, unknown>) {
    this.queue.push({
      event_id: crypto.randomUUID(),
      event_name: name,
      client_timestamp: Date.now(),
      session_id: this.session(),
      user_id: this.user(),
      ...params,
    })
    this.scheduleFlush()
  }

  private scheduleFlush() {
    if (this.queue.length >= 20) return this.flush()
    if (!this.timer) {
      this.timer = setTimeout(() => this.flush(), 5000)
    }
  }

  private flush() {
    if (this.queue.length === 0) return
    const batch = this.queue.splice(0)
    if (this.timer) { clearTimeout(this.timer); this.timer = null }

    // sendBeacon não bloqueia o unload da página
    const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' })
    const ok = navigator.sendBeacon('/collect', blob)
    if (!ok) this.queue.unshift(...batch)  // retry
  }
}

// Hook no unload — garantia final
window.addEventListener('pagehide', () => sdk.flush())
Q.Por que sendBeacon e não fetch?
A.sendBeacon é assíncrono, não bloqueia o unload da página e o browser garante o envio mesmo se a aba fechar. fetch() durante unload é cancelado pelo browser. Para purchase event (crítico), sendBeacon evita perda quando o usuário fecha a aba após confirmar compra.
Q.O que é o dataLayer e por que padronizar?
A.dataLayer é uma camada intermediária (geralmente injetada pelo GTM) onde todo evento passa antes de sair do browser. Padronizar no schema GA4 desacopla a página das ferramentas de destino — você troca o GA por outra ferramenta sem mexer no código da página.
Q.Como funciona batching no cliente?
A.Fila local em memória (com persistência em sessionStorage para sobreviver a navegação). Flush em 3 triggers: tamanho máximo (ex: 20 eventos), tempo máximo (ex: 5s) e antes do unload (com sendBeacon). Retry com backoff exponencial se o request falhar.
105/122← →