10.4.1 – Subject Naming Strategies
Module: Section 10 – Kafka Ecosystem & Tools
Section: 10.4 – Schema Registry Operations
Goal: Understand how subject naming strategies impact schema organization and evolution
Introduction
Subject naming strategies determine how schemas are organized and versioned in Schema Registry. The strategy you choose impacts schema evolution, multi-tenancy, and operational complexity.
Why This Module Matters: Wrong naming strategy = messy schema organization, difficult migrations, and coupling issues.
What Is a Subject?
Subject: Logical grouping of schemas in Schema Registry.
Example:
Subject: "users-value"
Version 1 → Schema ID 5
Version 2 → Schema ID 12
Version 3 → Schema ID 27
Subject = Schema Evolution Boundary: Compatibility rules apply per subject.
The Three Built-In Strategies
Schema Registry provides 3 built-in subject naming strategies:
- TopicNameStrategy (Default)
- RecordNameStrategy
- TopicRecordNameStrategy
1. TopicNameStrategy (Default)
How It Works
Subject Name: <topic>-key or <topic>-value
Example:
Topic: users
Key Subject: users-key
Value Subject: users-value
Producer Configuration
Properties props = new Properties();
props.put("value.subject.name.strategy",
"io.confluent.kafka.serializers.subject.TopicNameStrategy"); // Default
Use Case
Single schema per topic:
Topic: users
All messages use User schema (v1, v2, v3)
Example:
// All messages to "users" topic use User schema
User user1 = User.newBuilder().setUserId(1).build();
User user2 = User.newBuilder().setUserId(2).build();
producer.send(new ProducerRecord<>("users", "1", user1));
producer.send(new ProducerRecord<>("users", "2", user2));
// Subject: users-value (versions: 1, 2, 3)
Advantages
✅ Simple: One subject per topic
✅ Easy to understand: Topic = schema boundary
✅ Low overhead: Minimal subjects to manage
✅ Works for 95% of use cases
Disadvantages
❌ One schema per topic: Can't mix multiple event types in same topic
❌ Topic explosion: Need separate topic for each event type
When to Use
✅ Default choice for most applications
✅ One event type per topic (e.g., User events, Order events)
✅ Simple architectures
2. RecordNameStrategy
How It Works
Subject Name: <namespace>.<record-name>
Example:
Record: com.example.avro.User
Subject: com.example.avro.User
Record: com.example.avro.Order
Subject: com.example.avro.Order
Key Insight: Subject is based on schema name, not topic name.
Producer Configuration
Properties props = new Properties();
props.put("value.subject.name.strategy",
"io.confluent.kafka.serializers.subject.RecordNameStrategy");
Use Case
Multiple event types in one topic:
Topic: events
Message 1: User schema → Subject: com.example.avro.User
Message 2: Order schema → Subject: com.example.avro.Order
Message 3: User schema → Subject: com.example.avro.User
Example
User Schema:
{
"type": "record",
"name": "User",
"namespace": "com.example.avro",
"fields": [...]
}
Order Schema:
{
"type": "record",
"name": "Order",
"namespace": "com.example.avro",
"fields": [...]
}
Producer Code:
// Both go to same topic, different subjects
User user = User.newBuilder().setUserId(1).build();
Order order = Order.newBuilder().setOrderId("123").build();
producer.send(new ProducerRecord<>("events", "user-1", user));
// → Subject: com.example.avro.User
producer.send(new ProducerRecord<>("events", "order-123", order));
// → Subject: com.example.avro.Order
Advantages
✅ Multi-schema topics: Mix multiple event types in one topic
✅ Schema reuse: Same schema used across multiple topics
✅ Reduced topic count: Fewer topics to manage
✅ Event-driven patterns: Natural fit for event sourcing
Disadvantages
❌ Complex compatibility: Must manage compatibility per schema, not per topic
❌ Consumer complexity: Consumers must handle multiple schemas
❌ Harder to understand: Subject != topic name
When to Use
✅ Event sourcing (multiple event types in events topic)
✅ Multi-tenancy (same schema, multiple topics)
✅ Schema reuse across topics
✅ CDC (Change Data Capture with multiple table schemas)
Consumer Handling Multiple Schemas
ConsumerRecords<String, Object> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, Object> record : records) {
Object value = record.value();
if (value instanceof User) {
User user = (User) value;
processUser(user);
} else if (value instanceof Order) {
Order order = (Order) value;
processOrder(order);
} else {
logger.warn("Unknown type: {}", value.getClass());
}
}
3. TopicRecordNameStrategy
How It Works
Subject Name: <topic>-<namespace>.<record-name>
Example:
Topic: events
Record: com.example.avro.User
Subject: events-com.example.avro.User
Topic: events
Record: com.example.avro.Order
Subject: events-com.example.avro.Order
Key Insight: Combines topic name + record name.
Producer Configuration
Properties props = new Properties();
props.put("value.subject.name.strategy",
"io.confluent.kafka.serializers.subject.TopicRecordNameStrategy");
Use Case
Multiple schemas per topic + per-topic schema evolution:
Topic: events
Subject: events-com.example.avro.User (v1, v2, v3)
Subject: events-com.example.avro.Order (v1, v2, v3)
Topic: analytics
Subject: analytics-com.example.avro.User (v1, v2)
Subject: analytics-com.example.avro.Order (v1, v2)
Example
Same User schema used in two topics:
User user = User.newBuilder().setUserId(1).build();
producer.send(new ProducerRecord<>("events", "1", user));
// → Subject: events-com.example.avro.User
producer.send(new ProducerRecord<>("analytics", "1", user));
// → Subject: analytics-com.example.avro.User
Result: Same schema evolves independently per topic!
Advantages
✅ Per-topic evolution: Same schema evolves differently per topic
✅ Multi-schema topics: Like RecordNameStrategy
✅ Topic isolation: Schema changes in events don't affect analytics
✅ Fine-grained compatibility: Per-topic + per-schema control
Disadvantages
❌ Subject explosion: Many subjects to manage
❌ Complex: Hardest to understand
❌ Operational overhead: More subjects = more monitoring
When to Use
✅ Multi-tenancy with isolation (e.g., tenant-a-events, tenant-b-events)
✅ Per-topic schema evolution required
✅ Advanced architectures with strict isolation needs
Comparison Table
| Strategy | Subject Name | Schemas per Topic | Schema Reuse | Complexity |
|---|---|---|---|---|
| TopicNameStrategy | <topic>-value |
1 | ❌ | Low |
| RecordNameStrategy | <namespace>.<name> |
Many | ✅ | Medium |
| TopicRecordNameStrategy | <topic>-<namespace>.<name> |
Many | ✅ (isolated) | High |
Real-World Examples
Example 1: E-Commerce (TopicNameStrategy)
Architecture: One topic per event type.
Topic: users → Subject: users-value
Topic: orders → Subject: orders-value
Topic: payments → Subject: payments-value
Why?: Simple, one schema per topic, easy to understand.
Example 2: Event Sourcing (RecordNameStrategy)
Architecture: All events in one topic.
Topic: events
Subject: com.ecommerce.UserCreated
Subject: com.ecommerce.OrderPlaced
Subject: com.ecommerce.PaymentProcessed
Why?: Preserve event order, multiple event types, event store pattern.
Example 3: Multi-Tenant SaaS (TopicRecordNameStrategy)
Architecture: Per-tenant topics with multiple schemas.
Topic: tenant-a-events
Subject: tenant-a-events-com.app.User
Subject: tenant-a-events-com.app.Order
Topic: tenant-b-events
Subject: tenant-b-events-com.app.User
Subject: tenant-b-events-com.app.Order
Why?: Tenant isolation, per-tenant schema evolution, security boundaries.
Custom Naming Strategies
Implementing Custom Strategy
public class CustomSubjectNameStrategy implements SubjectNameStrategy {
@Override
public String subjectName(String topic, boolean isKey, Object value) {
if (value instanceof GenericRecord) {
GenericRecord record = (GenericRecord) value;
String namespace = record.getSchema().getNamespace();
String name = record.getSchema().getName();
// Custom: <env>-<topic>-<name>
return String.format("%s-%s-%s.%s",
System.getenv("ENVIRONMENT"), // dev, prod
topic,
namespace,
name
);
}
return topic + (isKey ? "-key" : "-value");
}
}
Configuration:
props.put("value.subject.name.strategy",
"com.example.CustomSubjectNameStrategy");
Key Takeaways
TopicNameStrategy (Default):
<topic>-value. One schema per topic, simplest approach.RecordNameStrategy:
<namespace>.<name>. Multiple schemas per topic, schema reuse.TopicRecordNameStrategy:
<topic>-<namespace>.<name>. Per-topic schema evolution.Subject = Evolution Boundary: Compatibility rules apply per subject.
Default Choice: TopicNameStrategy works for 95% of use cases.
Event Sourcing: Use RecordNameStrategy for multiple event types in one topic.
Multi-Tenancy: Use TopicRecordNameStrategy for per-tenant isolation.
Custom Strategies: Implement
SubjectNameStrategyinterface for custom logic.Operational Impact: More subjects = more monitoring, more complexity.
Choose Early: Changing strategies later requires migration.
What's Next?
Next Module: 10.4.2 – Schema Registry REST API
What We Covered:
- ✅ Subject naming strategies
- ✅ TopicNameStrategy (default)
- ✅ RecordNameStrategy (multi-schema)
- ✅ TopicRecordNameStrategy (isolated evolution)
What's Coming:
- ❌ Schema Registry REST API endpoints
- ❌ Monitoring Schema Registry
- ❌ Troubleshooting
References:
- Schema evolution → 10.3: Schema Evolution & Compatibility
- Avro serialization → 10.2.2: Avro Serialization with Kafka
- Operations → 10.4: Schema Registry Operations