Persistence
Persistence guide
The library traces commands and fires events — but it does not include a persistence layer. You provide the entity, the repository, and the event listener that connects them.
1. Entity
Create a JPA or Micronaut Data entity that implements CommandLogData.
The static factory method from(params, request, response) is the conventional way to construct it:
@Entity
@Table(name = "command_log")
@Data
public class MyCommandLogEntity implements CommandLogData {
@Id @GeneratedValue(strategy = IDENTITY)
private Long id;
private String tenantId;
private String userId;
@NonNull private UUID cmdUuid;
private String cmdSourceRef;
private String clientRef;
@Enumerated(STRING) private CommandImportance importance;
@Enumerated(STRING) private CommandRetention retention;
@Enumerated(STRING) private CommandState state;
private String cmdClass;
@Enumerated(STRING) private HttpMethod httpMethod;
@Enumerated(STRING) private HttpStatus httpStatus;
private Integer httpStatusCode;
private String problemCode;
@JdbcTypeCode(SqlTypes.JSON) private Problem problem;
@JdbcTypeCode(SqlTypes.JSON) private Map<String, Object> cmdBody;
@CreationTimestamp private Instant createdAt;
private Instant startedAt;
private Instant endedAt;
private Long durationInMillis;
private Boolean dryRun;
@JdbcTypeCode(SqlTypes.JSON) private Map<String, Object> attributes;
public static MyCommandLogEntity from(CommandTracingParams params,
CommandRequest req,
CommandResponse res) {
var entity = new MyCommandLogEntity();
entity.setTenantId(req.tenantId());
entity.setUserId(req.userId());
entity.setCmdUuid(req.command().getCmdUuid());
entity.setCmdSourceRef(req.command().getCmdSourceRef());
entity.setClientRef(req.clientRef());
entity.setImportance(params.importance());
entity.setRetention(CommandRetention.fromImportance(params.importance()));
entity.setState(res.decideState());
entity.setCmdClass(req.cmdClass());
entity.setHttpMethod(req.httpMethod());
entity.setHttpStatus(res.httpStatus());
entity.setHttpStatusCode(res.httpStatusCode());
entity.setProblemCode(res.problemCode());
entity.setProblem(res.problem());
entity.setCmdBody(req.cmdBody());
entity.setStartedAt(req.startedAt());
entity.setEndedAt(res.endedAt());
entity.setDurationInMillis(
Duration.between(req.startedAt(), res.endedAt()).toMillis());
entity.setDryRun(req.dryRun());
entity.setAttributes(req.attributes());
return entity;
}
}
2. Publisher
The CommandTracingEventPublisher enriches the minimal request with tenant ID, user ID, and optionally the command body, then fires an async event:
@Singleton
public class MyCommandTracingEventPublisher implements CommandTracingEventPublisher {
private final ApplicationEventPublisher<CommandTracingEvent> eventPublisher;
@Override
public void publishEvent(CommandTracingParams params,
CommandRequest minimal,
CommandResponse cmdResponse) {
// resolveTenantId and resolveUserId are your application-specific methods
// that extract identity from the command or the security context.
var cmdRequest = CommandRequest.of(
minimal,
resolveTenantId(minimal.command()),
resolveUserId(),
params.options().contains(ExcludeCmdBody)
? null
: cmdToMap(minimal.command())
);
eventPublisher.publishEventAsync(
new CommandTracingEvent(
MyCommandLogEntity.from(params, cmdRequest, cmdResponse),
params
)
);
}
}
Publishing asynchronously ensures the HTTP response is not delayed by persistence.
3. Event listener
A Micronaut @EventListener picks up the event and saves the entity:
@Singleton
public class MyCommandLogEventListener {
private final MyCommandLogRepository repository;
@EventListener
@Async
public void onCommandTraced(CommandTracingEvent event) {
if (event.data() instanceof MyCommandLogEntity entity) {
repository.save(entity);
}
}
}