]> juplo.de Git - website/commitdiff
WIP:Blog-styl-rewrite
authorKai Moritz <kai@juplo.de>
Fri, 6 Feb 2026 17:28:06 +0000 (18:28 +0100)
committerKai Moritz <kai@juplo.de>
Fri, 6 Feb 2026 17:28:06 +0000 (18:28 +0100)
content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-0-the-example.md
content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-1-the-outbox-table.md

index 338fc418b4c851713421b45af46cc89ef2530b94..c0b1b62f8c633b920e417f5ebe5ba84a19c4552a 100644 (file)
@@ -49,23 +49,27 @@ It mimics a part of the registration process for an web application:
 a (very!) simplistic service takes registration orders for new users.
 
 - 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:
+  ```shell
+  $ echo peter | http :8080/users
 
-`echo peter | http :8080/users
   HTTP/1.1 201
   Content-Length: 0
   Date: Fri, 05 Feb 2021 14:44:51 GMT
   Location: http://localhost:8080/users/peter
-  `
+  ```
 - Requests to registrate an already existing user will result in a 400 (Bad Request):
+  ```
+  $ echo peter | http :8080/users
 
-`echo peter | http :8080/users
   HTTP/1.1 400
   Connection: close
   Content-Length: 0
   Date: Fri, 05 Feb 2021 14:44:53 GMT
-  `
+  ```
 - Successfully registrated users can be listed:
-  `http :8080/users
+  ```
+  $ http :8080/users
+
   HTTP/1.1 200
   Content-Type: application/json;charset=UTF-8
   Date: Fri, 05 Feb 2021 14:53:59 GMT
@@ -78,7 +82,7 @@ a (very!) simplistic service takes registration orders for new users.
       },
       ...
   ]
-  `
+  ```
 
 ## The Messaging Use-Case
 
@@ -95,25 +99,25 @@ On the other hand, the publication of the event must happen reliably, because ot
 
 The users are stored in a database and the creation of a new user happens in a transaction.
 A "brilliant" colleague came up with the idea, to trigger an `IncorrectResultSizeDataAccessException` to detect duplicate usernames:
-
-`User user = new User(username);
+```java
+User user = new User(username);
 repository.save(user);
 // Triggers an Exception, if more than one entry is found
 repository.findByUsername(username);
-`
+```
 
 The query for the user by its names triggers an `IncorrectResultSizeDataAccessException`, if more than one entry is found.
 The uncaught exception will mark the transaction for rollback, hence, canceling the requested registration.
 The 400-response is then generated by a corresponding `ExceptionHandler`:
-
-`@ExceptionHandler
+```java
+@ExceptionHandler
 public ResponseEntity incorrectResultSizeDataAccessException(
     IncorrectResultSizeDataAccessException e)
 {
   LOG.info("User already exists!");
   return ResponseEntity.badRequest().build();
 }
-`
+```
 
 Please do not code this at home...
 
@@ -127,17 +131,18 @@ If the transaction is rolled back, no message must be send, because no new user
 In the example implementation I am using an `EventPublisher` to decouple the business logic from the implementation of the messaging.
 The controller publishes an event, when a new user is registered:
 
-`publisher.publishEvent(new UserEvent(this, usernam));
-`
+```java
+publisher.publishEvent(new UserEvent(this, usernam));
+```
 
 A listener annotated with `@TransactionalEventListener` receives the events and handles the messaging:
-
-`@TransactionalEventListener
+```java
+@TransactionalEventListener
 public void onUserEvent(UserEvent event)
 {
     // Sending the message happens here...
 }
-`
+```
 
 In non-critical use-cases, it might be sufficient to actually send the message to Kafka right here.
 Spring ensures, that the message of the listener is only called, if the transaction completes successfully.
index 3fe030736c06766bb129e38fc2382c96ccb37065..0f13c6d52d1d9f11cc16eef03d12aa9c4406263d 100644 (file)
@@ -53,33 +53,33 @@ One more thing that is worth noting is that we utilize the database to create an
 The generated **unique and monotonically increasing id** is required later, for the implementation of **Exactly-Once** semantics.
 
 [The SQL for the table](https://github.com/juplo/demos-spring-data-jdbc/blob/part-1/src/main/resources/db/migration/h2/V2__Table_outbox.sql) looks like this:
-
- `CREATE TABLE outbox (
+```sql
+CREATE TABLE outbox (
   id BIGINT PRIMARY KEY AUTO_INCREMENT,
   key VARCHAR(127),
   value varchar(1023),
   issued timestamp
 );
-`
+```
 
 ## Decoupling The Business Logic
 
 In order to decouple the business logic from the implementation of the messaging mechanism, I have implemented a thin layer, that uses [Spring Application Events](https://docs.spring.io/spring-integration/docs/current/reference/html/event.html) to publish the messages.
 
 Messages are send as a [subclass of `ApplicationEvent`](https://github.com/juplo/demos-spring-data-jdbc/blob/part-1/src/main/java/de/juplo/kafka/outbox/OutboxEvent.java):
-
-`publisher.publishEvent(
+```java
+publisher.publishEvent(
   new UserEvent(
     this,
     username,
     CREATED,
     ZonedDateTime.now(clock)));
-`
+```
 
 The event takes a key ( `username`) and an object as value (an instance of an enum in our case).
 An `EventListener` receives the events and writes them in the outbox table:
-
-`@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
+``` 
+@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
 public void onUserEvent(OutboxEvent event)
 {
   try
@@ -94,7 +94,7 @@ public void onUserEvent(OutboxEvent event)
     throw new RuntimeException(e);
   }
 }
-`
+```
 
 The `@TransactionalEventListener` is not really needed here.
 A normal `EventListener` would also suffice, because spring immediately executes all registered normal event listeners.