Estrutura do projeto
Layout do monorepo, camadas DDD e onde cada coisa vive.
Nível superior
apps/
client/ Next.js — landing, dashboard, auth, /docs
server/ Express — API, auth, billing, jobs
packages/
contracts/ Schemas Zod compartilhados + tipos inferidos
shared/ Catálogo de permissions, Result, errors, branded types
tsconfig/ tsconfig bases
eslint-config/ flat configs que aplicam o layering DDD
tests/ Playwright e2eO repo é um workspace Bun. Todo import cross-package interno passa por @app/contracts, @app/shared, etc. — nunca relativo entre apps.
apps/server
src/
index.ts Entry point. otel-init DEVE ser o primeiro import.
otel-init.ts Inicia o OTel antes de qualquer outra coisa.
bootstrap/
env.ts Schema Zod do env. Fonte de verdade.
container.ts Registry de DI com Awilix.
app.ts Constrói a app Express e monta todos os routers em /api.
worker.ts Bootstrap do worker BullMQ.
infrastructure/
db/ Cliente Prisma + helpers.
http/ Helper api-route, error handler, rate-limit, registry OpenAPI, requirePermission.
events/ Bus de eventos em memória.
jobs/ Factories de queue + worker BullMQ, Bull Board.
cache/ Adapter Redis (no-op quando REDIS_URL não definido).
logger/ Setup do pino.
observability/ Sentry, exporters OTel, helper captureError.
i18n/ Runtime de tradução server-side.
modules/<context>/ Bounded contexts (veja abaixo).
shared/
aggregate-root.ts Base AggregateRoot + tipo DomainEvent.
usecase.ts Contrato UseCase<Input, Output>.
value-object.ts Base de value object.Camadas DDD dentro de um módulo
Todo módulo em apps/server/src/modules/<context>/ segue o mesmo layout:
domain/ Entities, value objects, repository interfaces.
Sem I/O. Sem imports de framework. Sem Prisma. Sem Express.
application/ Use cases, ports (output adapters), DTOs.
infrastructure/ Repos Prisma, adapters de terceiros, mappers.
interfaces/http/ Controladores Express, registro de rotas, schemas Zod.A regra de dependências é apenas para dentro: interfaces/ depende de application/, que depende de domain/. Infrastructure implementa os ports declarados em application/ (ou as repository interfaces declaradas em domain/).
ESLint aplica. domain/ não pode importar express, @prisma/client, nem nada de application/ / infrastructure/ / interfaces/. Tente — o import é sinalizado antes do commit.
Os bounded contexts entregues hoje: iam, tenancy, billing, notifications, storage, audit, feature-flags. Ver Bounded contexts.
apps/client
app/
page.tsx, es/, pt/ Landing pages (i18n).
_landing/ Componentes da landing + dicionário.
(auth)/ Sign-in, sign-up, forgot-password.
(dashboard)/ Surface autenticado da app.
docs/[[...slug]]/ Fumadocs.
api/search/ Endpoint de busca Orama.
globals.css Tokens (OKLCH) + base + utilities.
layout.tsx Carrega Geist Sans + Geist Mono uma única vez.
components/
brand/ Primitives do design system UseDeploy. Leia /docs/pt/frontend.
ui/ Primitives derivados de shadcn.
content/docs/ O MDX que você está lendo.
lib/
api/ Cliente openapi-fetch + tipos gerados.
validations/ Refinements client-only construídos sobre @app/contracts.
source.ts Content source do Fumadocs.O client fala com o server via openapi-fetch e os paths tipados em lib/api/openapi-types.ts. Nunca redefina um campo conhecido do server em um schema Zod do client — estenda o contract, não bifurque.
packages/contracts
Schemas Zod + tipos inferidos que cruzam o limite client/server. Forms e mutations importam daqui:
src/
auth.ts Login, register, password reset.
user.ts Perfil de user, update, list.
common.ts Primitives compartilhados (paginação, IDs).
index.ts Barrel.Se um endpoint do server muda seu body ou response, o contract muda aqui, o OpenAPI é regenerado (bun run generate:api), e o client compile-checa a nova forma.
packages/shared
src/
permissions/ Catálogo resource:action + helper hasPermission.
result/ Result<T, E> + construtores ok/err.
errors/ DomainError, UnauthorizedError, ForbiddenError, etc.
types/ Tipos branded para IDs.
index.ts Barrel.Importado como @app/shared em ambas as apps.