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:
AbstractCrudControllerAbstractBaseCrudServiceNo minimo:
resourcePathresourceKeyExemplo:
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
resourceKeyAo gerar um recurso novo, nao trate resourceKey como
espelho cosmetico do path.
Use esta regra:
resourcePath identifica a URL do recursoresourceKey identifica a semantica canonica do
recursoExemplo:
resourcePath = /api/human-resources/employeesresourceKey = human-resources.employeesO starter usa resourceKey para:
/schemas/surfaces/schemas/actionscapabilitiesSe o recurso continua semanticamente o mesmo, prefira manter o mesmo
resourceKey mesmo quando a URL precisar evoluir.
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
Use sempre:
{Resource}ResponseDTO para leituraCreate{Resource}DTO para POSTUpdate{Resource}DTO para PUT base{Resource}FilterDTO para query/filterNo 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:
Update{Resource}{Intent}DTOPATCH /{id}/{intent}@ResourceIntent@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:
value = ApiPaths.HumanResources.EMPLOYEES define o path
publicado pelo controllerresourceKey = "human-resources.employees" define a
chave semantica que a plataforma usa em discovery e capabilities@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;
}
}@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();
}
}Quando o recurso for apenas leitura:
AbstractReadOnlyResourceControllerAbstractReadOnlyResourceService@ResourceIntentUse 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) {
...
}@UiSurfaceUse 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"
)@WorkflowActionUse apenas para comando de negocio explicito.
Exemplo:
@PostMapping("/{id}/actions/approve")
@WorkflowAction(id = "approve", title = "Aprovar", scope = ActionScope.ITEM)Para um recurso mutavel no core atual, o baseline esperado inclui:
GET /{resource}/allGET /{resource}/{id}POST /{resource}PUT /{resource}/{id}POST /{resource}/filterPOST /{resource}/filter/cursorPOST /{resource}/locate/schemas/filtered/schemas/catalog/schemas/surfaces/schemas/actionsGET /{resource}/capabilitiesGET /{resource}/{id}/capabilitiesV1/V2405 herdadoAntes de concluir:
@ApiResource(value = ..., resourceKey = ...)resourceKey representa a semantica do recurso, e nao
apenas a URL atual@Valid funciona de verdade/schemas/filtered resolve request e response/schemas/surfaces e /schemas/actions so
expoem referencias canonicasGET /{resource}/capabilities agrega sem redefinir
contratodocs/guides/GUIA-01-AI-BACKEND-APLICACAO-NOVA.mddocs/guides/GUIA-04-QUANDO-USAR-RESOURCE-SURFACE-ACTION-CAPABILITY.mddocs/technical/RESOURCE-ORIENTED-PILOT-IN-SRC-TEST.md