Arquitetura / Visão Geral Da Arquitetura
Fonte no GitHub

Visão Geral Da Arquitetura

O MangoStudio segue uma arquitetura modular inspirada em DDD distribuída em três workspaces. Este documento explica as decisões de design, as responsabilidades de cada camada e o fluxo de dados.

Mapa Dos Workspaces

Workspace Papel Stack
apps/api Servidor de API backend Elysia, Better Auth, Kysely + SQLite
apps/frontend SPA do navegador React 19, Vite 8, TanStack Router/Query, Tailwind CSS v4
apps/shared Contratos agnósticos a framework Tipos TypeScript, schemas TypeBox, dicionários i18n

Arquitetura Dos Módulos Da API

A API é organizada em módulos de domínio dentro de apps/api/src/modules/. Cada módulo segue uma estrutura inspirada em DDD:

modules/<domain>/
  application/     → Serviços de caso de uso (lógica de orquestração)
  domain/          → Regras e entidades de domínio (opcional, omitido em módulos simples)
  http/            → Definições de rotas Elysia
  infrastructure/  → Implementações de repositório/persistência

Responsabilidades Das Camadas

application/ — Serviços de orquestração que implementam casos de uso de negócio. Coordenam repositórios, serviços e lógica de domínio. Exemplos:

  • stream-text-turn.ts — o loop principal de geração que orquestra provedores, tools e continuação.
  • create-chat.ts — cria um novo chat com validação de ownership.
  • prompt-composer.ts — monta prompts de sistema com composição de regras.

domain/ — Funções puras e tipos que descrevem invariantes do domínio. Só existe quando as regras de negócio precisam de isolamento em relação à infraestrutura. Exemplos:

  • chat-ownership.ts — regras de validação de ownership.
  • connector.ts — validação da entidade connector.

http/ — Plugins de rota Elysia que tratam preocupações HTTP, como parsing de request, guards de autenticação e serialização de resposta. Eles delegam para serviços de aplicação.

infrastructure/ — Implementações de repositório com Kysely que abstraem queries SQL atrás de interfaces tipadas. Um repositório por aggregate root.

Módulos

Módulo Camadas Responsabilidades
chats A/D/H/I CRUD de chats, ownership, compactação de contexto
messages A/D/H/I Persistência de mensagens, consultas da galeria
generation A/D/H/I Streaming de turnos de texto, geração de imagem, resolução de modelo
connectors A/D/H/I Gerenciamento de chaves de API, enable/disable de modelos, persistência de secrets
app-settings A/H/I Configurações globais do usuário (tema, idioma, defaults de geração)
provider-settings A/H/I Configuração de runtime por provedor (cache, reasoning, tokens)
tool-settings A/H/I Enable/disable de tools e overrides de parâmetros
prompt-rules A/H Composição de prompts de sistema e resolução de arquivos de regra
attachments A/I Upload de arquivos, validação, storage e entrega ao provedor

A = Application, D = Domain, H = HTTP, I = Infrastructure

Quando Pular Camadas

Módulos simples pulam camadas que só adicionariam cerimônia sem gerar valor:

  • Sem domain/ — quando as regras de negócio são autoevidentes (por exemplo, app-settings é um wrapper CRUD simples com normalização).
  • Sem http/ — quando o módulo é puramente infraestrutural (por exemplo, generated-images/ só possui repositório).
  • Sem application/ — nunca; todos os módulos têm pelo menos um serviço.

Fluxo De Dados

Request Típico Da API

Browser (Eden Treaty)


Elysia Route (http/)
  ├─ middleware requireAuth
  ├─ validação do body com TypeBox


Application Service (application/)
  ├─ verificação de ownership (domain/)
  ├─ lógica de negócio
  ├─ chamadas a repositórios (infrastructure/)


Resposta (tipada via shared contracts)

Request De Streaming (Chat)

Browser (fetch + ReadableStream)


respond-stream-routes.ts
  ├─ validação prévia (ownership, conteúdo, modelo)


stream-text-turn.ts (orquestrador)
  ├─ resolve modelo + provedor
  ├─ compõe prompt rules
  ├─ decide estratégia de continuação
  ├─ chama provider.generateAgentTurnStream()
  ├─ executa tools (se o modelo chamá-las)
  ├─ envia resultados das tools de volta ao provedor
  └─ persiste o turno → produz eventos SSE

Preocupações Transversais

Auth

O Better Auth gerencia sessões via autenticação baseada em cookies. O plugin auth-middleware.ts decora rotas com requireAuth, que valida a sessão e injeta o contexto do usuário.

Tratamento De Erros

  • Erros de API usam ApiErrorResponse de @mangostudio/shared/contracts: { error: string, code?: string, details?: Record<string, string> }. O HTTP status é carregado pela resposta, não pelo body.
  • Erros de streaming usam SSEErrorEvent de @mangostudio/shared/streaming: { type: 'error', error, done: true }.
  • Erros de domínio estendem Error com códigos tipados, como ChatNotFoundError e ToolParameterError.
  • O plugin centralizado error-handler.ts mapeia exceções lançadas para respostas HTTP.

Rate Limiting

Há um rate limiter em memória (rate-limit.ts) que conta requests por (bucket, IP do cliente). Uma função classify (rate-limit-policy.ts) classifica cada path em um bucket nomeado — health e auth têm buckets próprios e mais generosos, então não ficam sujeitos ao limite geral da API, enquanto os demais endpoints compartilham o bucket general de base. Requests bloqueados retornam 429 no formato ApiErrorResponse (code: RATE_LIMITED) com header Retry-After. A limpeza de contadores expirados é lazy. Pode operar de forma proxy-aware quando a aplicação está atrás de reverse proxies.

Validação

  • Bodies de request são validados via schemas TypeBox nos contratos compartilhados.
  • Shapes de resposta são validados em testes de integração com Value.Check.
  • Uploads de attachment são validados por MIME type, magic bytes, tamanho e correção de UTF-8.

Arquitetura De Provedores

Os provedores implementam a interface AIProvider. O provider registry mapeia tipos de provedor para implementações. Camadas principais de abstração:

stream-text-turn.ts (orquestrador)

  ├─ resolve-model.ts → seleciona provedor + modelo
  ├─ continuation-runtime.ts → decide estratégia


Implementação do provedor (ex.: gemini/interactions-stream.ts)
  ├─ generateAgentTurnStream() → produz AgentEvent[]
  ├─ continuation-envelope.ts → serializa estado do cursor


Formato de wire do provedor (Gemini / OpenAI / Anthropic / DeepSeek)

Veja continuation.md para a arquitetura completa de continuação e ../providers/development.md para o guia de integração de provedores.

Arquitetura Do Frontend

routes/                → páginas file-based do TanStack Router
  _authenticated/      → rotas protegidas por autenticação
    index.tsx           → Chat (padrão)
    gallery.tsx         → Galeria de imagens
    settings.tsx        → Layout de configurações
    studio.tsx          → Workspace de imagem

features/              → módulos de feature por domínio
  chat/                → UI, componentes, hooks e serviços de chat
  gallery/             → página da galeria + queries
  generation/          → hooks e tipos de geração de texto/imagem
  settings/            → submódulos de settings (app, connectors, providers, tools, prompts)
  sidebar/             → sidebar + context ring

components/            → componentes compartilhados de UI
  ui/                  → primitivas do design system (Button, Input, Card etc.)
  layout/              → Header, Layout, ModelSelector, ThinkingToggle
  MarkdownContent.tsx  → renderização de markdown com syntax highlighting

Fluxo De Dados No Frontend

TanStack Query (cache + invalidação)


Eden Treaty Client (cliente de API tipado)


Elysia API (servidor)

Pacote Shared

O pacote shared é agnóstico a framework e pode ser importado tanto pela API quanto pelo frontend:

shared/src/
  contracts/           → barrel export de todos os tipos de contrato
  <module>/            → contratos por módulo + schemas TypeBox
  streaming/           → tipos e schemas de eventos SSE
  types/               → tipos de domínio (provider, agent-events, gallery)
  i18n/                → dicionários em Português/Inglês + sistema de tipos
  test-utils/          → mock factories compartilhadas

Imports entre workspaces usam nomes de pacote (@mangostudio/shared), nunca caminhos relativos.

Banco De Dados

SQLite via Kysely com 18 migrações cobrindo:

  • Tabelas centrais: chats, messages, generated_images, chat_attachments
  • Tabelas de auth: schema padrão do Better Auth (5 tabelas)
  • Tabelas de settings: user_app_settings, user_provider_settings, user_tool_settings

Colunas usam camelCase; tabelas usam snake_case. Aliases tipados do Kysely seguem o padrão <Entity>Select, <Entity>Insert, <Entity>Update.

Build Standalone

bun run build --binary compila a API em binários específicos por plataforma via bun build --compile. Os assets do frontend são embarcados como arquivos sidecar. Há suporte a 8 plataformas (linux/windows/darwin × x64/arm64 + glibc/musl). O banco de dados usa ~/.mango/database.sqlite por padrão.