Implementing the outbox pattern in Spring
The Outbox Pattern is an old pattern, that usually has been used for years to solve similar problems that were introduced with Microservices.
It is when you need to save/update some information in the database and in the same “atomic” transaction want to communicate with some external service, such as a Message Queue.
In this case, you might choose to use 2PC, but It won’t work for you if you need to communicate with some external API instead of sending to a message queue or an external service.
Multiple solutions are available, among them is using the Outbox pattern by inserting the data you want to communicate with the other system in the database (hence we have a real atomic transaction, the first save/update and the second save), and then there’s another background process that picks the saved record and tries to send it for you.
In this article, I will showcase a simple Java/Spring library that does this automatically using AOP (Aspect-oriented Programming).
Here’s how to use the outbox library:
First, You will need to annotate the call to the external system using the @Outbox
annotation.
@Slf4j
@Service
@RequiredArgsConstructor
public class UseService {
private final RestTemplate restTemplate;
private final UserRepository userRepository;
public void saveUser(UserEntity user) {
userRepository.save(user);
}
@Outbox
public void syncUser(UserEntity user) {
Map<?, ?> map = restTemplate.postForObject("https://gorest.co.in/public/v2/users", user, Map.class);
log.info("response from api: {}", map);
}
}
Second, You just need to call it as if you call a regular method:
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class UserController {
private final UseService useService;
@Transactional
@PostMapping("/users")
public void send(@RequestBody UserEntity user) {
useService.saveUser(user);
useService.syncUser(user);
}
}
Under the hood, the syncUser
method call will be serialized to the database, and another background job (using spring-scheduler) will pick that method invocation and invoke it for you.
If you are curious about how this works, you can check the source code of the library at Github, it is Open Source!
Don’t forgive to leave a star! 😉