Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/agents/feature-scaffolder.agent.md
Original file line number Diff line number Diff line change
@@ -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.
96 changes: 96 additions & 0 deletions .github/instructions/feature-scaffolding.instructions.md
Original file line number Diff line number Diff line change
@@ -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.
121 changes: 121 additions & 0 deletions .github/skills/feature-scaffolding/SKILL.md
Original file line number Diff line number Diff line change
@@ -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: <nome-da-feature> [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).