{"id":4541,"date":"2026-07-09T09:12:26","date_gmt":"2026-07-09T09:12:26","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/07\/09\/developing-real-time-event-processing-with-micronaut-and-kafka-streams-a-step-by-step-guide\/"},"modified":"2026-07-09T09:12:26","modified_gmt":"2026-07-09T09:12:26","slug":"developing-real-time-event-processing-with-micronaut-and-kafka-streams-a-step-by-step-guide","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/07\/09\/developing-real-time-event-processing-with-micronaut-and-kafka-streams-a-step-by-step-guide\/","title":{"rendered":"Developing Real-Time Event Processing With Micronaut and Kafka Streams: A Step-by-Step Guide"},"content":{"rendered":"<div><img data-opt-id=1314482815  fetchpriority=\"high\" decoding=\"async\" width=\"769\" height=\"330\" src=\"https:\/\/devops.com\/wp-content\/uploads\/2023\/06\/datastream.jpg\" class=\"attachment-large size-large wp-post-image\" alt=\"data pipeline, AI, Dynatrace OLAP, data processing, data mobility, strategy, privacy, developers, data privacy\" \/><\/div>\n<p><img data-opt-id=609474010  fetchpriority=\"high\" decoding=\"async\" width=\"150\" height=\"150\" src=\"https:\/\/devops.com\/wp-content\/uploads\/2023\/06\/datastream-150x150.jpg\" class=\"attachment-thumbnail size-thumbnail wp-post-image\" alt=\"data pipeline, AI, Dynatrace OLAP, data processing, data mobility, strategy, privacy, developers, data privacy\" \/><\/p>\n<p><a href=\"https:\/\/devops.com\/study-real-time-data-has-a-transformative-effect\/\" target=\"_blank\" rel=\"noopener\">Real-time data processing<\/a> is paramount in many modern applications across multiple domains, from monitoring financial transactions to analytics on IoT sensor data. Although historical solutions required the implementation of a complex distributed system involving possibly many JVMs, we are able to create a more simplistic yet powerful solution for stream processing by using Micronaut and Kafka Streams in combination. This blog demonstrates how to develop an entire real-time word counting application that will cover the important foundational areas of stream processing, and it will also use modern best practices to create a simple executable jar that can be deployed easily.<\/p>\n<h3>Why Micronaut + Kafka Streams?<\/h3>\n<p>Before we get to coding our application, it is essential to understand why this combination is one that many developers are now considering for stream processing applications.<\/p>\n<h3>Micronaut Benefits<\/h3>\n<p>Micronaut is a new framework that, instead of handling dependency injection and configuration at runtime, processes both at compile time. This offers several advantages for microservices and stream processing applications.<\/p>\n<p>The benefits include:<\/p>\n<ul>\n<li>Fast Startup Time: As there is no reflection processing because it is done at compile-time<\/li>\n<li>Reduced Memory: Fewer runtime dependencies and smaller bytecode<\/li>\n<li>More Cloud-Friendly: Built for container-based and serverless environments<\/li>\n<li>Improved Developer Experience: Shorter development cycles due to shorter restarts<\/li>\n<\/ul>\n<h3>Kafka Streams: Stream Processing Without Separate Clusters<\/h3>\n<p>As Kafka Streams allows for stream processing code to be executed as a library in the application rather than relying on some sort of cluster, operational overhead is reduced.<\/p>\n<p>Key advantages consist of:<\/p>\n<ul>\n<li>No Separate Infrastructure: Library running as part of your application<\/li>\n<li>Exactly-Once Processing: Data consistency guarantees built-in<\/li>\n<li>Automatic Scaling: Additional instances to increase throughput<\/li>\n<li>Fault-Tolerance: Automatic recovery of state and re-balancing<\/li>\n<li>Architecture Overview<\/li>\n<\/ul>\n<p>The application follows a clean architecture pattern, with REST APIs handling communication to the outside world and Kafka Streams managing the real-time processing pipeline.<\/p>\n<p>Data flows:<\/p>\n<p>1. REST API that accepts text messages<br \/>\n2. Messages are sent to Kafka Topics<br \/>\n3. The messages are processed in real-time by Kafka Streams<br \/>\n4. The results are saved to state stores and output topics<br \/>\n5. Query APIs allow for access to the processed results<\/p>\n<h3>What We\u2019re Building<\/h3>\n<p>We\u2019re building a word counting application that might seem simple on the surface, but it demonstrates all the core patterns you\u2019ll need for more complex stream processing scenarios. When you send a text message through our REST API, it gets broken down into individual words, counted in real-time and stored so you can query how many times any word has appeared.<\/p>\n<p>The beauty of this approach is that the same patterns work whether you\u2019re counting words, tracking user behavior or processing financial transactions. The topology stays conceptually the same; you just change the data models and processing logic.<\/p>\n<p>What makes this particularly interesting is that everything runs in a single JVM. No separate stream processing cluster, no complex deployment orchestration. Just a regular Java application that happens to be really good at processing streams of data.<\/p>\n<h3>Setting up the Development Environment<\/h3>\n<p>Prerequisites<\/p>\n<ul>\n<li>Java 17 or later<\/li>\n<li>Docker and Docker Compose<\/li>\n<li>A text editor<\/li>\n<\/ul>\n<p>There is a full Docker Compose configuration in the project repository, which has:<\/p>\n<ul>\n<li>Kafka broker, with auto-topic creation<\/li>\n<li>Zookeeper, for cluster coordination<\/li>\n<li>Local development with proper networking configuration<\/li>\n<\/ul>\n<p>Start up the infrastructure:<br \/>\ngit clone https:\/\/github.com\/mahitha-ada\/kafka-streams.git<br \/>\ncd kafka-streams<br \/>\ndocker compose up -d<\/p>\n<h3>Creating the Micronaut Project<\/h3>\n<p>The repository contains a complete Micronaut project that has support for Kafka Streams. The project has important dependencies like:<\/p>\n<ul>\n<li>micronaut-kafka-streams: Core Kafka Streams integration<\/li>\n<li>jackson-databind: JSON serialization support<\/li>\n<li>jackson-datatype-jsr310: Support for Java 8 time types for JSON serialization<\/li>\n<li>micronaut-http-server-netty: Implementation of HTTP server<\/li>\n<\/ul>\n<p>Note: You need to include the dependency on the jackson-datatype-jsr310 for you to be able to handle java.time.Instant and other Java 8 time types in your message models.<\/p>\n<h3>Configuration That Makes it Work<\/h3>\n<p>The Key Configuration Points in Application.yml<\/p>\n<ul>\n<li>Application ID: Your Kafka Streams application will have a unique ID. This is important because Kafka uses this to track which consumer group your application belongs to and where it left off processing.<\/li>\n<li>Exactly-Once Semantics: This guarantees that each record will be processed exactly once, even if your application crashes and restarts.<\/li>\n<li>Commit Interval: This controls how often the state store gets flushed to disk. Lower values mean more up-to-date queries but higher I\/O overhead.<\/li>\n<li>Bootstrap Servers: This is how your application connects to the Kafka cluster. In production, you\u2019d have multiple servers here for redundancy.<\/li>\n<\/ul>\n<h3>Constructing the Stream Processing Pipeline<\/h3>\n<p>The foundation of our application is the Kafka Streams topology that describes the flow of data through the processing pipeline.<\/p>\n<p>The topology does the following:<\/p>\n<p>1. Read Messages: Creates a stream from the text-messages topic<br \/>\n2. Split Into Words: Each message gets split into individual words<br \/>\n3. Group and Count: Groups by word and count occurrences<br \/>\n4. Store Results: The counts are stored in a state store called word-counts-store<br \/>\n5. Output Results: Finally, sends the results to the word-counts topic<\/p>\n<p>The key insight here is that this processing happens continuously. As new messages arrive, word counts get updated in real-time, and you can query the current state at any time.<\/p>\n<h3>REST API Integration<\/h3>\n<p>Stream processing handles the data transformation, but REST APIs provide the interface for external systems to interact with our application.<\/p>\n<p>There are two primary controllers:<\/p>\n<ul>\n<li>MessageController handles message ingestion \u2014 a simple POST endpoint that accepts text messages and sends them to Kafka.<\/li>\n<li>WordCountController provides query access to results \u2014 you can query individual word counts or get the top N words.<\/li>\n<\/ul>\n<p>Important Implementation Note: Our working implementation uses a direct KafkaProducer&lt;String, String&gt; rather than Micronaut\u2019s @KafkaClient interface. This ensures reliable JSON string serialization and avoids common issues where messages appear to be sent successfully but arrive as null values in the Kafka Streams processor.<\/p>\n<h3>Testing the Application<\/h3>\n<p>Before testing the application, you need to create the required Kafka topics:<\/p>\n<p>docker exec -it kafka kafka-topics \u2013create \u2013topic text-messages \u2013bootstrap-server localhost:9092 \u2013partitions 3 \u2013replication-factor 1<\/p>\n<h3>You Can Run the Application With: .\/gradlew Run<\/h3>\n<p>The application starts up in a few seconds and is ready to process messages on localhost:8082.<\/p>\n<p>Send Message:<br \/>\ncurl -X POST http:\/\/localhost:8082\/api\/messages -H \u2018Content-Type: application\/json\u2019 -d \u2018{\u201ccontent\u201d: \u201chello world hello kafka streams\u201d, \u201cuserId\u201d: \u201cuser123\u201d}\u2019<\/p>\n<p>Query Word Counts:<br \/>\ncurl http:\/\/localhost:8082\/api\/wordcounts\/hello # Returns {\u201cword\u201d:\u201dhello\u201d,\u201dcount\u201d:2}<br \/>\ncurl http:\/\/localhost:8082\/api\/wordcounts\/kafka # Returns {\u201cword\u201d:\u201dkafka\u201d,\u201dcount\u201d:1}<\/p>\n<h3>Common Gotchas and Remedies<\/h3>\n<ul>\n<li>Jackson Time Serialization: When deserializing java.time.Instant, JsonMappingException occurs. Add the JSR310 module dependency and register it with your ObjectMapper.<\/li>\n<li>Producer Implementation Issues: Messages sent via REST API, but Kafka Streams shows null values. Use a direct KafkaProducer&lt;String, String&gt; implementation instead of the @KafkaClient interface.<\/li>\n<li>Topic Creation: Application does not start with \u2018topic does not exist\u2019 errors. Always ensure topics are created before starting the application.<\/li>\n<li>State Store not Operational: Application does not start after ungraceful shutdown. Implement proper cleanup policies and health checks.<\/li>\n<li>Memory Use: High memory consumption for large state stores. Configure appropriate cache sizes and use windowed operations for time-based data.<\/li>\n<\/ul>\n<h3>Next Steps<\/h3>\n<p>In this blog, we have covered the basics of building real-time stream processing applications using Micronaut and Kafka Streams. Here are some directions to explore:<\/p>\n<ul>\n<li>Add windowed operations for time-based analytics<\/li>\n<li>Implement joins over multiple streams<\/li>\n<li>Add schema registry for compliance<\/li>\n<li>Add metrics and health checks<\/li>\n<\/ul>\n<h3>Conclusion<\/h3>\n<p>You have a simple yet powerful approach to building real-time stream processing applications using Micronaut and Kafka Streams. By leveraging compile-time enhancements and embedded stream processing, you can build systems that are both performant and operationally simple.<\/p>\n<p>The combination works well because it eliminates a lot of the operational complexity that comes with traditional stream processing platforms. You don\u2019t need to manage separate clusters, worry about complex deployment orchestration or deal with the networking issues that come with distributed systems.<\/p>\n<p>The patterns you\u2019ve learned here scale up well to more complex scenarios. Whether you\u2019re processing financial transactions, IoT sensor data or user behavior events, the fundamental approach remains the same.<\/p>\n<p><a href=\"https:\/\/devops.com\/developing-real-time-event-processing-with-micronaut-and-kafka-streams-a-step-by-step-guide\/\" target=\"_blank\" class=\"feedzy-rss-link-icon\">Read More<\/a><\/p>\n<p>\u200b<\/p>","protected":false},"excerpt":{"rendered":"<p>Real-time data processing is paramount in many modern applications across multiple domains, from monitoring financial transactions to analytics on IoT [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4542,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[5],"tags":[],"class_list":["post-4541","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/4541","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=4541"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/4541\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media\/4542"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=4541"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=4541"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=4541"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}