GUIA-02-AI-BACKEND-CRUD-METADATA

Guia 02 - IA Backend - Recurso Metadata-Driven no Core Atual

Objetivo

Este guia orienta a implementacao de um recurso backend metadata-driven alinhado ao core atual do praxis-metadata-starter.

O baseline correto hoje e resource-oriented. Este guia nao deve gerar:

Entrada minima para a LLM

No minimo:

  1. entidade JPA ou sua estrutura
  2. resourcePath
  3. resourceKey
  4. grupo OpenAPI
  5. pacote base

Exemplo:

Gere um recurso metadata-driven canonico.

Entrada:
- Entidade: src/main/java/com/example/hr/entity/Employee.java
- Resource path: /api/human-resources/employees
- Resource key: human-resources.employees
- Api group: human-resources
- Pacote base: com.example.hr

Semantica obrigatoria de resourceKey

Ao gerar um recurso novo, nao trate resourceKey como espelho cosmetico do path.

Use esta regra:

Exemplo:

O starter usa resourceKey para:

Se o recurso continua semanticamente o mesmo, prefira manter o mesmo resourceKey mesmo quando a URL precisar evoluir.

Arquivos minimos do recurso

src/main/java/{base-package}/
|-- dto/
|   |-- {Resource}ResponseDTO.java
|   |-- Create{Resource}DTO.java
|   |-- Update{Resource}DTO.java
|   `-- filter/
|       `-- {Resource}FilterDTO.java
|-- mapper/
|   `-- {Resource}Mapper.java
|-- repository/
|   `-- {Resource}Repository.java
|-- service/
|   `-- {Resource}Service.java
`-- controller/
    `-- {Resource}Controller.java

DTOs canonicos

Use sempre:

No baseline atual do starter, PATCH /{id} nao faz parte do core HTTP canonico. Use PATCH apenas em operacoes explicitas por intencao com @ResourceIntent.

Se existir escrita parcial por intencao:

Controller canonico

@RestController
@ApiResource(value = ApiPaths.HumanResources.EMPLOYEES, resourceKey = "human-resources.employees")
@ApiGroup("human-resources")
public class EmployeeController extends AbstractResourceController<
        EmployeeResponseDTO,
        Long,
        EmployeeFilterDTO,
        CreateEmployeeDTO,
        UpdateEmployeeDTO> {

    private final EmployeeService service;

    public EmployeeController(EmployeeService service) {
        this.service = service;
    }

    @Override
    protected EmployeeService getService() {
        return service;
    }

    @Override
    protected Long getResponseId(EmployeeResponseDTO dto) {
        return dto.getId();
    }
}

Leitura correta do exemplo:

Service canonico

@Service
public class EmployeeService extends AbstractBaseResourceService<
        Employee,
        EmployeeResponseDTO,
        Long,
        EmployeeFilterDTO,
        CreateEmployeeDTO,
        UpdateEmployeeDTO> {

    private final EmployeeMapper mapper;

    public EmployeeService(EmployeeRepository repository, EmployeeMapper mapper) {
        super(repository, Employee.class);
        this.mapper = mapper;
    }

    @Override
    protected ResourceMapper<Employee, EmployeeResponseDTO, CreateEmployeeDTO, UpdateEmployeeDTO, Long> getResourceMapper() {
        return mapper;
    }
}

Mapper canonico

@Component
public class EmployeeMapper implements ResourceMapper<
        Employee,
        EmployeeResponseDTO,
        CreateEmployeeDTO,
        UpdateEmployeeDTO,
        Long> {

    @Override
    public EmployeeResponseDTO toResponse(Employee entity) { ... }

    @Override
    public Employee newEntity(CreateEmployeeDTO dto) { ... }

    @Override
    public void applyUpdate(Employee entity, UpdateEmployeeDTO dto) { ... }

    @Override
    public Long extractId(Employee entity) {
        return entity.getId();
    }
}

Read-only canonico

Quando o recurso for apenas leitura:

Quando adicionar ResourceIntent, UiSurface e WorkflowAction

@ResourceIntent

Use quando a operacao ainda e manutencao do recurso, mas com DTO parcial e semantica propria.

Exemplo:

@PatchMapping("/{id}/profile")
@ResourceIntent(id = "employee-profile", title = "Editar perfil")
public ResponseEntity<RestApiResponse<EmployeeResponseDTO>> updateProfile(
        @PathVariable Long id,
        @Valid @RequestBody UpdateEmployeeProfileDTO dto) {
    ...
}

@UiSurface

Use quando a UX precisar descobrir semanticamente uma operacao real.

Exemplo:

@PatchMapping("/{id}/profile")
@UiSurface(
        id = "profile",
        kind = SurfaceKind.PARTIAL_FORM,
        scope = SurfaceScope.ITEM,
        title = "Editar perfil",
        intent = "profile"
)

@WorkflowAction

Use apenas para comando de negocio explicito.

Exemplo:

@PostMapping("/{id}/actions/approve")
@WorkflowAction(id = "approve", title = "Aprovar", scope = ActionScope.ITEM)

O que o recurso deve expor no runtime

Para um recurso mutavel no core atual, o baseline esperado inclui:

O que nao deve ser gerado

Checklist do recurso

Antes de concluir:

Referencias