Event Bus Design
Table of Contents
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Component Details
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendix
Introduction
This document targets event-driven architecture developers, systematically explains event bus design and implementation, focuses on the following aspects:
- StreamHub architecture design and responsibility boundaries
- Event bus interface abstraction and unified interface for local/remote implementations
- Subscriber management mechanism and event handler registration
- Event routing and distribution strategy
- Lifecycle management and graceful shutdown
- Startup process and configuration items
- Performance optimization and error handling best practices
Project Structure
Modules around event bus and event stream (StreamHub/NATS JetStream) are distributed as follows:
- Event Bus Abstraction and Local Implementation: pkg/eventbus
- Event Stream Publish/Subscribe and Reader: pkg/messaging
- NATs Configuration: pkg/config
Core Components
Event Bus Interface and Local Implementation
- Interface Abstraction: Event bus provides publish, subscribe, unsubscribe, statistics and close capabilities, unifies local and remote implementations
- Memory Event Bus: Uses read-write lock to protect subscription table, asynchronously calls handlers during publish, supports statistics and close status
- NATS Event Bus: Based on standard NATS connection, supports publish/subscribe/unsubscribe/close
Event Stream Publisher and Subscriber
- JetStream Publisher: Responsible for ensuring stream exists and publishing domain events
- JetStream Subscriber: Responsible for creating/starting/stopping persistent consumers, supports graceful shutdown and error recovery
- JetStream Reader: Provides event acquisition, replay and replay by version/offset capabilities
StreamHub
- Unified event stream entry, encapsulates subscriber collection, provides start/close and handler registration capabilities
Generic Event Model
- Generic event contains event identifier, event type, timestamp and payload, convenient for cross-implementation transmission
Architecture Overview
StreamHub as unified entry of event stream system, coordinates JetStream publisher, subscriber and reader, while compatible with generic event bus interface, achieves unified abstraction of local and remote event bus.
Component Details
JetStream Publisher
- Function Points: Ensures event stream exists, creates if not exists according to configuration
- Subject Naming Rule: serviceName.aggregateType.eventType
- Configuration Items: Service name, aggregate type list, maximum message age/size, stream size, debug mode, etc.
JetStream Subscriber
- Lifecycle and Graceful Shutdown
- Start: Creates persistent consumer, asynchronous Consume, supports context cancellation
- Stop/Close: Cancels consumption context, waits for processing to complete, forces exit on timeout; cleans up resources
- Error Handling and Robustness
- Uses defer recover in consumption loop to prevent panic from causing process crash
- Sends negative acknowledgment (Nak) when deserialization fails or handler execution fails, supports retry
- Type Safety: Constrains event type through generic constraints, ensures handler signature matches event type
JetStream Reader
- Capabilities: Get aggregate events, replay by version, replay by offset, apply events to aggregate root
- Design Characteristics: Uses ephemeral consumer (non-persistent) to avoid control plane pressure from frequent creation/destruction
StreamHub
- Role Positioning: Unified entry of event stream system, holds subscriber collection, implements start/close interface
- Handler Registration: Registers domain event handlers to subscriber collection through AddSub
Event Publisher
- Responsibilities: Reads domain events from event store, converts to generic events and publishes to event bus
- Subject Naming: Adopts serviceName.aggregateType.eventType naming convention, ensures routing consistency
Event Subscriber
- Responsibilities: Deserializes generic events to domain events, filters by service name/aggregate type then calls handlers
- Routing and Filtering: Filters by service name and aggregate type through subject encoding and parsing
Dependency Analysis
Module Dependencies
- StreamHub depends on JetStream publisher, subscriber and reader, forms event stream closed loop
- Event publisher depends on event store and event bus, achieves event persistence and distribution
- Event subscriber depends on event bus, achieves domain event type processing
- NATS configuration provides connection parameters for NATS event bus
Performance Considerations
Publish Path
- JetStream publisher performs serialization and Base64 encoding before publishing, recommend minimizing repeated serialization overhead at event store layer
- Batch publishing can reduce network round trips and control plane pressure
Subscribe Path
- JetStream subscriber uses explicit acknowledgment and persistent consumer, ensures reliability while needs to pay attention to acknowledgment delay
- Uses recover in consumption loop to prevent single point exception from causing overall crash
Read Path
- Reader adopts ephemeral consumer and batch Fetch, avoids control plane pressure from frequent creation/destruction
- Recommend combining rate limiting and backpressure strategies when replaying by version/offset
Memory Event Bus
- Asynchronously executes handlers during publish, avoids blocking publish thread; recommend setting timeout and isolation strategies for handlers
Troubleshooting Guide
Publishing Failure
- Check if JetStream publisher's stream exists and configuration is correct
- Verify if subject naming conforms to serviceName.aggregateType.eventType specification
Subscription Failure
- Confirm consumer creation and startup context not cancelled prematurely
- Check if handler execution throws exception, view Nak behavior in logs when necessary
Deserialization Failure
- Check if generic event payload is valid Base64 encoding
- Confirm event type matches handler generic constraints
Graceful Shutdown
- If Stop times out, check if handler is still processing or blocking; adjust timeout time or handler implementation when necessary
Memory Event Bus
- Returns error after closing when continuing to publish; confirm close process and concurrent access control
Conclusion
This design achieves consistent abstraction of local and remote event bus through unified event bus interface and StreamHub composition. JetStream publisher/subscriber/reader provides reliable event storage, subscription and replay capabilities, combined with event publisher and subscriber, builds complete chain from event storage to domain event processing. Through clear lifecycle management, graceful shutdown and error handling mechanism, the system has good foundation in reliability and maintainability.