From: Kai Moritz Date: Fri, 6 Feb 2026 17:28:06 +0000 (+0100) Subject: WIP:Blog-styl-rewrite X-Git-Url: https://juplo.de/gitweb/?a=commitdiff_plain;h=292f21e3a5f30e758c5efbfb0b9be510e92b2cef;p=website WIP:Blog-styl-rewrite --- diff --git a/content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-0-the-example.md b/content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-0-the-example.md index 338fc418..c0b1b62f 100644 --- a/content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-0-the-example.md +++ b/content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-0-the-example.md @@ -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. diff --git a/content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-1-the-outbox-table.md b/content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-1-the-outbox-table.md index 3fe03073..0f13c6d5 100644 --- a/content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-1-the-outbox-table.md +++ b/content/blog/archive/2021/implementing-the-outbox-pattern-with-kafka-part-1-the-outbox-table.md @@ -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.