11 classic-editor-remember: classic-editor
12 date: "2021-02-05T17:59:38+00:00"
13 guid: http://juplo.de/?p=1201
16 title: 'Implementing The Outbox-Pattern With Kafka - Part 0: The example'
17 url: /implementing-the-outbox-pattern-with-kafka-part-0-the-example/
20 _This article is part of a Blog-Series_
22 Based on a [very simple example-project](/implementing-the-outbox-pattern-with-kafka-part-0-the-example/)
23 we will implemnt the [Outbox-Pattern](https://microservices.io/patterns/data/transactional-outbox.html) with [Kafka](https://kafka.apache.org/quickstart).
25 - Part 0: The Example-Project
26 - [Part 1: Writing In The Outbox-Table](/implementing-the-outbox-pattern-with-kafka-part-1-the-outbox-table/ "Jump to the explanation what has to be added, to enqueue messages in an outbox for successfully written transactions")
30 In this part, a small example-project is introduced, that features a component, which has to inform another component upon every succsessfully completed operation.
34 In this mini-series I will implement the [Outbox-Pattern](https://microservices.io/patterns/data/transactional-outbox.html)
35 as described on Chris Richardson's fabolous website [microservices.io](https://microservices.io/).
37 The pattern enables you, to send a message as part of a database transaction in a reliable way, effectively turining the writing of the data
38 to the database and the sending of the message into an **[atomic operation](https://en.wikipedia.org/wiki/Atomicity_(database_systems))**:
39 either both operations are successful or neither.
41 The pattern is well known and implementing it with [Kafka](https://kafka.apache.org/quickstart) looks like an easy straight forward job at first glance.
42 However, there are many obstacles that easily lead to an incomplete or incorrect implementation.
43 In this blog-series, we will circumnavigate these obstacles together step by step.
45 ## The Example Project
47 To illustrate our implementation, we will use a simple example-project.
48 It mimics a part of the registration process for an web application:
49 a (very!) simplistic service takes registration orders for new users.
51 - Successfull registration requests will return a 201 (Created), that carries the URI, under which the data of the newly registered user can be accessed in the `Location`-header:
53 `echo peter | http :8080/users
56 Date: Fri, 05 Feb 2021 14:44:51 GMT
57 Location: http://localhost:8080/users/peter
59 - Requests to registrate an already existing user will result in a 400 (Bad Request):
61 `echo peter | http :8080/users
65 Date: Fri, 05 Feb 2021 14:44:53 GMT
67 - Successfully registrated users can be listed:
70 Content-Type: application/json;charset=UTF-8
71 Date: Fri, 05 Feb 2021 14:53:59 GMT
72 Transfer-Encoding: chunked
75 "created": "2021-02-05T10:38:32.301",
83 ## The Messaging Use-Case
85 As our messaging use-case imagine, that there has to happen several processes after a successful registration of a new user.
86 This may be the generation of an invoice, some business analytics or any other lengthy process that is best carried out asynchronously.
87 Hence, we have to generate an event, that informs the responsible services about new registrations.
89 Obviously, these events should only be generated, if the registration is completed successfully.
90 The event must not be fired, if the registration is rejected, because a duplicate username.
92 On the other hand, the publication of the event must happen reliably, because otherwise, the new might not be charged for the services, we offer...
96 The users are stored in a database and the creation of a new user happens in a transaction.
97 A "brilliant" colleague came up with the idea, to trigger an `IncorrectResultSizeDataAccessException` to detect duplicate usernames:
99 `User user = new User(username);
100 repository.save(user);
101 // Triggers an Exception, if more than one entry is found
102 repository.findByUsername(username);
105 The query for the user by its names triggers an `IncorrectResultSizeDataAccessException`, if more than one entry is found.
106 The uncaught exception will mark the transaction for rollback, hence, canceling the requested registration.
107 The 400-response is then generated by a corresponding `ExceptionHandler`:
110 public ResponseEntity incorrectResultSizeDataAccessException(
111 IncorrectResultSizeDataAccessException e)
113 LOG.info("User already exists!");
114 return ResponseEntity.badRequest().build();
118 Please do not code this at home...
120 But his weired implementation perfectly illustrates the requirements for our messaging use-case:
121 The user is written into the database.
122 But the registration is not successfully completed until the transaction is commited.
123 If the transaction is rolled back, no message must be send, because no new user was registered.
125 ## Decoupling with Springs EventPublisher
127 In the example implementation I am using an `EventPublisher` to decouple the business logic from the implementation of the messaging.
128 The controller publishes an event, when a new user is registered:
130 `publisher.publishEvent(new UserEvent(this, usernam));
133 A listener annotated with `@TransactionalEventListener` receives the events and handles the messaging:
135 `@TransactionalEventListener
136 public void onUserEvent(UserEvent event)
138 // Sending the message happens here...
142 In non-critical use-cases, it might be sufficient to actually send the message to Kafka right here.
143 Spring ensures, that the message of the listener is only called, if the transaction completes successfully.
144 But in the case of a failure this naive implementation can loose messages.
145 If the application crashes, after the transaction has completed, but before the message could be send, the event would be lost.
147 In the following blog posts, we will step by step implement a solution based on the Outbox-Pattern, that can guarantee Exactly-Once semantics for the send messages.
149 ## May The Source Be With You!
151 The complete source code of the example-project can be cloned here:
153 - `git clone /git/demos/spring/data-jdbc`
154 - `git clone https://github.com/juplo/demos-spring-data-jdbc.git`
156 It includes a [Setup for Docker Compose](https://github.com/juplo/demos-spring-data-jdbc/blob/master/docker-compose.yml), that can be run without compiling
157 the project. And a runnable [README.sh](https://github.com/juplo/demos-spring-data-jdbc/blob/master/README.sh), that compiles and run the application and illustrates the example.