I believe I know the answer to this but I'm looking for any holes or anything I may be missing.
This is focused on Spring and Java but could really apply to any programming stack.
Anyway, we have a typical service layer annotated with @Transactional
in Spring. For example, we could have:
EmailService
OrderService
HistoryService
Now, the user performs an action that calls a RESTful
service that does the following:
orderService.create(....); // wrapped in @Transactional
historyService.create(....); // wrapped in @Transactional
emailService.emailUser(....); // wrapped in @Transactional (also annotated with @Async)
While all of these are called from a controller (which is NOT @Transactional), if either the order service or the history service fail, I want both to be rolled back and the email service would abort.
I don't like mixing the services. I think it would be ugly to have the order service call the history service just so that both of them are in the same transaction boundaries.
My first instinct was to create a hybrid service that would wrap both services together. Something like:
@Transactional
public void orderEntryService.create(....) {
orderService.create(....); // STILL wrapped in @Transactional
historyService.create(....); // STILL wrapped in @Transactional
}
This way, my controller could be:
public String create(...) {
orderEntryService.create(...);
emailService.emailUser(...); // this is @Async
// and will never be called if previous
// orderEntryService.create fails
}
While I think this keeps my service layer cleaner, it can quickly start adding up to bulky and forgetful "aggregate" service classes. How to handle this?