Jumentix
Blueprint frontend

Crie produtos instaláveis que continuam funcionando offline

Combine SDKs gerados com arquitetura SPA ou PWA, persistência IndexedDB e o mesmo vocabulário de contratos do backend.

Mascote open source do Jumentix

O que este blueprint entrega à sua equipe

  • Fluxos de dados offline-first
  • Pacotes cliente REST, Socket.IO e gRPC
  • Governança compartilhada entre frontend e backend
bun install
bun run cli
bun run dev:express

Do zero ao primeiro MVP

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

Use este caminho quando o primeiro MVP precisa rodar no browser, manter estado local e depois sincronizar com contratos backend.

Modele registros locais

Comece com tabelas Category e Task, um filtro, uma ação de create e um evento que a UI consegue ouvir.

  • Defina o schema Cana: stores categories e tasks com keyPath id.
  • Adicione os índices byCategory e byCompleted que os filtros da UI vão usar.
  • Congele a versão 1 do schema e escreva a regra de upgrade da próxima versão.
  • Escreva critérios de aceite: registros sobrevivem a um reload do browser.

Saída MVP: modelo de dados browser com fluxo visível.

const schema = {
  version: 1,
  stores: [
    { name: 'categories', keyPath: 'id', indexes: [{ name: 'byName', keyPath: 'name', unique: true }] },
    {
      name: 'tasks',
      keyPath: 'id',
      indexes: [
        { name: 'byCategory', keyPath: 'categoryId' },
        { name: 'byCompleted', keyPath: 'completed' }
      ]
    }
  ]
} as const;

Critérios de pronto do primeiro MVP

  • O fluxo create/list funciona com a rede desligada.
  • Componentes re-renderizam a partir dos eventos de mudança confirmados do Cana.
  • A premissa de sync está documentada: local-only, sync-later ou apoiado por API.
  • Registros sobrevivem a reload do browser — IndexedDB durável, não localStorage.

Fora de escopo de propósito

  • Resolução de conflitos avançada para edições concorrentes.
  • Push notifications e background sync.
  • Identidade multi-device e gerenciamento de sessão.
  • Migrações de schema IndexedDB além da versão 1.

Escopo MVP

Mantenha a primeira versão pequena o bastante para comprovar

Duas tabelas locais

Category e Task provam relacionamento, filtro e atualização de UI por evento.

Fatia de dados

Uma biblioteca de estado

Use Context, Redux ou Pinia como primeira superfície de integração.

Fatia de UI

Uma premissa de sync

Documente se o MVP é local-only, sync-later ou apoiado por API.

Fatia de produto
Área de provaPergunta a responderMecanismo JumentixEvidência MVP
Dados locaisO app cria e lê registros sem servidor?Playground Cana/browser in-memory e checks de persistência local.O primeiro fluxo funciona offline.
Atualizações de estadoComponentes atualizam a partir dos eventos do Cana?Exemplos React/Vue de state management e assertions de listener.UI permanece consistente com dados locais.
Backend futuroO mesmo vocabulário mapeia para contratos de API depois?Nomes de SDK gerado e paridade de contrato Category/Task.O MVP frontend não inventa outro domínio.

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 Category = { id: string; name: string };
type Task = { id: string; title: string; categoryId: string; completed: boolean };

const categories = new Map<string, Category>([
  ['work', { id: 'work', name: 'Work' }]
]);
const tasks = new Map<string, Task>();
const listeners = new Set<() => void>();

export function subscribe(listener: () => void) {
  listeners.add(listener);
  return () => listeners.delete(listener);
}

export function createTask(input: { title: string; categoryId: string }) {
  if (!categories.has(input.categoryId)) throw new Error('category not found');
  const task = { id: crypto.randomUUID(), completed: false, ...input };
  tasks.set(task.id, task);
  listeners.forEach((listener) => listener());
  return task;
}

export function listTaskCards() {
  return [...tasks.values()].map((task) => ({
    ...task,
    category: categories.get(task.categoryId)
  }));
}

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.