Personally, I like abstraction, although I always frequently ask myself is this really a good abstraction :)
in this happy flow, we assume each stage returns an expected response (non-null), if not, an expection should be thrown out inside each stage implementation.
Now what will happen if flow failed at getScreeningMessage or ParseMessage or callScreening, how can we hanlde them in a more centralized way.
here comes custom exception, each stage try-catch block can return a custom screening exception with specific error code.
switch (ex.getErrorCode()) case GET_MESSAGE_FAILED -> log.error("get message failed..", ex), case PARSE_FAILED -> log.error("invalid message..", ex), case CALL_FAILED -> log.error("call screening api failed ..", ex), default -> log.error("unexpected error..", ex) }
should we use transaction?
previouly, I like transaction, all or nothing, I can easily say the request data are not valid, you should fix the request first; or, I can say our parter’s system are not stable, so the request failed and lost.
now I perfer stage tracking or state-machine pattern, without transaction. no data lost, and safe-reprocessing.
inside each concrete stage, if failed, we throw custom exception, inside the exception handler method, we save the dbRecord with stage specific status.