Spring Cloud微服务系统中Axon事件溯源落地全流程咨询
Hey there! Let's walk through how to implement Axon Framework with Event Sourcing in your Spring Cloud microservices setup— I’ll cover all your questions, from core configuration to aggregate design, plus point you to fresh, up-to-date examples since the old GitHub ones are too dated.
1. Core Architecture Overview
First, let’s align on the big picture: Axon ties together command routing (via distributed command bus), event propagation (across services via event bus/MQ), and event storage (for event sourcing) in Spring Cloud. Each microservice will own its aggregates, handle commands, publish events, and subscribe to events from other services to update read models or trigger workflows.
2. Step-by-Step Configuration
2.1 Distributed Command Bus (DCB)
Axon’s DCB uses Spring Cloud Service Discovery (like Eureka) to route commands to the correct service that owns the target aggregate.
Dependencies
Add these to your pom.xml (use the latest stable Axon version, e.g., 4.9.x):
<!-- Axon Spring Cloud integration --> <dependency> <groupId>org.axonframework</groupId> <artifactId>axon-spring-cloud-starter</artifactId> <version>4.9.0</version> </dependency> <!-- Spring Cloud Eureka Client (or your preferred service discovery) --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
Application Configuration (application.yml)
axon: distributed: enabled: true command-bus: spring-cloud: enabled: true discovery-client: eureka routing-strategy: consistent-hash # Use round-robin for simple load balancing spring: application: name: order-service # Unique name for each microservice eureka: client: service-url: defaultZone: http://localhost:8761/eureka/
Why consistent-hash? It ensures commands targeting the same aggregate always go to the same service instance, avoiding concurrency issues.
2.2 Event Bus & MQ Integration
For cross-service event propagation, you have two solid options:
Option 1: Axon Server (Recommended)
Axon Server is the official, purpose-built tool for Axon— it handles event storage, command routing, and event propagation out of the box, so you don’t need a separate MQ.
Dependencies
<dependency> <groupId>org.axonframework</groupId> <artifactId>axon-server-connector-spring-boot-starter</artifactId> <version>4.9.0</version> </dependency>
Configuration
axon: server: enabled: true servers: axon-server:8124 # Your Axon Server instance address
Axon Server automatically syncs events across all services and provides a dashboard to monitor event streams and command traffic.
Option 2: Kafka/RabbitMQ as Event Bus
If you prefer using an existing MQ, Axon integrates seamlessly with Kafka or RabbitMQ. Here’s a Kafka example:
Dependencies
<dependency> <groupId>org.axonframework</groupId> <artifactId>axon-kafka-spring-boot-starter</artifactId> <version>4.9.0</version> </dependency>
Configuration
axon: eventhandling: processors: default: mode: tracking # Persists processing progress, critical for event replay kafka: producer: bootstrap-servers: localhost:9092 consumer: bootstrap-servers: localhost:9092 group-id: ${spring.application.name} # Each service gets its own consumer group
2.3 Data Source Configuration for Event Store
If you’re not using Axon Server, you’ll need to configure a custom event store (e.g., JDBC/MongoDB). Here’s a JDBC setup with MySQL:
Dependencies
<dependency> <groupId>org.axonframework</groupId> <artifactId>axon-jdbc</artifactId> <version>4.9.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
Configuration Class
@Configuration public class AxonJdbcConfig { @Autowired private DataSource dataSource; @Bean public EventStorageEngine eventStorageEngine(Serializer serializer) { return JdbcEventStorageEngine.builder() .dataSource(dataSource) .serializer(serializer) .build(); } }
Application Configuration
spring: datasource: url: jdbc:mysql://localhost:3306/axon_event_store username: root password: your-password driver-class-name: com.mysql.cj.jdbc.Driver
Axon will auto-create the required tables (domain_event_entry, snapshot_event_entry) to store events.
3. Microservice Event Storage Mechanism
You have two event storage strategies to choose from:
- Centralized Event Store: Use Axon Server as a single source of truth for all events. This simplifies cross-service event queries and replay, and is ideal for most use cases.
- Distributed Event Store: Each microservice maintains its own event store (JDBC/Mongo). Great for strict data isolation, but requires additional work to build cross-service read models (e.g., using Axon Query Bus or projecting events to a shared database).
Snapshotting (Critical for Performance)
To avoid replaying hundreds/thousands of events when loading an aggregate, enable snapshotting:
axon: snapshot: trigger: threshold: 50 # Take a snapshot after 50 events per aggregate
You can also customize snapshot triggers with the @SnapshotTriggerDefinition annotation on your aggregate class.
4. Aggregate Object Generation Logic
Aggregates are the heart of event sourcing— their state is entirely built by replaying past events. Follow these rules:
- Annotate the Aggregate: Use
@Aggregatefor the class,@AggregateIdentifierfor the unique ID field. - Command Handlers: Use
@CommandHandlerto handle commands and publish events viaAggregateLifecycle.apply(). - Event Sourcing Handlers: Use
@EventSourcingHandlerto update the aggregate’s state when events are applied. - No-Arg Constructor: Required for Axon to reconstruct the aggregate from events.
Example Aggregate
@Aggregate public class OrderAggregate { @AggregateIdentifier private String orderId; private OrderStatus status; // Required for event sourcing reconstruction protected OrderAggregate() {} @CommandHandler public OrderAggregate(CreateOrderCommand command) { // Validate command input if (StringUtils.isEmpty(command.getOrderId())) { throw new IllegalArgumentException("Order ID cannot be empty"); } // Publish event to trigger state change AggregateLifecycle.apply(new OrderCreatedEvent( command.getOrderId(), command.getProductId(), command.getQuantity() )); } @EventSourcingHandler public void on(OrderCreatedEvent event) { this.orderId = event.getOrderId(); this.status = OrderStatus.CREATED; } @CommandHandler public void handle(ConfirmOrderCommand command) { if (!status.equals(OrderStatus.CREATED)) { throw new IllegalStateException("Only created orders can be confirmed"); } AggregateLifecycle.apply(new OrderConfirmedEvent(command.getOrderId())); } @EventSourcingHandler public void on(OrderConfirmedEvent event) { this.status = OrderStatus.CONFIRMED; } }
When you load an aggregate, Axon fetches all its events from the event store and runs them through the @EventSourcingHandler methods to rebuild the current state.
5. Up-to-Date Example Projects
Since the old GitHub examples are outdated, here are reliable ways to get started:
- Official Axon Samples: The Axon Framework repo has a
samplesdirectory with aspring-cloud-sampleproject (using 4.9.x) that demonstrates distributed command routing, Axon Server integration, and cross-service event handling. - Custom Multi-Service Demo: Build two simple services:
order-service: Handles order commands, publishes order events.inventory-service: Subscribes to order events to update inventory levels.
Use Axon Server for event storage and propagation, plus Eureka for service discovery.
6. Key Best Practices
- Keep Aggregates Small: Each aggregate should own a single business boundary (e.g., Order, InventoryItem) to avoid bloated event streams.
- Use Event Upcasters: When you need to change event schemas, use upcasters to convert old events to new formats without breaking event replay.
- Test Event Sourcing: Write tests that verify aggregate state is correctly rebuilt from events, and that commands produce the expected events.
- Monitor Event Processing: Use Axon Server’s dashboard or Spring Actuator metrics to track event latency and failures.
内容的提问来源于stack exchange,提问作者Mavlarn

