Piyush-Nagar/coding-agents-skills

Nossos serviços backend seguem a organização **Pacote por Funcionalidade** em quatro módulos irmãos. Este documento é a fonte da verdade para onde colocar novo código.

O que é coding-agents-skills?

Esta skill define uma estrutura de pacotes rigorosa para serviços backend, seguindo o princípio 'Pacote por Funcionalidade'. Ela organiza o código em quatro módulos irmãos: `capabilities` (lógica de negócio e controladores), `db` (persistência com repositórios), `client` (integrações externas) e `starter` (configuração e inicialização). A principal vantagem é que alterações relacionadas a uma mesma funcionalidade ficam agrupadas no mesmo local, facilitando a manutenção e a evolução do sistema. A skill é agnóstica em relação à linguagem de programação, mas os exemplos são fornecidos em Java/Spring Boot, e as regras de nomenclatura (como sufixos `*Repo`, `*Client`, `*Db`) devem ser seguidas rigorosamente. Ela é útil para qualquer desenvolvedor ou agente de IA que precise criar ou modificar código em um backend que siga essa arquitetura, garantindo consistência e respeitando as regras de dependência entre os módulos.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Piyush-Nagar/coding-agents-skills/tree/HEAD/backend/core

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Estrutura de Pacotes

Nossos serviços backend seguem a organização Pacote por Funcionalidade em quatro módulos irmãos. Este documento é a fonte da verdade para onde colocar novo código.

Nota sobre a linguagem. Os exemplos abaixo estão escritos em Java/Spring Boot porque é nossa principal stack de backend, mas a estrutura em si é agnóstica em relação à linguagem e obrigatória em todas as linguagens backend que usamos (Kotlin, Go, Python, Node, etc.). Traduza as extensões de arquivo e anotações do framework; mantenha a estrutura de pastas, sufixos de nome (*Db, *Repo, *RepoImpl, *Dao, *Controller, *ControllerImpl) e a divisão interface/implementação inalterados.

Princípio central

Agrupar por funcionalidade, não por camada técnica. Uma alteração na funcionalidade "pedidos" deve tocar em arquivos que ficam próximos uns dos outros, não em arquivos espalhados pelos pacotes controllers/, services/, repositories/. A divisão em camadas técnicas acontece no limite do módulo (db, client, capabilities, starter), e a organização por funcionalidade acontece dentro de cada módulo.

Os quatro módulos

Cada serviço é composto por estes quatro módulos irmãos:

<serviço>/
├── capabilities/   # funcionalidades/lógica de negócio — o cérebro
├── db/             # persistência: entidades, repositórios, migrações
├── client/         # integrações de saída com sistemas externos
└── starter/        # Inicializador Spring Boot — conecta tudo e executa

capabilities/ — lógica de negócio

Cada funcionalidade tem seu próprio pacote, e dentro de cada funcionalidade há um sub-pacote por endpoint de API (getById, create, updateById, getByQuery, …). Cada sub-funcionalidade possui três pastas irmãs:

  • service/ — a classe de serviço única para aquela API
  • dto/ — request/ e response/ (o que for aplicável)
  • mapper/ — traduz entidades *Db ↔ DTOs (ambas as direções) apenas para esta sub-funcionalidade

O controlador da funcionalidade fica na raiz do pacote da funcionalidade, dividido em uma interface e uma implementação, assim como os repositórios:

  • capabilities/<funcionalidade>/<Funcionalidade>Controller.java — interface; declara rotas, verbos HTTP, tipos de requisição/resposta, anotações de documentação da API
  • capabilities/<funcionalidade>/<Funcionalidade>ControllerImpl.java — implementação; carrega a anotação do framework (@RestController) e delega cada rota para o serviço da sub-funcionalidade correspondente

Compartilhado entre todas as sub-funcionalidades daquela funcionalidade. Consulte Controllers.

  • Depende de: db (apenas através de interfaces *Repo — consulte Regra de uso do DB), client
  • Nunca depende de: starter
  • Não contém classe main, nem @SpringBootApplication
  • Possui: controladores HTTP, DTOs de requisição/resposta da API, mapeadores, serviços

db/ — persistência

Organizado uma pasta por tabela. Cada pasta de tabela tem exatamente três sub-pastas:

db/<nomeDaTabela>/
├── model/
│   ├── <NomeDaEntidade>Db.java          # entidade JPA principal
│   └── <Outro>Db.java                   # sub-modelos (tipos incorporados, entidades filhas, projeções)
├── dao/
│   └── <NomeDaEntidade>Dao.java         # acesso a dados de baixo nível (Criteria/JPQL/consultas nativas)
└── repository/
    ├── <NomeDaEntidade>Repo.java        # interface PÚBLICA — o único tipo que capabilities vê
    └── impl/
        └── <NomeDaEntidade>RepoImpl.java # compõe Spring Data + Dao por trás da interface

Convenção de nomenclatura

CamadaSufixoExemplo
Entidade JPADbTestcaseDb
Interface do repositórioRepoTestcaseRepo
Implementação do repositórioRepoImplTestcaseRepoImpl
Auxiliar de acesso a dadosDaoTestcaseDao

Regra de uso do DB

Capabilities devem depender apenas de interfaces *Repo. *RepoImpl e *Dao são internos — nunca injetados ou referenciados de fora do pacote db/<nomeDaTabela>/. Isso mantém a camada de persistência intercambiável e os testes de capabilities focados no contrato.

  • Depende de: nada neste serviço
  • Expõe: interfaces *Repo e tipos de modelo *Db
  • Nunca contém: lógica de negócio, transformação para DTOs de API

client/ — integrações de saída

Contém o código que chama outros serviços dos quais este serviço depende. Principalmente uma sub-pasta por microsserviço dependente em nossa própria plataforma (paymentService, userService, executionService, …). Wrappers de SDKs de terceiros ou produtores de mensagem também podem ficar aqui, mas o padrão dominante — e a razão pela qual este módulo existe — são os clientes de microsserviços dependentes.

client/<nomeDoMicrosserviço>/
├── <Microsserviço>Client.java       # interface — do que capabilities depende
├── impl/
│   └── <Microsserviço>ClientImpl.java   # impl: chamada HTTP/gRPC/SDK, retentativas, timeouts
├── dto/                            # formas de requisição/resposta para a API DESTE microsserviço
│   ├── request/
│   └── response/
└── mapper/                         # opcional: traduz entre as formas deles e as nossas

Regras

  • Uma sub-pasta por microsserviço dependente, nomeada de acordo com o serviço. Não agrupe vários microsserviços em um único pacote, mesmo que sejam "relacionados".

  • Capabilities dependem apenas de interfaces *Client. *ClientImpl é interno — mesma regra de *Repo / *RepoImpl.

  • A interface do cliente expõe a API do microsserviço em termos de seus próprios DTOs; capabilities podem adaptá-los ainda mais, se necessário.

  • Preocupações transversais (retentativas, circuit breaking, logging de requisição/resposta, cabeçalhos de autenticação) pertencem a impl/ — capabilities nunca devem vê-los.

  • Depende de: nada neste serviço

  • Expõe: interfaces *Client e seus DTOs de requisição/resposta

  • Nunca contém: lógica de negócio, orquestração de capabilities

starter/ — raiz de composição

O único módulo que executa. Contém @SpringBootApplication, manipuladores globais de exceção REST, configuração de segurança, filtros e application.yml. Sem controladores — eles ficam em capabilities/.

  • Depende de: capabilities (e transitivamente db, client)
  • Possui: o artefato implantável, configuração web transversal (segurança, CORS, tratamento de erros)
  • Nunca possui: controladores de funcionalidades ou DTOs de funcionalidades

Regra de dependência

starter → capabilities → { db, client }

As setas vão em uma direção. db e client nunca importam de capabilities ou starter. capabilities nunca importa de starter.

Controllers

Há um controlador por funcionalidade, não por sub-funcionalidade, e ele fica na raiz do pacote da funcionalidade dentro de capabilities/. Ele é dividido em dois arquivos, espelhando a divisão *Repo / *RepoImpl:

  • capabilities/<funcionalidade>/<Funcionalidade>Controller.java — interface. Declara as rotas, verbos HTTP, parâmetros de caminho/consulta, tipos de DTO de requisição/resposta e anotações de documentação da API (OpenAPI/Swagger). Qualquer pessoa lendo a interface deve ver todo o contrato HTTP.
  • capabilities/<funcionalidade>/<Funcionalidade>ControllerImpl.java — implementação. Carrega a anotação do framework (@RestController no Spring), é o alvo de injeção para os serviços de sub-funcionalidade e contém um método curto por rota que chama o service/ correspondente e retorna o resultado. Nenhuma lógica além de delegação.

Por que a divisão? A interface é o contrato da API — estável, documentado, testável isoladamente. O impl é a fiação — pode mudar sem alterar o contrato. Mesmo raciocínio de *Repo vs *RepoImpl.

O controlador delega cada rota HTTP para o serviço da sub-funcionalidade correspondente em capabilities/<funcionalidade>/<sub-funcionalidade>/service/. Os DTOs de requisição/resposta são de propriedade da sub-funcionalidade (capabilities/<funcionalidade>/<sub-funcionalidade>/dto/); o controlador é apenas um adaptador HTTP fino.

Por que dentro de capabilities/ e não em starter/? O controlador, seu serviço e seus DTOs mudam juntos quando uma API é alterada — mantê-los em um módulo preserva a promessa de "pacote por funcionalidade" de "uma funcionalidade, um lugar". starter/ é reservado para fiação transversal (segurança, manipuladores de erro, ponto de entrada da aplicação), não para código web por funcionalidade.

Exemplo prático: uma funcionalidade testcase

A árvore abaixo usa extensões .java para ilustração. Substitua a extensão do arquivo e os idiomas do framework da sua linguagem — Kotlin .kt, pacotes Go, módulos Python, etc. — mas mantenha a estrutura de pastas e os sufixos de nome idênticos.

testcase-service/
├── capabilities/
│   └── com/example/testcase/capabilities/
│       └── testcase/                          # funcionalidade principal
│           ├── TestcaseController.java        # interface — contrato HTTP, declarações de rota, docs da API
│           ├── TestcaseControllerImpl.java    # impl — @RestController, delega para serviços de sub-funcionalidade
│           ├── getById/                       # sub-funcionalidade = uma API
│           │   ├── service/
│           │   │   └── GetTestcaseByIdService.java
│           │   ├── dto/
│           │   │   └── response/
│           │   │       └── GetTestcaseByIdResponse.java
│           │   └── mapper/
│           │       └── GetTestcaseByIdMapper.java     # TestcaseDb → resposta
│           ├── create/
│           │   ├── service/
│           │   │   └── CreateTestcaseService.java
│           │   ├── dto/
│           │   │   ├── request/
│           │   │   │   └── CreateTestcaseRequest.java
│           │   │   └── response/
│           │   │       └── CreateTestcaseResponse.java
│           │   └── mapper/
│           │       └── CreateTestcaseMapper.java      # requisição → TestcaseDb, TestcaseDb → resposta
│           ├── updateById/
│           │   ├── service/
│           │   │   └── UpdateTestcaseByIdService.java
│           │   ├── dto/
│           │   │   ├── request/
│           │   │   │   └── UpdateTestcaseByIdRequest.java
│           │   │   └── response/
│           │   │       └── UpdateTestcaseByIdResponse.java
│           │   └── mapper/
│           │       └── UpdateTestcaseByIdMapper.java
│           └── getByQuery/
│               ├── service/
│               │   └── GetTestcaseByQueryService.java
│               ├── dto/
│               │   ├── request/
│               │   │   └── GetTestcaseByQueryRequest.java
│               │   └── response/
│               │       └── GetTestcaseByQueryResponse.java
│               └── mapper/
│                   └── GetTestcaseByQueryMapper.java
│
├── db/                                        # uma pasta por tabela
│   └── com/example/testcase/db/
│       └── testcase/                          # nome da tabela
│           ├── model/
│           │   ├── TestcaseDb.java            # entidade JPA — principal
│           │   └── TestcaseStepDb.java        # sub-modelo (filho/incorporado)
│           ├── dao/
│           │   └── TestcaseDao.java           # auxiliar de consulta/critério, uso privado ao pacote
│           └── repository/
│               ├── TestcaseRepo.java          # interface — capabilities depende DESTA
│               └── impl/
│                   └── TestcaseRepoImpl.java  # compõe Spring Data + TestcaseDao
│
├── client/                                   # uma pasta por microsserviço dependente
│   └── com/example/testcase/client/
│       ├── executionService/                 # microsserviço dependente que chamamos
│       │   ├── ExecutionServiceClient.java        # interface
│       │   ├── impl/
│       │   │   └── ExecutionServiceClientImpl.java  # chamada HTTP/gRPC, retentativas, timeouts
│       │   ├── dto/
│       │   │   ├── request/
│       │   │   │   └── RunTestcaseRequest.java
│       │   │   └── response/
│       │   │       └── RunTestcaseResponse.java
│       │   └── mapper/
│       │       └── ExecutionServiceMapper.java
│       └── userService/                      # outro microsserviço dependente
│           ├── UserServiceClient.java
│           └── impl/
│               └── UserServiceClientImpl.java
│
└── starter/                                  # apenas fiação transversal — sem controladores
    └── com/example/testcase/
        ├── TestcaseServiceApplication.java
        ├── config/
        │   ├── SecurityConfig.java
        │   └── WebConfig.java
        ├── error/
        │   └── GlobalExceptionHandler.java   # @ControllerAdvice — transversal, não por funcionalidade
        └── resources/application.yml

Regras práticas

  • Adicionando uma nova API a uma funcionalidade existente? Crie um novo pacote de sub-funcionalidade dentro de capabilities/<funcionalidade>/ com seu próprio service/, dto/{request,response}/ e mapper/, depois adicione a declaração da rota em <Funcionalidade>Controller.java (interface) e o método delegador em <Funcionalidade>ControllerImpl.java. Não crie um segundo controlador.
  • Adicionando uma funcionalidade totalmente nova? Crie o pacote da funcionalidade em capabilities/ (controlador na raiz, sub-funcionalidades por API) e a pasta de tabela correspondente em db/<nomeDaTabela>/ com model/, dao/, repository/{impl/}. Adicione um cliente apenas se você chamar um sistema externo. Não mexa em starter/.
  • Sempre injete *Repo, nunca *RepoImpl ou *Dao. Capabilities dependem apenas da interface. *RepoImpl e *Dao são internos a db/<nomeDaTabela>/ — eles existem para permitir trocar a estratégia de consulta ou o armazenamento subjacente sem alterar o código de capabilities.
  • Sempre injete *Client, nunca *ClientImpl. Mesma regra para chamadas de microsserviços dependentes. Capabilities dependem da interface; o transporte (HTTP, gRPC, SDK) fica por trás de impl/.
  • Uma pasta por microsserviço dependente dentro de client/. Nomeie a pasta de acordo com o microsserviço. Não agrupe "todos os serviços internos" em um pacote.
  • A granularidade de sub-funcionalidade para em capabilities/. db/ é organizado por tabela; client/ por sistema externo. Dividir qualquer um por API espalharia código coeso.
  • DTOs de requisição/resposta e seu mapeador são por sub-funcionalidade, nunca compartilhados. Se create e updateById parecem semelhantes hoje, mantenha dois DTOs e dois mapeadores. Compartilhar acopla duas APIs que divergirão independentemente.
  • Mapeadores convertem entidade ↔ DTO diretamente. Sem camada de modelo de domínio intermediária. <SubFuncionalidade>Mapper converte *Db ↔ DTOs de requisição/resposta daquela sub-funcionalidade.
  • Três sub-funcionalidades têm código semelhante? Resista à tentação de extrair um serviço ou mapeador compartilhado até ter o mesmo padrão em quatro ou mais lugares.
  • Reutilização entre funcionalidades? Coloque utilitários compartilhados em um pacote common/ dentro de capabilities/, mas apenas quando o padrão aparecer em pelo menos três funcionalidades. Evite abstr

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Habilidades Relacionadas