Como criar um modulo

Modulos de negocio (protocolo, financeiro, tributario, atendimento) sao adicionados em modules/<nome>/ (manifestos) + apps/api/src/modules/<nome>/ (runtime).

1. Esqueleto

modules/protocolo/
├── module.json
├── audit.json
├── openapi.yaml
└── README.md

apps/api/src/modules/protocolo/
├── protocolo.module.ts
├── protocolo.controller.ts
├── protocolo.service.ts
├── protocolo.service.test.ts
└── dto/
    ├── criar-protocolo.dto.ts
    └── movimentar.dto.ts

2. Schema

// apps/api/prisma/schema.prisma
model Protocolo {
  id          String   @id @default(uuid())
  municipioId String  // OBRIGATORIO (R10)
  numero      String
  ...
  @@index([municipioId])
  @@map("protocolos")
}

Rode prisma migrate diff para gerar a migration.

3. Service usando tenant + audit

@Injectable()
export class ProtocoloService {
  constructor(private prisma: GovonPrismaService) {}

  async criar(input: CriarProtocoloInput) {
    return this.prisma.db.protocolo.create({
      data: scopedData({
        numero: gerarNumero(),
        requerenteCpf: input.cpf,
      }),
    });
  }

  async listar() {
    return this.prisma.db.protocolo.findMany({
      where: scopedWhere({}),
      orderBy: { criadoEm: 'desc' },
    });
  }
}

4. Controller com permissoes

@Controller('protocolo')
export class ProtocoloController {
  constructor(private service: ProtocoloService) {}

  @Post()
  @RequirePermission('protocolo.criar')
  criar(@Body() body: CriarProtocoloInput) { ... }

  @Get()
  @RequirePermission('protocolo.ler')
  listar() { ... }

  @Post(':id/movimentacoes')
  @RequirePermission('protocolo.movimentar')
  movimentar(@Param('id') id: string, @Body() body) { ... }
}

5. Mascarar dados sensiveis em listagens

import { maskObject } from '@govon/mask';

return items.map(p => maskObject(p, { cpfRequerente: 'cpf' }));

6. Upload de anexos

import { UploadHandler, LocalFileStorage } from '@govon/upload';

const storage = new LocalFileStorage({ baseDir: process.env.UPLOAD_DIR! });
const upload = new UploadHandler({
  storage,
  rules: { allowedMime: ['application/pdf'], maxBytes: 10 * 1024 * 1024 },
  audit: (e) => prisma.uploadAuditLog.create({ data: e }),
});

7. Job de notificacao

import { BullMqQueueAdapter, JobRunner } from '@govon/jobs';

const queue = new BullMqQueueAdapter({
  queueName: 'protocolo.notificar',
  connection: { host: 'redis', port: 6379 },
  maxAttempts: 5,
  baseDelayMs: 2000,
});

const runner = new JobRunner(queue, {
  name: 'protocolo.notificar',
  audit: (e) => prisma.jobAuditLog.create({ data: e }),
});

await runner.enqueue({ protocoloId });

8. Validacao

npm run typecheck && npm run lint && npm run test && npm run build

Checklist completo em /modulos.