# Event-Driven Architecture Proof-of-Concept
## Overview
We were discussing different strategies we might use to support an event-driven architecture. Currently JSON representations of our incoming orders land in an AWS S3 bucket. The current system uses the S3 objects as a sort of log and history.
## A Potential Architecture
Since we already have orders landing in S3, we can use these incoming orders as a trigger for our processing. S3 can send an [S3 Event Notification](https://docs.aws.amazon.com/AmazonS3/latest/userguide/NotificationHowTo.html) messages for a variety of bucket events. In this case we want notifications when new objects are put into the bucket.
![[event-driven_architecture_poc.png]]
The general activity flow is:
1. Incoming notifications are processed by the `S3Notification` Lambda function. The function reads some order data from the S3 bucket and constructs a message that contains enough data so that other system resources can determine whether they need to react to the events. Notice that the event payload below contains `Metadata` as suggested by [[Luc van Donkersgoed The Tao of Event-Driven Architectures|Luc van Donkersgoed]]. The payload also contains `service_info` and `payment_type` which can be used by Amazon EventBridge rules in order to react to events.
```{
"Metadata": {
"event_time_iso8601": "2023-05-12T05:02:13.128510z",
"event_type": "OrderCreated",
"event_version": "1.0",
"event_id": "0e3c948f-06ec-4a4d-b3db-5aacd2a63631"
},
"Data": {
"original_object": {
"bucket": "production-consumer-api-orders-log",
"key": "Dr541r357e/doordash/Dr541r357e_doordash_47bd6709-613e-4b05-a938-4ee4eb2f8dac_20230512_050211.json"
},
"order_id": "47bd6709-613e-4b05-a938-4ee4eb2f8dac",
"prepare_by_datetime": "2023-05-12T05:30:53Z",
"callback_url": null,
"provider": null,
"service_info": "delivery",
"payment_type": "pre_paid"
}
}
2. After the event has been posted to Amazon EventBridge the EventBridge rules control the routing of the events to the appropriate processing function. An EventBridge rule for `OrderCreated`, for example, is routed to the `NewOrder` Lambda function. The rule is very simple in this case:
```
{
"detail-type": ["new-order"],
"detail": {
"Metadata": {
"event_type": ["OrderCreated"]
}
}
}```
3. The `NewOrder` Lambda function receives the event and adds the `Order` data to an AWS DynamoDB table. This function then posts an `OrderAccepted` event. The event looks like:
```
{
"Metadata": {
"event_time_iso8601": "2023-05-12T05:02:13.723716z",
"event_type": "OrderAccepted",
"event_version": "1.0",
"event_id": "8bb74637-760b-4808-9caa-28920bab756a"
},
"Data": {
"original_object": {
"bucket": "production-consumer-api-orders-log",
"key": "Dr541r357e/doordash/Dr541r357e_doordash_47bd6709-613e-4b05-a938-4ee4eb2f8dac_20230512_050211.json"
},
"order_id": "47bd6709-613e-4b05-a938-4ee4eb2f8dac",
"prepare_by_datetime": "2023-05-12T05:30:53Z",
"callback_url": null,
"provider": null,
"service_info": "delivery",
"payment_type": "pre_paid"
}
}```
4. The EventBridge rules to route `OrderAccepted` events get a little more interesting. Now we are deciding whether we need to `ArrangeDelivery` or go directly to `CapturePayment`. We will need to arrange delivery if the `service_info` in the event is "self_delivery". Here is what the rule looks like:
```
{
"detail-type": ["order-accepted"],
"detail": {
"Metadata": {
"event_type": ["OrderAccepted"]
},
"Data": {
"service_info": ["self_delivery"]
}
}
}```
- In contract, if the `service_info` is "delivery" then the rule can target the `CapturePayment` Lambda function:
```
{
"detail-type": ["order-accepted"],
"detail": {
"Metadata": {
"event_type": ["OrderAccepted"]
},
"Data": {
"service_info": ["delivery"]
}
}
}```
5. `ArrangeDelivery` can do its work and then post an event that `OrderDeliveryArranged` event has happened.
```
{
"Metadata": {
"event_time_iso8601": "2023-05-12T06:07:43.878430z",
"event_type": "OrderDeliveryArranged",
"event_version": "1.0",
"event_id": "20f5149e-ac4d-464e-9c3c-c64db38b99a4"
},
"Data": {
"original_object": {
"bucket": "production-consumer-api-orders-log",
"key": "ConKa584a672l/olo/ConKa584a672l_olo_1ecbc2b4-d82d-45f4-a59a-f84350526751_20230512_060740.json"
},
"order_id": "1ecbc2b4-d82d-45f4-a59a-f84350526751",
"prepare_by_datetime": "2023-05-12T06:22:00Z",
"callback_url": null,
"provider": "doordash",
"service_info": "pick_up",
"payment_type": "pre_paid"
}
}
```
6. An Amazon EventBridge rule see the event and targets the `CapturePayment` Lambda function.
### Event Logging
Amazon EventBridge supports the ideas of archive and replay of events. The [Amazon documentation](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-archive.html) explains more details.
Additionally, it seemed like it would be helpful to store the event history along with the order data in the DynamoDB table. Here is how we can group the order and the events for single-query access
![[Pasted image 20230512002509.png]]
### X-Ray Tracing
Because we are using native AWS resources we can benefit easily from AWS X-Ray.
#### Trace with `ArrangeDelivery`
![[Pasted image 20230512002808.png]]
#### Trace without `ArrangeDelivery`
![[Pasted image 20230512002836.png]]
### What about idempotency?
S3 Event Notifications may generate seemingly duplicate messages for a given object. However, the byte counts per key were different so the messages were not exactly the same message (and they had different AWS X-Ray trace ids). As a result, S3Notification needed not to reprocess duplicate messages which it could do by using the `key` as the unique id. We used the [@idempotent_function](https://awslabs.github.io/aws-lambda-powertools-python/2.14.1/utilities/idempotency/#idempotent_function-decorator) function decorator from [AWS Lambda Powertools](https://awslabs.github.io/aws-lambda-powertools-python/2.14.1/)
to ignore repeat messages.
### X-Ray Complications
When the S3 Event Notification directly invokes the Lambda function (S3Notification) then the *system does* not successfully pass AWS X-Ray trace ids through the whole system. When the S3 Event Notification posts a message to an AWS API Gateway endpoint the invokes the Lambda function (S3Notification) then the system *does* successfully pass AWS X-Ray trace ids through the whole system.
---
Created: 2023-05-11 22:39