Technical
Event-Driven Architecture: Lessons from Enterprise IoT Transformation
When I joined Packsize, the software organization was struggling with a legacy monolith that couldn't scale. By leading the transformation to distributed, event-driven systems, we achieved a 15x increase in throughput while serving 3000+ IoT devices and 2000+ customers.
The Challenge: Legacy Systems at Scale
Our original system suffered from classic enterprise problems:
- Tight coupling: Changes in one area broke unexpected parts
- Deployment bottlenecks: All code had to deploy together
- Team dependencies: Multiple teams stepping on each other
- Limited scalability: The entire system scaled as one unit
More critically, we served industrial IoT devices requiring high reliability and regional deployment capabilities.
Domain-Driven Design Foundation
Before writing code, we mapped our business domains:
- Device Management: IoT device registration, monitoring, control
- Order Processing: Customer orders and fulfillment workflows
- Packaging Optimization: Box sizing and material calculation
- Customer Management: User accounts, permissions, billing
- Analytics: Data aggregation and business intelligence
Each domain became a bounded context with its own data model, business rules, and team ownership.
Event-Driven Architecture Patterns
Event Sourcing
Instead of storing current state, we stored the sequence of events that led to that state:
interface DeviceEvent {
deviceId: string;
eventType: 'REGISTERED' | 'ACTIVATED' | 'DEACTIVATED' | 'ERROR';
timestamp: Date;
payload: any;
}
This gave us:
- Complete audit trail: Every change is recorded
- Time travel: Replay events to any point in time
- Debugging: Understand exactly what happened
- Analytics: Rich data for business intelligence
CQRS (Command Query Responsibility Segregation)
We separated write operations (commands) from read operations (queries):
- Commands: Change state, published as events
- Queries: Read from optimized projections
- Projections: Materialized views built from events
Enterprise Event Bus
I built our enterprise event bus using Azure Service Bus, designed for:
- Reliability: Guaranteed message delivery with retry logic
- Scalability: Handle thousands of events per second
- Observability: Full tracing and monitoring of event flows
- Schema evolution: Backward-compatible event versioning
Implementation Strategy
Phase 1: Strangler Fig Pattern
We didn't rewrite everything at once. Instead, we gradually replaced functionality:
- Identify boundaries: Find natural seams in the monolith
- Extract services: Pull out well-defined capabilities
- Route traffic: Gradually shift load to new services
- Retire old code: Remove unused monolith pieces
Phase 2: Event-First Design
New features were built event-first:
- Define events: What happened in the business?
- Design aggregates: What entities produce these events?
- Build projections: What queries do we need?
- Implement handlers: How do we respond to events?
Phase 3: Cross-Cutting Concerns
We standardized infrastructure concerns:
- Service discovery: Consul for service registration
- Configuration: Centralized config management
- Monitoring: Distributed tracing with Application Insights
- Security: OAuth 2.0 with Azure AD integration
Technical Implementation
Event Schema Design
interface BaseEvent {
id: string;
aggregateId: string;
aggregateType: string;
eventType: string;
version: number;
timestamp: Date;
correlationId: string;
causationId: string;
}
interface DeviceRegisteredEvent extends BaseEvent {
eventType: 'DeviceRegistered';
data: {
deviceId: string;
customerId: string;
deviceType: string;
location: string;
};
}
Event Store Implementation
We used Azure Cosmos DB as our event store:
- Partitioning: By aggregate ID for optimal performance
- Consistency: Strong consistency within partition
- Scalability: Automatic scaling based on throughput
- Global distribution: Multi-region deployment
Message Processing
class EventHandler {
async handle(event: BaseEvent): Promise<void> {
try {
await this.processEvent(event);
await this.updateProjections(event);
await this.publishDownstreamEvents(event);
} catch (error) {
await this.handleError(event, error);
}
}
}
Operational Excellence
Monitoring and Observability
- Event flow visualization: Real-time event stream monitoring
- Performance metrics: Latency, throughput, error rates
- Business metrics: Devices processed, orders fulfilled
- Alerting: Proactive notification of issues
Deployment Strategy
- Blue-green deployments: Zero-downtime releases
- Feature flags: Gradual rollout of new functionality
- Rollback capability: Quick recovery from issues
- Regional deployment: Independent service deployment by region
Results: Transformational Performance
The transformation delivered exceptional results:
- 15x increase in boxes processed and staged
- Deployment lead time: From weeks to minutes
- Change failure rate: Reduced to under 5%
- Mean time to recovery: Under 15 minutes
- Regional deployment: Independent service deployment by region
- Team velocity: 3x increase in feature delivery
- System reliability: 99.9% uptime across all services
Lessons Learned
What Worked Well
- Domain-driven design: Clear boundaries reduced complexity
- Gradual migration: Strangler fig pattern minimized risk
- Event-first thinking: Natural decoupling and scalability
- Strong observability: Fast problem identification and resolution
What We'd Do Differently
- Start with events: Define event schemas before building services
- Invest in tooling: Better event visualization and debugging tools
- Team training: More upfront investment in event-driven patterns
- Testing strategy: Better integration testing for event flows
Getting Started with Event-Driven Architecture
If you're considering event-driven architecture:
- Start small: Pick one bounded context to experiment
- Define events: Focus on business events, not technical ones
- Build observability: You can't manage what you can't see
- Plan for failure: Design retry logic and error handling
- Invest in team skills: Event-driven thinking is different
Event-driven architecture isn't just a technical pattern—it's a way of thinking about business processes that leads to more resilient, scalable, and maintainable systems. The investment in transformation pays dividends in team velocity, system reliability, and business agility.
- architecture
- microservices
- event-driven
- azure
- iot