Defining Problem Codes
A ProblemCode is an application-level error code that carries:
-
a stable string identifier (the
codefield on the response, e.g.PET-2000), -
a default HTTP status,
-
a human-readable detail, and
-
(optionally) a default
ProblemLevel.
Implementing it as an enum keeps every error code declared in one place and lets the compiler enforce that callers reference a known code.
package no.conta.problem.micronaut.example;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.http.HttpStatus;
import no.conta.problem.ProblemCode;
public enum PetProblemCode implements ProblemCode {
PetNotFound(2000, HttpStatus.NOT_FOUND, "Not possible to find pet");
private final String code;
private final Integer status;
private final String detail;
PetProblemCode(int code, HttpStatus status, String detail) {
this.code = "PET-" + code;
this.status = status.getCode();
this.detail = detail;
}
@Override
public String getCode() {
return code;
}
@Nullable
@Override
public Integer getStatus() {
return status;
}
@Override
public String getDetail() {
return detail;
}
}
The "PET-" prefix is a convention — pick a short, app-specific prefix so codes from different services are distinguishable in logs and dashboards.
Looking up by code
ProblemCode exposes a registry that maps the string code back to the enum constant:
ProblemCode pc = ProblemCode.fromValue("PET-2000", PetProblemCode.class);
Codes have to be registered with ProblemCode.add(…) before lookups can find them.
The convention is a static initializer on the enum:
public enum PetProblemCode implements ProblemCode {
PetNotFound(2000, HttpStatus.NOT_FOUND, "Not possible to find pet");
static {
Arrays.stream(values()).forEach(ProblemCode::add);
}
// ...
}
The registry warns on duplicates, so a clash between two enums using the same code surfaces in the logs at startup.