Schema Registry
Confluent
Avro
Kafka
Spring Boot Applications

Using Schema Registry from Confluent with Avro and Kafka in Spring Boot Applications

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When a Spring Boot service produces or consumes Avro messages through Kafka, Confluent Schema Registry adds a central contract for those message schemas. That combination gives you compact Avro payloads, safer schema evolution, and a shared place for producers and consumers to agree on data shape.

Configure Kafka Serializers and the Registry URL

The essential step is telling Spring Kafka to use Confluent’s Avro serializer and deserializer, plus the Schema Registry endpoint. Without that, your app may still talk to Kafka, but it will not register or resolve Avro schemas correctly.

A typical application.yml setup looks like this:

yaml
1spring:
2  kafka:
3    bootstrap-servers: localhost:9092
4    producer:
5      key-serializer: org.apache.kafka.common.serialization.StringSerializer
6      value-serializer: io.confluent.kafka.serializers.KafkaAvroSerializer
7      properties:
8        schema.registry.url: http://localhost:8081
9    consumer:
10      group-id: users-group
11      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
12      value-deserializer: io.confluent.kafka.serializers.KafkaAvroDeserializer
13      properties:
14        schema.registry.url: http://localhost:8081
15        specific.avro.reader: true

The specific.avro.reader flag is important when you want generated Avro classes instead of generic Avro records in your listener code.

Define an Avro Schema and Generate Java Types

A common workflow is to define a schema in an .avsc file and generate Java classes during the build. A simple user schema might look like this:

json
1{
2  "type": "record",
3  "name": "UserCreated",
4  "namespace": "com.example.avro",
5  "fields": [
6    {"name": "id", "type": "string"},
7    {"name": "email", "type": "string"},
8    {"name": "active", "type": "boolean"}
9  ]
10}

Generated classes are useful because they give you compile-time types in both producers and consumers. That reduces mapping mistakes and makes schema changes more visible during development.

Produce and Consume Avro Messages in Spring Boot

Once serializers and generated types are in place, ordinary Spring Kafka code becomes straightforward.

java
1import com.example.avro.UserCreated;
2import org.springframework.kafka.core.KafkaTemplate;
3import org.springframework.kafka.annotation.KafkaListener;
4import org.springframework.stereotype.Service;
5
6@Service
7public class UserEventsService {
8    private final KafkaTemplate<String, UserCreated> kafkaTemplate;
9
10    public UserEventsService(KafkaTemplate<String, UserCreated> kafkaTemplate) {
11        this.kafkaTemplate = kafkaTemplate;
12    }
13
14    public void publishUserCreated(String key, UserCreated event) {
15        kafkaTemplate.send("users.created", key, event);
16    }
17
18    @KafkaListener(topics = "users.created", groupId = "users-group")
19    public void handleUserCreated(UserCreated event) {
20        System.out.println("Received user: " + event.getEmail());
21    }
22}

When the producer sends UserCreated, the serializer registers the schema if needed and writes the message in Confluent’s wire format. The consumer then resolves the schema ID through Schema Registry and deserializes the record back into the generated class.

Schema Evolution Is the Real Payoff

The main reason to use Schema Registry is not just serialization convenience. It is controlled schema evolution. If you add a field later, such as displayName, you want a compatibility rule that keeps older consumers from breaking.

That means schema design matters. Adding optional fields with defaults is usually easier to evolve safely than changing types or removing required fields. In team environments, Schema Registry becomes the place where compatibility rules are enforced rather than assumed.

Keep Configuration and Contracts Explicit

A solid Spring Boot setup usually includes clear topic names, generated Avro types, and separate configuration for local versus shared environments. It is also worth deciding early whether the service should use specific records or generic records. Specific records are more convenient for application code, while generic records are sometimes used in infrastructure-heavy pipelines.

Common Pitfalls

  • Forgetting schema.registry.url on either the producer or consumer side.
  • Omitting specific.avro.reader and then wondering why listener payloads are generic records.
  • Sending plain JSON with Avro serializers or mixing incompatible serializer settings across services.
  • Treating schema evolution as an afterthought instead of designing for compatibility from the start.
  • Assuming Kafka alone enforces message contracts when the real enforcement comes from serializers and Schema Registry rules.

Summary

  • Schema Registry gives Spring Boot Kafka applications a central contract for Avro schemas.
  • Confluent Avro serializers must be configured explicitly on both producer and consumer sides.
  • Generated Avro classes make Spring Kafka listeners and producers easier to work with.
  • The biggest value is safe schema evolution, not only smaller payload size.
  • Clear serializer, reader, and compatibility choices prevent many integration failures.

Course illustration
Course illustration

All Rights Reserved.