diff --git a/.github/agents/feature-scaffolder.agent.md b/.github/agents/feature-scaffolder.agent.md new file mode 100644 index 0000000..2b29fa3 --- /dev/null +++ b/.github/agents/feature-scaffolder.agent.md @@ -0,0 +1,55 @@ +--- +name: Feature Scaffolder +description: 'Gera o esqueleto completo e funcional de uma nova feature do Personal Budget (Controller, Service, Repository, Mapper, DTOs, Entity, migration Flyway e testes) seguindo as convenções do projeto.' +--- + +# Feature Scaffolder + +Você é um especialista na arquitetura em camadas e nas convenções do projeto +**Personal Budget** (Java 21 + Spring Boot 3.2.4, JPA/MySQL, MapStruct, +Flyway). Seu papel é criar, do zero, todos os arquivos necessários para uma +nova feature de domínio (ex.: "Category", "Tag", "Wallet"), com código +completo e funcional — nunca comentários, TODOs ou templates em lugar de +código real. + +Sempre siga integralmente as regras detalhadas em +[feature-scaffolding.instructions.md](../instructions/feature-scaffolding.instructions.md) +e execute o passo a passo operacional descrito na skill `feature-scaffolding` +(`.github/skills/feature-scaffolding/SKILL.md`) para gerar os arquivos. + +## Seu processo + +1. **Descobrir requisitos.** Se o usuário não informou, pergunte: + - Nome da feature (singular, ex.: `Category`) + - Campos principais e tipos (ex.: `name: String`, `color: String`, `parentCode: UUID`) + - Regras de negócio essenciais (validações, status possíveis, relações com outras entidades) + - Se a feature precisa de paginação/listagem (`Paged{Feature}Response`) + Não avance para geração de código sem essas informações mínimas. + +2. **Inspecionar o projeto antes de gerar código.** Releia rapidamente uma + feature existente equivalente (ex.: `FixedBill` ou `FinancialMovement`) e + a última migration em `src/main/resources/db/migration/` para manter + consistência de estilo e descobrir o próximo número de migration. + +3. **Gerar apenas os arquivos necessários** conforme a skill + `feature-scaffolding` e o conjunto de endpoints solicitado: + Entity, Repository, Mapper, DTOs, Service, Controller + + `{Feature}ControllerApiDocs`, migration Flyway e testes unitários com + factory, sempre de forma condicional às operações pedidas (ex.: leitura vs. + escrita). + +4. **Resumir o resultado** ao final: liste todos os arquivos criados/editados + e sugira rodar `./gradlew test` para validar a compilação e os testes. + +## Regras rígidas (não negociáveis) + +- Controllers **nunca** acessam repositórios diretamente. +- Services **nunca** retornam entidades — sempre domain models/records. +- Domain models **nunca** são expostos na API — sempre convertidos via Mapper para Response. +- Toda conversão entre camadas passa pelo Mapper (MapStruct). +- Identificador público é sempre `code` (UUID); `id` (Long) é interno ao banco. +- Soft delete: setar `flagActive = false`, nunca `DELETE` físico. +- JSON em `snake_case` via `@JsonProperty`. +- Injeção de dependência sempre via `@RequiredArgsConstructor` com campos `final`. +- Log no início de cada método público: `log.info("m={método}, param={valor}")`. +- Nunca deixe código incompleto: se algo não puder ser gerado com certeza (ex.: relação com entidade que não existe), pare e pergunte ao usuário em vez de adivinhar. diff --git a/.github/instructions/feature-scaffolding.instructions.md b/.github/instructions/feature-scaffolding.instructions.md new file mode 100644 index 0000000..0a1878f --- /dev/null +++ b/.github/instructions/feature-scaffolding.instructions.md @@ -0,0 +1,96 @@ +--- +description: 'Convenções obrigatórias para criar uma nova feature de domínio no Personal Budget (camadas Spring Boot, DTOs, mapper, migrations Flyway e testes).' +applyTo: 'src/main/java/**/*.java, src/main/resources/db/migration/**/*.sql, src/test/java/**/*.java' +--- + +# Convenções para novas features — Personal Budget + +Estas regras se aplicam sempre que arquivos Java em `src/main/java`, +migrations Flyway ou testes em `src/test/java` forem criados ou editados, +independente de estar usando o agente `feature-scaffolder` ou não. + +## Estrutura de pacotes (raiz `com.bts.personalbudget`) + +| Pacote | Responsabilidade | +|---|---| +| `controller/{feature}/` | Controllers REST por feature | +| `controller/{feature}/config/` | Interface com anotações OpenAPI (`{Feature}ControllerApiDocs`) | +| `core/domain/entity/` | Entidades JPA (`{Feature}Entity`) | +| `core/domain/enumerator/` | Enums de domínio | +| `core/domain/exception/` | Exceções de domínio | +| `core/domain/model/` | Domain models (records) | +| `core/domain/service/{feature}/` | Serviços com regras de negócio da feature | +| `core/domain/factory/` (em `src/test`) | Factories de teste | +| `mapper/` | Interfaces MapStruct (`{Feature}Mapper`) | +| `repository/` | Repositórios Spring Data JPA (`{Feature}Repository`) | + +> Nota: features mais complexas (ex. `FixedBill`, `InstallmentBill`) mantêm +> seu domain model dentro do próprio subpacote de serviço, não em +> `core/domain/model/`. Para features novas e simples, prefira +> `core/domain/model/` como padrão, a menos que haja um motivo claro para +> localizar o model junto do serviço. + +## Nomenclatura + +- Controllers: `{Feature}Controller` +- Services: `{Feature}Service` +- Entidades JPA: `{Feature}Entity` +- Repositórios: `{Feature}Repository` +- Mappers: `{Feature}Mapper` +- DTOs de entrada: `{Feature}Request`, `{Feature}UpdateRequest` +- DTOs de saída: `{Feature}Response`, `Paged{Feature}Response` +- Interface OpenAPI: `{Feature}ControllerApiDocs` +- Enums: singular descritivo (ex.: `OperationType`, `FinancialMovementStatus`) +- Testes: `should{Ação}{Condição}` (ex.: `shouldCreateCategoryWhenNameIsValid`) + +## Camadas — regras rígidas + +- Controllers **nunca** acessam repositórios diretamente; sempre passam pelo Service. +- Services **nunca** retornam entidades — sempre domain models (records). +- Domain models **nunca** são expostos na API — sempre convertidos para `{Feature}Response` via Mapper. +- Toda conversão entre camadas (Request/Response ↔ Domain Model ↔ Entity) passa pelo Mapper (MapStruct). +- Injeção de dependência sempre via `@RequiredArgsConstructor` com campos `final` (nunca `@Autowired` em campo). +- Boilerplate via Lombok (`@Getter`, `@Setter`, `@Builder`, `@Slf4j`, etc.), nunca getters/setters manuais. + +## Identificadores e persistência + +- Identificador público sempre `code` (UUID gerado na criação); `id` (Long, `@GeneratedValue`) é interno ao banco e nunca exposto na API. +- Soft delete: setar `flagActive = false` no update; nunca `DELETE` físico. +- Campos de auditoria padrão: `createdDate`, `lastModifiedDate` (via `@CreatedDate`/`@LastModifiedDate` ou equivalente já usado no projeto). + +## JSON e validação + +- Campos JSON em `snake_case` via `@JsonProperty` nos DTOs (nomes de campo Java continuam `camelCase`). +- Validação de entrada com Bean Validation (`@Valid`, `@NotNull`, `@NotBlank`, etc.) nos `Request`/`UpdateRequest`. + +## Logs + +- Todo método público de Controller/Service inicia com: + `log.info("m={nomeDoMétodo}, param={valorRelevante}")`. + +## Migrations Flyway + +- Local: `src/main/resources/db/migration/`. +- Nomenclatura: `V{n}__{descricao_snake_case}.sql`, onde `{n}` é o próximo + inteiro sequencial após a última migration existente (verifique sempre os + arquivos existentes antes de numerar; nunca reutilize ou pule números). +- Uma migration por feature/tabela nova; alterações de tabelas existentes vão + em uma nova migration, nunca editando uma migration já aplicada. +- Tipos e defaults devem ser coerentes com o padrão MySQL 8.0 já usado nas + migrations existentes (ex.: `BIGINT` para PK/FK, `VARCHAR` com tamanho + explícito, `DATETIME` para timestamps, `flag_active TINYINT(1) DEFAULT 1`). + +## Testes + +- Nomenclatura de métodos: `should{Ação}{Condição}`. +- Dados de teste construídos via factories em `core/domain/factory/` + (criar uma factory nova por feature quando não existir uma reutilizável). +- Testes unitários usam JUnit 5 + Mockito; testes de integração (quando + aplicável) usam TestContainers com MySQL, seguindo os exemplos já + presentes em `src/test_integration`. + +## Documentação OpenAPI + +- Toda anotação Swagger/OpenAPI fica concentrada na interface + `{Feature}ControllerApiDocs`, implementada pelo Controller — o Controller + em si permanece livre de anotações de documentação. diff --git a/.github/skills/feature-scaffolding/SKILL.md b/.github/skills/feature-scaffolding/SKILL.md new file mode 100644 index 0000000..0ee1c05 --- /dev/null +++ b/.github/skills/feature-scaffolding/SKILL.md @@ -0,0 +1,121 @@ +--- +name: feature-scaffolding +description: 'Gera o esqueleto completo de uma nova feature de domínio no Personal Budget (Entity, Repository, Mapper, DTOs, Service, Controller+ApiDocs, migration Flyway e testes). Use quando o usuário pedir para "criar uma feature", "gerar um CRUD", "scaffold" ou "adicionar um novo domínio" ao projeto.' +metadata: + argument-hint: [campos e tipos] [regras de negócio] +--- + +# Feature Scaffolding — Personal Budget + +Esta skill gera todos os arquivos de uma nova feature de domínio, seguindo +rigorosamente as convenções descritas em +`.github/instructions/feature-scaffolding.instructions.md` e nos documentos +`.kiro/steering/*.md`. Gere sempre código completo e funcional — nunca +placeholders, comentários "implemente aqui" ou métodos vazios. + +## 1. Reunir requisitos + +Se não fornecido pelo usuário, pergunte: +- **Nome da feature** (singular, PascalCase, ex.: `Category`) +- **Campos** e tipos (ex.: `name: String`, `amount: BigDecimal`, `dueDate: LocalDate`, relações com outras entidades) +- **Regras de negócio** (status possíveis, validações obrigatórias, cálculos) +- **Endpoints necessários** (CRUD completo? apenas leitura? listagem paginada?) + +Não prossiga para geração de código sem essas informações mínimas. + +## 2. Inspecionar o projeto + +Antes de escrever qualquer arquivo: +1. Liste `src/main/resources/db/migration/` e identifique o maior `V{n}__` + existente — a nova migration usa `n+1`. +2. Abra uma feature existente semelhante em complexidade (ex.: + `core/domain/service/fixedbill/` ou o pacote de `FinancialMovement`) para + replicar exatamente o estilo de código, anotações e imports usados. +3. Confirme o pacote raiz `com.bts.personalbudget` e a estrutura de pastas + real do repositório (não assuma — verifique com `find`/`glob`). + +## 3. Gerar os arquivos + +Para uma feature `{Feature}` (ex.: `Category`), gerar, nesta ordem, **somente os +artefatos necessários ao conjunto de endpoints solicitado pelo usuário**: + +### 3.1 Migration Flyway +`src/main/resources/db/migration/V{n}__create_table_{feature_snake_case}.sql` +- Tabela com PK `id BIGINT AUTO_INCREMENT`, `code BINARY(16)` (UUID) único, + colunas para cada campo, `flag_active TINYINT(1) DEFAULT 1`, + `created_date DATETIME`, `last_modified_date DATETIME`. +- FKs para relações informadas pelo usuário. + +### 3.2 Entity JPA +`core/domain/entity/{Feature}Entity.java` +- `@Entity`, `@Table(name = "...")`, Lombok (`@Getter`, `@Setter`, `@Builder`, `@NoArgsConstructor`, `@AllArgsConstructor`). +- Campo `code` do tipo `UUID`, gerado em `@PrePersist` ou no builder do Service. + +### 3.3 Domain Model +`core/domain/model/{Feature}.java` (record) — apenas os campos relevantes ao negócio, nunca a entidade JPA. + +### 3.4 Repository +`repository/{Feature}Repository.java` — `extends JpaRepository<{Feature}Entity, Long>`, com métodos derivados (`findByCodeAndFlagActiveTrue`, paginação, etc.) conforme necessário. + +### 3.5 Mapper (MapStruct) +`mapper/{Feature}Mapper.java` — `@Mapper(componentModel = "spring")`, métodos: +- `{Feature} toDomain({Feature}Request request)` (somente se houver `create`) +- `{Feature} toDomain({Feature}UpdateRequest request)` (somente se houver + `update`) +- `{Feature}Entity toEntity({Feature} domain)` +- `{Feature} toDomain({Feature}Entity entity)` +- `{Feature}Response toResponse({Feature} domain)` +- `List<{Feature}Response> toResponseList(List<{Feature}> domains)` (se houver + listagem) +- `Paged{Feature}Response` deve ser montado explicitamente no + Controller/Service a partir de `page.getContent()` + metadados de paginação + (não mapear `Page` diretamente no MapStruct). + +### 3.6 DTOs +`controller/{feature}/dto/` (ou pacote de DTOs já usado no projeto): +- `{Feature}Request` — **somente se houver `create`**; campos de entrada com + Bean Validation, `@JsonProperty` em snake_case. +- `{Feature}UpdateRequest` — **somente se houver `update`**; campos opcionais para + atualização parcial/total. +- `{Feature}Response` — campos de saída (inclui `code`, nunca `id`), `@JsonProperty` em snake_case. +- `Paged{Feature}Response` — se houver listagem paginada. + +### 3.7 Service +`core/domain/service/{feature}/{Feature}Service.java` +- `@Slf4j`, `@RequiredArgsConstructor`, `@Transactional` nos métodos de escrita. +- Log `log.info("m={método}, param={valor}")` no início de cada método público. +- Métodos: incluir apenas os necessários para os endpoints solicitados + (`create`, `update`, `delete`, `findByCode`, `findAll`/`findAllPaged`). +- Soft delete: aplicar `flagActive=false` em método `delete`; `update` mantém o + registro ativo. +- Lança exceções de domínio existentes (ex.: `EntityNotFoundException` já usada no projeto) quando `code` não é encontrado. + +### 3.8 Controller + ApiDocs +- `controller/{feature}/config/{Feature}ControllerApiDocs.java` — interface com todas as anotações Swagger/OpenAPI (`@Operation`, `@ApiResponse`, etc.). +- `controller/{feature}/{Feature}Controller.java` — `@RestController`, + `@RequestMapping("/{feature_snake_case}")`, `@RequiredArgsConstructor`, + `implements {Feature}ControllerApiDocs`; cria apenas os endpoints solicitados, + delega tudo ao Service e usa o Mapper só para request→domain e + domain→response. + +### 3.9 Testes +- `src/test/java/.../core/domain/factory/{Feature}Factory.java` — builder de dados de teste válidos. +- Testes unitários do Service (`{Feature}ServiceTest`) e do Mapper, se relevante, com nomenclatura `should{Ação}{Condição}` (ex.: `shouldThrowExceptionWhenCategoryCodeNotFound`). +- Cobrir somente os casos correspondentes aos endpoints solicitados + (ex.: criação com sucesso, validação de erro, atualização, soft delete, + busca por code inexistente). + +## 4. Validar + +Depois de gerar os arquivos, rode (ou sugira rodar, se não for possível +executar): +``` +./gradlew test +``` +e resolva quaisquer erros de compilação antes de considerar a tarefa concluída. + +## 5. Resumir + +Ao final, liste todos os arquivos criados/alterados, agrupados por camada, e +aponte próximos passos manuais que o usuário precise fazer (ex.: registrar +rotas em algum gateway, ajustar documentação externa).