1---2name: messaging3description: Async messaging patterns including broker selection, message design, producer/consumer patterns, schema evolution, and monitoring. Use when implementing Kafka, RabbitMQ, or event-driven architecture.4license: MIT5---6# Messaging Rules78## 1. Message Broker Selection910| Broker | Strengths | Best For |11| ---------------- | ---------------------------------------------- | -------------------------------------- |12| Kafka | High throughput, durability, ordering | Event streaming, log aggregation |13| RabbitMQ | Flexible routing, low latency | Task queues, request-reply |14| NATS Core | Ultra-low latency, lightweight, simple | Microservice RPC, IoT, edge computing |15| NATS JetStream | Persistence on NATS, exactly-once, KV/Object | Durable streaming with NATS simplicity |16| Apache Pulsar | Multi-tenancy, tiered storage, geo-replication | Multi-tenant SaaS, hybrid cloud |17| Redis Pub/Sub | Simple, fast | Real-time notifications, caching |1819### Selection Criteria2021- Use **Kafka** when ordering, replay, and ecosystem maturity are required22- Use **RabbitMQ** when flexible routing and acknowledgment patterns are priorities23- Use **NATS Core** when ultra-low latency and operational simplicity are critical (no persistence needed)24- Use **NATS JetStream** when you need Kafka-like durability with NATS operational simplicity25- Use **Apache Pulsar** when multi-tenancy, tiered storage, or geo-replication are requirements26- Use **Redis Pub/Sub** only for ephemeral messages where loss is acceptable2728### Broker Comparison Matrix2930| Feature | Kafka | RabbitMQ | NATS Core | NATS JetStream | Pulsar | Redis Pub/Sub |31| ---------------------- | ------------- | ----------------- | ---------- | -------------- | ------------- | ------------- |32| Persistence | Yes | Yes | No | Yes | Yes | No |33| Ordering guarantee | Per-partition | Per-queue | No | Per-stream | Per-partition | No |34| Message replay | Yes | Limited | No | Yes | Yes | No |35| Exactly-once | Yes | No | No | Yes | Yes | No |36| Multi-tenancy | Limited | Vhost | Account | Account | Native | No |37| Geo-replication | MirrorMaker | Shovel/Federation | Leaf nodes | Leaf nodes | Native | No |38| Operational complexity | High | Medium | Low | Low-Medium | High | Low |39| Library maturity | Excellent | Excellent | Good | Good | Good | Excellent |40| Throughput | Very high | Medium | Very high | High | Very high | High |41| Latency | Medium | Low | Ultra-low | Low | Medium | Ultra-low |4243---4445## 2. Message Design4647### Message Structure4849```json50{51 "messageId": "uuid-v4",52 "type": "order.created",53 "source": "order-service",54 "timestamp": "2024-01-15T10:30:45.123Z",55 "version": "1.0",56 "data": {57 "orderId": "order-123",58 "userId": "user-456",59 "totalAmount": 1500060 },61 "metadata": {62 "traceId": "abc-123",63 "correlationId": "req-789"64 }65}66```6768### Message Design Rules6970- Always include a unique `messageId` for deduplication71- Include `traceId` for distributed tracing correlation72- Use a `version` field to support schema evolution73- Keep messages self-contained — consumers should not need to call back to the producer74- Use past tense for event names (`order.created`, not `create.order`)7576---7778## 3. Producer Patterns7980### Delivery Guarantees8182| Guarantee | Description | Trade-off |83| -------------- | ---------------------------------- | ---------------------- |84| At-most-once | Fire and forget | May lose messages |85| At-least-once | Retry until acknowledged | May duplicate messages |86| Exactly-once | Deduplication + transactional send | Highest complexity |8788### Producer Rules8990- Default to at-least-once delivery — it is the safest general-purpose guarantee91- Use transactional outbox pattern for database + message atomicity92- Never publish messages inside a database transaction without outbox93- Include idempotency keys so consumers can deduplicate9495---9697## 4. Consumer Patterns9899### Idempotent Consumers100101- Always design consumers to handle duplicate messages102- Use `messageId` or business key for deduplication103- Store processed message IDs with TTL to prevent reprocessing104105### Error Handling106107| Strategy | When to Use | Implementation |108| ------------------ | ------------------------------------ | ------------------------------ |109| Retry with backoff | Transient errors (network, timeout) | Exponential backoff, max 3-5 |110| Dead letter queue | Persistent failures | Route to DLQ after max retries |111| Skip and log | Poison messages (invalid format) | Log error, acknowledge message |112113### Consumer Rules114115- Always set a maximum retry count — never retry indefinitely116- Route failed messages to a dead letter queue (DLQ) for investigation117- Monitor DLQ size — growing DLQ indicates unresolved issues118- Process messages in order only when business logic requires it119120---121122## 5. NATS-Specific Patterns123124### NATS Core125126- Use subject-based addressing (`orders.created`, `payments.>` wildcard)127- Leverage request-reply pattern for synchronous microservice communication128- Use queue groups for load balancing across consumer instances129- NATS Core is fire-and-forget — no persistence or replay130131### NATS JetStream132133- Use streams for durable message storage with configurable retention134- Leverage consumer groups with durable names for reliable processing135- Use Key-Value store for lightweight configuration or state sharing136- Use Object store for large payload handling (claim check pattern)137- Configure stream limits: `max_msgs`, `max_bytes`, `max_age` to prevent unbounded growth138- Prefer pull-based consumers over push-based for backpressure control139140### NATS Design Rules141142- Use dot-separated hierarchical subjects (`service.entity.action`)143- Leverage wildcards (`*` single token, `>` multi-token) for flexible subscriptions144- Keep payloads small (< 1MB for Core, configurable for JetStream)145- Use headers for metadata (traceId, version) instead of embedding in payload146147---148149## 6. Pulsar-Specific Patterns150151### Topic and Namespace Design152153- Use tenant/namespace/topic hierarchy (`public/orders/created`)154- Leverage namespaces for access control and policy isolation155- Use partitioned topics for high-throughput scenarios156- Configure topic-level policies (retention, TTL, backlog quota) per use case157158### Subscription Modes159160| Mode | Behavior | Use Case |161| ---------- | ------------------------------------- | --------------------------------- |162| Exclusive | Single consumer per subscription | Ordered processing |163| Failover | Active-standby consumer pair | High availability |164| Shared | Round-robin across consumers | Parallel processing |165| Key_Shared | Partition by key across consumers | Ordered per-key parallel |166167### Pulsar Design Rules168169- Use tiered storage for cost-effective long-term retention170- Leverage schema registry with Avro/Protobuf for type safety171- Use delayed message delivery for scheduled tasks172- Configure backlog quota policies to prevent unbounded topic growth173- Use geo-replication for multi-region disaster recovery174175---176177## 7. Schema Evolution (All Brokers)178179### Backward Compatibility180181- Adding new optional fields is always safe182- Removing fields or changing types is a breaking change183- Use schema registry (Avro, Protobuf) for strict contract enforcement184- Version your message schemas and support at least N-1 version185186### Migration Strategy187188- Deploy consumers that support both old and new schema first189- Then deploy producers with the new schema190- Never deploy producer changes before consumer compatibility is verified191192---193194## 8. Monitoring195196### Key Metrics197198| Metric | Alert Threshold | Severity |199| -------------------------------------- | ----------------- | -------- |200| Consumer lag | Growing over time | Warning |201| DLQ message count | > 0 | Warning |202| Message processing time | > SLA threshold | Warning |203| Producer error rate | > 1% | Critical |204| Consumer group rebalance frequency | Frequent | Warning |205206---207208## 9. Anti-Patterns209210- Publishing messages inside database transactions (use outbox pattern)211- No dead letter queue for failed messages212- Consumers that assume message ordering without partition keys213- Missing idempotency handling in consumers214- Unbounded retry without backoff or max attempts215- Large message payloads (> 1MB) — use claim check pattern instead216- No schema versioning for message contracts217218## Additional References219220- For Kafka topic design, partitioning, consumer groups, and delivery guarantees, see [references/kafka-patterns.md](references/kafka-patterns.md)