Jumentix
Blueprint realtime

APIs bidirecionais com fallback incorporado

Execute Socket.IO ou gRPC como interface principal enquanto um processo REST separado oferece fallback e documentação AsyncAPI.

Mascote open source do Jumentix

O que este blueprint entrega à sua equipe

  • Mensagens request/response correlacionadas
  • Redis Streams e resiliência em cluster para Socket.IO
  • Contratos AsyncAPI compartilhados com clientes gerados
const socket = io('http://localhost:3001');

socket.emit('request', {
  id: crypto.randomUUID(),
  subject: 'tasks.create',
  payload: { title: 'Realtime task' },
});

socket.on('response', ({ id, payload }) => {
  console.log(id, payload);
});

Do zero ao primeiro MVP

Uma sequência prática de lançamento para este blueprint

Use este caminho quando o primeiro MVP precisa parecer vivo: progresso, colaboração, notificações ou resultados de comando voltando por canal bidirecional.

Escolha o momento live

Escolha o único evento que usuários precisam ver sem refresh, como task.created ou task.statusChanged.

  • Escolha tasks.create como único comando aceito pelo socket no MVP.
  • Escolha tasks.created como único evento que a UI renderiza sem refresh.
  • Congele o formato de mensagem: subject mais payload, nada anônimo.
  • Escreva critérios de aceite: subjects não suportados retornam erro explícito.

Saída MVP: um canal AsyncAPI e um subject request/response correlacionado.

type Task = { id: string; title: string; categoryId: string; completed: boolean };

type TaskCommand = {
  subject: 'tasks.create';
  payload: { title: string; categoryId: string };
};

type TaskEvent =
  | { subject: 'tasks.ready'; payload: { total: number } }
  | { subject: 'tasks.created'; payload: Task };

Critérios de pronto do primeiro MVP

  • Toda resposta realtime carrega o subject que o client enviou — sem efeitos anônimos.
  • O fallback REST retorna o mesmo resultado de negócio quando o socket está indisponível.
  • Um drill de reconnect é capturado como evidência antes do piloto.
  • Processos realtime e fallback rodam sob um perfil PM2 nomeado com logs inspecionáveis.

Fora de escopo de propósito

  • Fan-out multi-região e cluster com Redis Streams.
  • Presence, indicadores de digitação e replay de histórico.
  • Streaming gRPC além do primeiro subject request/response.
  • Escala horizontal da camada de socket.

Escopo MVP

Mantenha a primeira versão pequena o bastante para comprovar

Um evento live

Comece com um evento que muda a UI ou confirma um comando de forma visível.

Fatia realtime

Um fallback

Mantenha REST disponível para que o primeiro MVP tenha recuperação suportável.

Fatia de confiabilidade

Um perfil de processo

Rode realtime e fallback explicitamente com perfis locais ou PM2.

Fatia operacional
Área de provaPergunta a responderMecanismo JumentixEvidência MVP
CorrelaçãoO client relaciona toda resposta ao request?Message id, contrato de subject, testes do response handler.Sem efeitos realtime anônimos.
FallbackREST retorna o mesmo resultado quando realtime cai?Rota fallback e contrato de use-case compartilhado.Modo degradado continua usável.
OperaçãoO processo pode iniciar, ser inspecionado e reiniciado?Perfil PM2/runtime e logs.O MVP é demonstrável fora do terminal de dev.

Implementação prática

Código completo para a primeira fatia funcional

Estes exemplos mantêm Category e Task como vocabulário de produto e mostram controller, contrato, client, worker ou camada de estado necessários para chegar a um MVP executável.

type Task = { id: string; title: string; categoryId: string; completed: boolean };
type Client = { send: (message: string) => void };

const tasks = new Map<string, Task>();
const clients = new Set<Client>();

function broadcast(subject: string, payload: unknown) {
  const message = JSON.stringify({ subject, payload });
  for (const client of clients) client.send(message);
}

export function connectTaskSocket(client: Client) {
  clients.add(client);
  client.send(JSON.stringify({ subject: 'tasks.ready', payload: { total: tasks.size } }));
  return () => clients.delete(client);
}

export async function handleTaskMessage(message: { subject: string; payload: { title: string; categoryId: string } }) {
  if (message.subject !== 'tasks.create') return { ok: false, error: 'unsupported subject' };
  const task: Task = {
    id: crypto.randomUUID(),
    title: message.payload.title,
    categoryId: message.payload.categoryId,
    completed: false
  };
  tasks.set(task.id, task);
  broadcast('tasks.created', task);
  return { ok: true, result: task };
}

Construa o produto. Preserve a arquitetura.

Explore o código, execute a fábrica localmente e transforme seu próximo serviço Node.js em uma capacidade repetível de plataforma.