Choosing a Processing Layer
The idea in one sentence
Section titled “The idea in one sentence”There is no single “right” way to process a Kafka topic — pick the lightest tool that fits the job, from a plain consumer for full control, to Kafka Streams for embedded logic, to ksqlDB for declarative SQL, to Connect for pure data movement.
The five options
Section titled “The five options”Each layer trades control for convenience:
- Plain consumer — you write the loop with the raw client. Maximum control, any language, but you own offsets, retries, state, and scaling by hand.
- Kafka Streams — a JVM library you embed in your service. Real code (map, join, windowing) with state and exactly-once handled for you, but Java/Scala only and it lives inside your app.
- ksqlDB / SQL — write stream processing as declarative SQL (
CREATE STREAM ... SELECT ...). Fastest to express, no code to deploy, but bounded by what SQL can say. - Kafka Connect — move data, no logic. Ingest a database or sink to a warehouse with config only.
- External stream processor — Flink, Spark Structured Streaming, and friends. A separate cluster for very large jobs, complex event-time semantics, or when you need one engine across many sources.
flowchart TD
start["What does the job need?"] --> move{"Just move data,
no logic?"}
move -->|"yes"| connect["Kafka Connect"]
move -->|"no"| logic{"Express it in SQL?"}
logic -->|"yes"| ksql["ksqlDB"]
logic -->|"no"| jvm{"JVM app, embedded?"}
jvm -->|"yes"| streams["Kafka Streams"]
jvm -->|"no"| scale{"Huge scale or
non-JVM control?"}
scale -->|"external engine"| flink["Flink / Spark"]
scale -->|"full control"| consumer["Plain consumer"] How to pick
Section titled “How to pick”Work down from the simplest fit:
- Only moving data? Use Connect — do not write code to copy a table.
- Logic that fits SQL? Use ksqlDB — filters, joins, and windowed aggregates with no deployment.
- Logic that needs real code, on the JVM? Use Kafka Streams — embedded, stateful, exactly-once, scales with your app.
- Need another language, or total control of the loop? Use a plain consumer.
- Massive scale or advanced event-time processing across many systems? Reach for an external processor like Flink.
A healthy system usually mixes them: Connect ingests, Streams or ksqlDB transforms, and a plain consumer serves the last mile into a service. The rule of thumb is simply: do not pay for power you will not use.
Closing the module
Section titled “Closing the module”You now have the whole processing toolkit. Schema Registry gives your records a versioned contract so teams can evolve safely. Connect moves data in and out without code. Kafka Streams processes streams with real, stateful logic inside your app. And this guide tells you which to reach for. Together with the earlier modules on brokers, producers, consumers, and delivery guarantees, you can now design an end-to-end Kafka system — and justify every layer in it.