Skip to main content

Stack tecnológica

CamadaTecnologiaVersão
FrameworkNestJS11.x
LinguagemTypeScript5.9
Banco de dadosPostgreSQL15
ORMTypeORM0.3
FilasBullMQ + Redis5.x
AutenticaçãoPassport + JWT
Validaçãoclass-validator + class-transformer
Rate limiting@nestjs/throttler6.x
Multi-tenancynestjs-cls (Continuation-Local Storage)6.x
E-mailResend + React Email
Documentação APISwagger (OpenAPI)

Estrutura de pastas

src/
├── main.ts                  # Bootstrap — CORS, ValidationPipe, Swagger, porta
├── app.module.ts            # Módulo raiz — registra todos os imports
├── app.controller.ts        # Health check

├── auth/                    # Autenticação (Passport, JWT, estratégias)
│   ├── strategies/          #   LocalStrategy, JwtStrategy
│   ├── guards/              #   JwtAuthGuard
│   ├── session.service.ts   #   Refresh tokens (Redis)
│   └── auth.service.ts      #   Login, logout, refresh

├── common/                  # Infraestrutura compartilhada
│   ├── tenancy/             #   ⭐ Multi-tenancy (middleware + subscriber)
│   ├── base-entity/         #   TenantBaseEntity (herança)
│   ├── guards/              #   Filtros de exceção
│   ├── decorators/          #   @CurrentUser, etc.
│   ├── interceptors/        #   Logging, transformação
│   ├── mail/                #   Templates React Email + envio via Resend
│   ├── outbox/              #   Padrão Outbox (eventos assíncronos)
│   ├── queues/              #   Registro global de filas BullMQ
│   ├── redis/               #   RedisCustomModule (ioredis)
│   └── transformers/        #   BigInt, decimais

├── catalog/                 # PIM — Gestão de produtos (★ módulo mais complexo)
│   ├── entities/            #   Product, Inventory, InventoryEntry, Kit…
│   ├── services/            #   CQRS: CommandService + QueryService
│   ├── controllers/         #   Endpoints REST
│   ├── processors/          #   Jobs BullMQ do catálogo
│   ├── repositories/        #   Queries especializadas
│   ├── dto/                 #   Create, Update, Bulk DTOs
│   └── types/               #   Enums, interfaces

├── integrations/            # Conectores de marketplace
│   ├── adapters/            #   MeliAdapter, ShopeeAdapter
│   ├── base/                #   BaseMarketplaceGateway (contrato)
│   ├── meli/                #   Lógica específica Mercado Livre
│   ├── oauth/               #   Fluxo OAuth callback
│   ├── orchestrators/       #   Orquestração de sincronização
│   ├── tokens/              #   CRUD de tokens criptografados
│   └── services/            #   Serviço principal

├── listings/                # Anúncios em marketplaces
├── orders/                  # Pedidos de venda
├── tenants/                 # Contas SaaS (tenants)
├── users/                   # Usuários e RBAC
├── companies/               # Perfil de empresa do tenant
├── brands/                  # Cadastro de marcas
├── suppliers/               # Cadastro de fornecedores
├── warehouses/              # Depósitos e locais de estoque
├── processing/              # Lógica de processamento em batch
├── workers/                 # Consumers BullMQ
├── config/                  # Configuração centralizada
├── database/                # Migrations TypeORM
├── types/                   # Tipos globais
└── utils/                   # Utilitários puros

Multi-tenancy

O sistema é estritamente multi-tenant. Cada empresa (tenant) enxerga apenas os seus próprios dados. A isolamento funciona em três camadas:
               ┌──────────────────────────────────┐
  Request ───▶ │   TenancyMiddleware              │
               │   Decodifica JWT, extrai          │
               │   tenantId e userId               │
               │   Salva no CLS (nestjs-cls)       │
               └───────────┬──────────────────────┘

               ┌───────────▼──────────────────────┐
   Service ───▶│   TenantAwareService             │
               │   this.currentTenantId            │
               │   Herança: extends TenantAware    │
               └───────────┬──────────────────────┘

               ┌───────────▼──────────────────────┐
     ORM  ───▶ │   TenantSubscriber               │
               │   beforeInsert() → injeta         │
               │   tenantId automaticamente        │
               └──────────────────────────────────┘

Três componentes da tenancy

ComponenteArquivoResponsabilidade
TenancyMiddlewarecommon/tenancy/tenancy.middleware.tsRoda em toda requisição. Decodifica o JWT e injeta tenantId e userId no CLS.
TenantAwareServicecommon/tenancy/tenant-aware.service.tsClasse base. Services que herdam dela ganham this.currentTenantId.
TenantSubscribercommon/tenancy/tenant.subscriber.tsTypeORM subscriber global. Injeta tenantId automaticamente em INSERT.
Regra crítica: O TenantSubscriber só garante isolamento em inserts. Todas as queries (find, createQueryBuilder, etc.) devem filtrar por tenantId explicitamente no service. Nunca confie apenas no subscriber para leitura.

TenantBaseEntity

Toda entity de domínio herda de TenantBaseEntity, que já traz as colunas id, tenantId, createdAt e updatedAt:
@Entity('products')
@Index('idx_product_tenant', ['tenantId'])
export class Product extends TenantBaseEntity {
  @Column({ name: 'name' })
  name: string;
  // ...
}

CQRS — Command / Query

Módulos complexos (como catalog) separam leitura e escrita em services distintos:
TipoExemploResponsabilidade
CommandServiceProductCommandServiceCriação, atualização, exclusão. Gerencia transações.
QueryServiceProductQueryServiceListagem, filtros, paginação. Otimizado para leitura.
ValidationServiceProductValidationServiceRegras de negócio chamadas pelo CommandService antes de gravar.

Regras de performance (QueryService)

  1. Sem joins pesados em listagem — agregados como BOM, componentes e estoque só são carregados em endpoints de detalhe (findOne).
  2. Paginação obrigatória — todo endpoint de lista deve paginar.
  3. Índices compostos — sempre criar @Index em combinações tenantId + campo_de_filtro.

Padrão de transação (CommandService)

async createProduct(dto: CreateProductDto) {
  return this.repository.manager.transaction(async (manager) => {
    // 1. Validação
    await this.validationService.validate(dto);
    // 2. Criação da entity principal
    const product = manager.create(Product, { ...dto, tenantId: this.currentTenantId });
    await manager.save(product);
    // 3. Criação de dependentes dentro da MESMA transação
    await this.createInventory(manager, product);
    return product;
  });
}
Sempre passe o manager recebido no callback para métodos auxiliares. Isso garante que tudo roda dentro da mesma transação.

Autenticação

ConceitoImplementação
LoginPOST /auth/login — LocalStrategy valida credenciais, retorna JWT
Access tokenJWT com { sub, tenantId, role }, expira em minutos
Refresh tokenArmazenado como cookie HttpOnly, gerenciado no Redis
RefreshPOST /auth/refresh — lê cookie, valida no Redis, gera novo access
LogoutPOST /auth/logout — apaga refresh token do Redis

Guards

  • JwtAuthGuard — protege todas as rotas autenticadas.
  • ThrottlerGuard — rate limit global: 200 req/min por IP.

Filas e processamento assíncrono

Jobs assíncronos usam BullMQ com Redis como broker:
  Controller               FIla (Redis)              Worker (Processor)
  ──────────                ──────────                ──────────────────
  Ação do usuário  ───▶   enfileira job   ───▶      processa em background
                           (auto-retry 3x)           (exponential backoff)

Configuração global

ParâmetroValorDescrição
attempts3Tentativas antes de marcar como falha
backoff.typeexponentialAtraso cresce a cada retry
backoff.delay5 000 msDelay base
removeOnCompletetrueJobs completos são apagados do Redis
removeOnFail.age24hJobs falhos são mantidos por 24h

Padrão Outbox

O módulo common/outbox/ implementa o padrão Transactional Outbox: eventos são gravados no banco dentro da mesma transação da operação de domínio e depois processados pelo worker. Isso garante consistência mesmo que o Redis esteja temporariamente indisponível.

Integrações com marketplaces

A camada de integrações segue o padrão Adapter:
                       ┌──────────────────────────────┐
                       │   BaseMarketplaceGateway     │
                       │   (contrato / interface)      │
                       └──────────┬───────────────────┘

               ┌──────────────────┤──────────────────────┐
               │                  │                      │
       ┌───────▼───┐      ┌──────▼──────┐       ┌───────▼───────┐
       │MeliAdapter│      │ShopeeAdapter│       │ Futuro adapter│
       └───────────┘      └─────────────┘       └───────────────┘

Fluxo de conexão OAuth

  1. Frontend redireciona o usuário para a URL de autorização do marketplace.
  2. Marketplace redireciona de volta para POST /integrations/oauth/callback.
  3. Backend troca o code por tokens de acesso.
  4. Tokens são criptografados com TOKEN_ENCRYPTION_KEY e salvos no banco.
  5. Um @Cron job renova tokens automaticamente antes de expirarem.

Serviço de tokens

O módulo integrations/tokens/ gerencia o armazenamento seguro dos tokens OAuth:
  • Criptografia AES usando a variável TOKEN_ENCRYPTION_KEY.
  • Cada par de tokens (access + refresh) é associado ao tenantId + marketplace.
  • Renovação automática via scheduler.

Módulos do sistema

MóduloDiretórioDescrição
Authauth/Login, refresh, logout, password reset, strategies
Catalogcatalog/PIM completo: produtos, estoque, kits, custos, NCM
Integrationsintegrations/Conectores OAuth (Mercado Livre, Shopee), adapters
Listingslistings/Gerenciamento de anúncios publicados nos marketplaces
Ordersorders/Pedidos de venda sincronizados dos marketplaces
Usersusers/CRUD de usuários, perfis e permissões (RBAC)
Tenantstenants/Gestão de contas SaaS
Companiescompanies/Dados cadastrais da empresa
Brandsbrands/Cadastro de marcas
Supplierssuppliers/Cadastro de fornecedores
Warehouseswarehouses/Depósitos e locais de estoque multi-warehouse
Processingprocessing/Lógica de processamento batch
Workersworkers/Consumers BullMQ (processamento assíncrono)

Módulo Catalog em detalhe

O catálogo é o módulo mais extenso. Seus services cobrem:
ServiceResponsabilidade
ProductCommandServiceCRUD de produtos, bulk create/update
ProductQueryServiceListagem com filtros, busca, paginação
ProductValidationServiceRegras de negócio (SKU único, campos obrigatórios)
InventoryServiceSaldo de estoque por warehouse
InventoryEntryServiceEntradas e ajustes de estoque
InventoryDashboardServiceDashboard analytics de estoque
StockMovementServiceHistórico de movimentações
StockTransferServiceTransferências entre depósitos
KitServiceKits e produtos compostos (BOM)
ProductComponentsServiceComponentes de cada kit
CostServiceCálculo de custos (CMC, custo médio)
ProductCostServiceCusto de produção e markup
NcmServiceClassificação fiscal NCM/CEST
NfeParserServiceParser de XML de NF-e para importação
ProductCacheServiceCache Redis de dados de produto
ProductInheritanceServiceHerança de atributos para variações

Padrões de código

Entities

// ✅ Correto
@Entity('products')
@Index('idx_product_tenant', ['tenantId'])
@Index('idx_product_tenant_sku', ['tenantId', 'sku'])
export class Product extends TenantBaseEntity {
  @Column({ name: 'seq_id' })
  seqId: number;

  @Column({ name: 'sku', nullable: true })
  sku: string;
}

DTOs

// Separar em Create, Update e Bulk
export class CreateProductDto {
  @IsString()
  @IsNotEmpty()
  name: string;

  @IsOptional()
  @IsString()
  sku?: string;
}

export class UpdateProductDto extends PartialType(CreateProductDto) {}

Error handling

// ✅ Usar exceções HTTP do NestJS
throw new NotFoundException(`Produto ${id} não encontrado`);
throw new BadRequestException('SKU já existe para este tenant');

// ❌ Nunca expor erros internos do banco
// throw error;  ← NÃO FAÇA ISSO

Testes

TipoLocalizaçãoComandoFoco
Unitáriossrc/**/*.spec.tsnpm run testLógica de negócio nos services
E2Etest/npm run test:e2eFluxos completos da API
Cargaload-tests/npm run test:loadPerformance sob carga (k6)

Checklist para novos desenvolvedores

1

Entenda a multi-tenancy

Leia common/tenancy/ — middleware, subscriber e TenantAwareService. Toda query deve filtrar por tenantId.
2

Crie a Entity

Em src/seu-modulo/entities/. Herde de TenantBaseEntity. Use snake_case para colunas e crie índices.
3

Crie os DTOs

Separe CreateXDto, UpdateXDto e, se necessário, BulkCreateXDto. Use class-validator.
4

Crie o Service

Para módulos simples, um único service. Para módulos complexos, separe em Command e Query.
5

Crie o Controller

Defina endpoints REST. Decore com @ApiTags, @ApiResponse para o Swagger.
6

Registre no Module

Adicione controllers e providers no module do domínio. Importe no AppModule se for um módulo novo.
7

Gere a Migration

npm run migration:generate -- --name=NomeDaAlteracao → revise → npm run migration:run.