When we create a software, we try to satisfy most (if not all) requirements that are critical to the business. But we all know there will always be new requirements (sometimes a very surprising one) that will make us scratch our head in order to implement. It doesn’t really matter whether we have a big monolith or microservices, we don’t want to clog up our application servers with actions that are not crucial to the core business activity. For example, if you click a button to book a flight, there are basic things the system must do at the transaction time. To name a few:
Charge the credit card (requires integration with a financial institution)
Make the actual booking (requires integration with a travel fulfiller)
Create transactional records (for reporting, invoicing etc.)
Send an email to the customer
I will keep the list short for the sake of simplicity, but you can guess how complicated it may get. From a business perspective, you will have to decide what a booking means in the most granular term for your business or what makes it incomplete. There is no right or wrong answer here but there is a common ground to which you will come closer. Most people would say #1 is crucial because this is the way business makes money. #2 is crucial as this is how the customer will board the airplane. #3 is crucial because this is the backbone of your system for bookkeeping, issue management etc. You may argue that #4 is also important but hey, maybe the email could drop in customer’s inbox a few minutes later.
Ok, so how will we implement it? I’ll break down below different parts of such a system in an event driven architecture using AWS services:
Message publisher(s): your applications will produce events (like the booking above) and publish the event messages. This is about notifying the system that something interesting did just happen. In our case we’ve made a custom NPM package that publishes simple AWS SNS messages for all (or filtered) http requests that our server replies. This way any incoming request (any user actions) gets the ability to produce an event in the system.
Message broker(s): The broker filters and routes the messages to the appropriate listeners (or subscribers). AWS SNS is one of the most straightforward answers in the Amazon world to build an application that reacts to high throughput and low latency events published by other applications. With SNS, you can route your messages to all subscribers (including Amazon SQS queues, AWS Lambda functions, HTTPS endpoints, and Amazon Kinesis Data Firehose) which would create a basic fan-out implementation. Alternatively, you can also do topic or attribute-based filtering for routing your messages to specific subscribers. I know that it sounds very tempting, however filtering your messages (using topics or attribute policies) can lead to very complicated rules that are hard to maintain in a real-world scenario. Plus, I don’t want to change a property in my infrastructure every time I need to make a change in event processing requirements. In most of the cases, I tend to do a fanout and inspect the messages in the workers using a NPM library that I built to filter-out the messages.
Recipient(s): For easy throttling and delivery reliability we added AWS SQS in-between our worker applications and SNS. In other words, SNS sends the event messages to SQS queues, and the worker apps listen to the SQS messages for event processing. This also helps with scaling because SQS is ultimately scalable and if you need to process more messages per second, all you need to do is to fire up another worker server and let it fetch messages from SQS.
In such a system, you may easily get lost trying to trace a transaction going back and forth between layers, so you will want to have a neat logging and tracing ability. You may find more information about logging in this post.
In the example above, there are still a few things you will want to do after the time of transaction:
- Check the airline system (approximately) 5 minutes after the time of purchase (In some cases airlines systems create important information within first few minutes after you make a booking (like a free-upgrade, free lounge for corporate clients, last ticketing date and so on))
- Send a reminder email to the customer prior the flight (generally 24h before)
- Let’s add some more fun and imagine that this is a corporate purchase and the client wants bi-weekly invoices with the total amount of transactions that occurs in 2-week time frame.
These requirements go in the territory of deferred and batch executions, which I explain in the next post.