Back to The Vault

AuraFlow: What Happens Between Saving a Product and Sending Its Event?

Follow a product update through AuraFlow to understand event-driven services, the gap between two writes, and what retries and sagas need to get right.

August 10, 2025•Updated October 17, 2026•7 min read•

The product saved. Did everyone hear about it?

Imagine adding a product to a supply-chain application. The inventory service stores it successfully, then tries to announce the change. That second operation fails. The product exists, but downstream services may never learn about it.

This is the most useful place to enter AuraFlow. The project combines Spring Boot services, DynamoDB storage, SQS messages, and notification helpers. Its broader design explores event-driven supply-chain workflows. The concrete implementation gives us a small sequence we can inspect: save a product, publish an event, return a response.

We'll follow that sequence first, then use its failure window to understand why reliable event-driven systems need more than a queue. The saga discussion is a design extension; the published saga helper currently simulates starting and compensating a transaction.

One product, four steps

Follow the flow

From product request to response

Choose a step to see what happens and why it matters.

Spring Boot · product controller

A product request reaches the controller, where request-body validation runs before creation. Invalid input can be rejected before it changes storage.

The design lessonDefine what is allowed to enter the workflow before discussing how it scales.
Step 1 of 4

Here is the implemented controller path. Indigo receives and responds, slate holds inventory, and amber carries the event. The order of the arrows matters: storing the product and publishing its event are separate calls.

The source makes that sequence unusually easy to see. These are the three central lines from the creation endpoint, with validation and surrounding method code omitted:

productRepository.save(product);
eventPublisher.publishEvent("PRODUCT_CREATED", product.getId());
return ResponseEntity.status(HttpStatus.CREATED).body(product);

That is a dual write: one logical operation changes two systems. Seeing both calls in the same method doesn't make them one atomic transaction.

Why publish an event at all?

A direct call says, “Do this now.” An event says, “This happened.” Once a product has changed, another part of the application might need to update a report, send a notification, or prepare related work.

An event lets the inventory service describe the change without embedding every downstream action in the HTTP request. In AuraFlow, the publisher constructs a small JSON message containing eventType and productId, then sends it to SQS. The repository also contains shipment publishing and SNS notification helpers.

There is a useful limit to this description: those helpers and infrastructure definitions are building blocks. They do not by themselves establish a fully connected workflow with durable recovery. The inspected worker consumer prints a message, and its processor simulates whether input is nonempty.

Trade-off: Direct calls or asynchronous events?
Decision: Use an event when downstream work can proceed independently

A direct call makes completion easy to follow but couples the request to the next service's availability. A queue creates a handoff and a place to wait, while introducing message state, duplicate handling, and delayed outcomes that the application must explain.

The gap a queue cannot close

Return to the failure after the product save. The controller has no single transaction spanning DynamoDB and SQS. If publication fails, the database change may already exist even though the client does not receive the expected success response.

Pause and predict: should the client simply repeat the request?

Repeating might create another change or publish another event, depending on the identifiers and write behavior. Before treating a retry as safe, define the intended final state. A stable operation identifier and idempotent effects help distinguish a repeated delivery from a genuinely new operation.

One possible extension is a transactional outbox: commit a change and a record of the event to send within the same local transaction, then have a separate publisher deliver pending records. That design moves the failure problem into a durable, inspectable publication process. It is a proposed extension here, not a claim about AuraFlow's current controller.

Even then, the publisher can send an event and fail before marking it sent. Consumers still need to handle duplicates. Reliability comes from the entire chain of decisions, not one architectural label.

A retry and a compensation answer different questions

A retry asks, “Can we attempt this action again?” A compensation asks, “How do we address an earlier action that already succeeded?”

For an illustrative supply-chain order, reserving stock might succeed before shipment creation fails. Retrying shipment creation may be appropriate for a temporary problem. Releasing the reservation may be appropriate if the order cannot proceed. That release is another business action; it isn't time travel that erases every effect of the reservation.

This is the idea behind a saga: local steps, recorded progress, and deliberate compensating actions. AuraFlow's SagaManager exposes start and compensate methods, but their inspected bodies log messages and return success. A durable saga would additionally need persistent progress, defined recovery policies, and handling for compensation failures.

FailureFirst question to answer
Event publication fails after a saveWhere is the intent to publish recorded?
A message arrives twiceWhich identifier makes the effect safe to repeat?
A later business step failsShould it retry or trigger compensation?
Compensation also failsWho records and resumes recovery?

What the performance number actually describes

The repository README reports roughly 40,000 events per minute from a local DynamoDB benchmark: 10,000 events in about 15 seconds. It explicitly describes this as local event-sourcing throughput.

That is a narrower claim than an end-to-end AWS deployment sustaining the same rate through inventory changes, SQS, worker processing, and notifications. It doesn't establish an uptime or delivery guarantee. A useful benchmark story names the stage measured before presenting the number.

The same care applies to the autoscaling and error-handling helpers: the inspected versions simulate behavior with log messages. They're places to develop operational policies, not evidence that those policies have already run under production load.

What I would test next

Force publication to fail after a successful product save. Deliver the same event twice. Stop a consumer after its side effect but before completion is recorded. Then ask whether the application can tell the difference between work that never happened, work that happened once, and work whose outcome is uncertain.

Those are proposed validation cases. They help turn a collection of services into a workflow with promises a user can understand.

The takeaway from AuraFlow is the boundary between an action, the event describing it, and the work that follows. Once those are explicit, choices about queues, retries, and sagas become much less mysterious.

Explore the implementation

Source reviewed at revision f0aabb1:

Cite this page
Kapoor, Saksham. "AuraFlow: What Happens Between Saving a Product and Sending Its Event?." The Vault (blog). August 10, 2025. https://saksham-kapoor.vercel.app/vault/architecting-event-driven-microservices

Related Posts

Press ⌘+K to search