Software Engineering, Architecture and AWS Serverless Technology from makit
Migrating from a Monolith to EDA on AWS
September 21, 2025

Migrating from a Monolith to EDA on AWS

Posted on September 21, 2025  •  5 minutes  • 1045 words
Table of contents

Migrating from a monolithic solution to an event-driven architecture (EDA) can be a daunting task, but it’s a journey that many organisations are now embarking on to enable greater agility, scalability, and innovation.

In this post, I’ll share practical insights and techniques based on a real customer migration I worked on. These lessons are broadly applicable, whether you’re running in AWS today or considering how to modernize your legacy workloads.

The Starting Point

The migration began with a real customer project: an online gaming company. The starting point was a large .NET Framework application running on Windows Servers with a SQL Server backend.

When a new customer registered, automated age verification checks needed to be run. This involved calling a third-party API, processing the result, and deciding whether to allow deposits or lock the account.

The entire flow was coded inside the registration endpoint, making synchronous calls to multiple internal classes and the third-party API. As you might guess, this led to tight coupling and limited flexibility.

Starting Point

The Goal

The vision was to break the application into domain-based services. Each service would own its own logic and data, reducing coupling and isolating business functionality.

For example, all logic around Age Verification could be encapsulated in its own service. That way, any change to verification (like integrating a new provider) would be localized to that single service.

The Goal

Introducing the Age Verification Service

We started by creating a standalone Age Verification Service, which:

The integration was done incrementally using several established patterns that I’ll walk through below.

Age Verification Service

Key Patterns for Migration

Strangler Fig

Coined by Martin Fowler, the Strangler Fig pattern involves slowly growing a new system around the edges of the old one until the monolith becomes obsolete.

We began by extracting the age verification process first, leaving the rest of the system unchanged. Over time, more functionality was peeled away until the monolith had little left.

Event Notification

We adopted the Event Notification pattern using Amazon EventBridge.

This approach decoupled services and enabled asynchronous behavior without forcing upstream systems to change.

Dark Launching

To test the new service safely, we ran it in dark launch mode.

We used reports to compare old vs. new outcomes, while debugging and tuning without customer impact.

Feature Toggles

We then introduced feature flags to remotely control whether the monolith or the new service was authoritative.

AWS offers simple ways to implement this using SSM Parameter Store, AppConfig, or you can use third-party tools like LaunchDarkly.

Once validated, we flipped the toggle to disable the monolith’s verification code and fully rely on the new service.

As as example, supposed you have code similar to this in the monolith:

def register(user_data):

    SaveUserToDatabase(user_data)

    IsVerified = HttpCallToAgeVerificationService(user_data)
    PersistAgeVerificationToDatabase(IsVerified, user_data)

A feature flag could be added like this, with the new event notification code firing at all times:

def register(user_data):

    SaveUserToDatabase(user_data)

    if not feature_flag_enabled(USE_NEW_AGE_VERIFICATION_SERVICE):
        IsVerified = HttpCallToAgeVerificationService(user_data)
        PersistAgeVerificationToDatabase(IsVerified, user_data)
	
    SendUserRegisteredEventNotification(user_data)

When ready the USE_NEW_AGE_VERIFICATION_SERVICE flag can be disabled and the monolith is now just sending the event, and no longer doing the Age Verification call itself.

Legacy Mimic

Finally, we needed to maintain compatibility. The monolith still expected verification results in its own database.

To solve this, we introduced a Legacy Mimic service:

This kept the monolith running without invasive changes, and the mimic was later decommissioned once the monolith retired.

Legacy Mimic

Challenges and Solutions

No migration was completed without major issues. We ran into several common problems though:

  1. Latency - Event-driven systems introduce delays compared to synchronous monolith calls. In our case, we designed database defaults to handle “verification pending” states.
  2. Race Conditions – Events can arrive out of order. Services need retries, idempotency, and sometimes fallback reads from a source of truth API.
  3. Idempotency – At-least-once delivery means duplicates happen. We used event IDs and deduplication checks in Amazon DynamoDB to prevent multiple offers being assigned. It’s important to test idempotency.
  4. Event Chaos – Without governance, events get messy. We built an Event Catalog documenting producers, consumers, and attributes to ensure consistency. This helps avoid events with inconsistent naming, duplicate events with different names, events that serve no purpose, or events with more than one responsibility.
  5. Observability – Logs, metrics, and tracing are critical. On AWS, this means CloudWatch Logs, X-Ray tracing, and metrics dashboards for latency, retries, and error rates.

The Final Solution

Here’s the final architecture at a high level:

  1. Customer registers in the monolith and this publishes a CustomerRegistered event to EventBridge.
  2. The Age Verification Service consumes the event, calls the third-party provider, and stores results in it’s local database.
  3. It then publishes AgeVerificationPassed/AgeVerificationFailed events.
  4. Legacy Mimic listens and updates the monolith database, keeping compatibility for dependent code.
  5. Over time, other domains (like address changes) also published events, enabling further decoupling.

Final architecture

Lessons Learned

Migrating to an event-driven architecture isn’t just about technology – it’s about patterns, testing strategies, and business trade-offs.

Some key takeaways:

Conclusion

Migrating from a monolith to an event-driven architecture on AWS is an evolution, not a big bang rewrite. By combining EventBridge, microservices, and proven migration patterns, you can deliver incremental business value while reducing risk.

Embrace the power of events, let your architecture evolve organically, and don’t be afraid to experiment.

Follow me

If you are interested in Coding, AWS, Serverless or ML