Q) Order, inventory, and notification. Okay. They are running in a sequence, first order ends, then inventory, and then notification. If inventory fails, you have to roll back the order microservice also. Either all of them will go through or all of them will go back. Yeah. To maintain this type of transaction in microservice endpoint, what strategy or design pattern you will implement and how you will do it?
A)
Client
|
Order Service
|
Inventory Service
|
Notification Service
Orchestration
A central Saga Orchestrator controls the entire workflow.
Client
|
Saga Orchestrator
/ | \
/ | \
Order Inventory Notification
1. Create Order
✔
2. Reserve Inventory
❌ Failed
3. Orchestrator calls
CancelOrder()
Order status = Cancelled
Azure Implementation
In Azure, a typical implementation is:
- Order Service → Creates the order.
-
Azure Service Bus → Publishes
OrderCreatedevent. - Inventory Service → Consumes the event and reserves stock.
-
If reservation succeeds, it publishes
InventoryReserved. -
If reservation fails, it publishes
InventoryReservationFailed. - Saga Orchestrator (or Order Service in choreography) consumes the failure event and calls the Order Service to cancel the order.
- Notification Service is called only after all previous steps succeed.
To maintain transactions across multiple microservices, I would use the Saga Pattern. Each service performs a local transaction and, if a later step fails, executes a compensating transaction instead of a database rollback. In my project, I'd prefer the Orchestration Saga, where a Saga Orchestrator coordinates the workflow. For example, if the Order Service creates an order and the Inventory Service fails to reserve stock, the orchestrator invokes the Order Service's CancelOrder() API to compensate for the earlier step. Communication is typically asynchronous using Azure Service Bus, RabbitMQ, or Kafka. This approach keeps services loosely coupled and is the recommended pattern for distributed transactions in microservices.
I would implement the Saga using the Orchestration approach. I'd create a Saga Orchestrator as a separate service (or a workflow component) that coordinates the process. It first calls the Order Service to create an order with a Pending status. Then it calls the Inventory Service. If inventory reservation succeeds, it proceeds to the Notification Service and finally updates the order to Confirmed. If any service fails, the orchestrator invokes compensating APIs, such as CancelOrder(), to undo the previously completed step. This provides eventual consistency without using distributed database transactions
Step-by-step flow
-
Client sends a
PlaceOrderrequest. - The Saga Orchestrator calls the Order Service.
- Order Service creates the order with status Pending and returns success.
- The Saga Orchestrator calls the Inventory Service.
- If inventory reservation succeeds, the orchestrator calls the Notification Service.
- If all steps succeed, the orchestrator calls the Order Service again to update the order status to Confirmed.
If the Inventory Service fails:
-
The orchestrator immediately calls the Order Service's
CancelOrder()endpoint. - Order status changes from Pending to Cancelled.
- Notification Service is not called.
How it works
- The client sends a request to the Saga Orchestrator.
- The orchestrator calls the Order Service, which creates the order with a Pending status.
-
The orchestrator publishes an
OrderCreatedmessage to an SQS queue. - The Inventory Service reads the message from SQS and tries to reserve stock.
- If successful, it publishes a success message, and the orchestrator proceeds to the Notification Service.
- If inventory fails, it sends a failure message to another SQS queue (or a failure event).
-
The Saga Orchestrator receives the failure message and calls the Order Service's
CancelOrder()API as the compensating transaction.
HttpClientFactory. Polly provides configurable retry policies, exponential backoff, jitter, timeout, and circuit breaker support| Feature | SQS (Simple Queue Service) | SNS (Simple Notification Service) |
|---|---|---|
| Communication | Point-to-Point | Publish-Subscribe (Pub/Sub) |
| Message Delivery | One consumer processes one message | Multiple subscribers receive the same message |
| Purpose | Queue messages for processing | Broadcast messages to multiple services |
| Processing | Asynchronous | Asynchronous |
| Message Storage | Messages are stored until consumed | Messages are not stored for long-term processing by SNS itself |
| Consumers | Usually one consumer per message | Many subscribers (SQS, Lambda, HTTP endpoints, email, SMS, etc.) |
-
API Gateway
- Triggered when an HTTP/REST API request is received.
-
Example: A client calls
/orders, and API Gateway invokes a Lambda function.
-
Amazon SQS
- Lambda is triggered when new messages arrive in an SQS queue.
- Example: Process orders asynchronously from a queue.
-
Amazon SNS
- Lambda is triggered when a message is published to an SNS topic.
- Example: Send notifications or process events.
- Amazon S3
- Triggered when a file is uploaded, deleted, or updated.
- Example: Resize an uploaded image or process a PDF.
Workflow
- A file is uploaded to the S3 bucket.
- The S3 event triggers the Step Function.
- Step Functions invoke Lambda 1 to read the file.
- If successful, it invokes Lambda 2 to process the file.
- If successful, it invokes Lambda 3 to save the data to the database.
- If any Lambda fails, Step Functions execute a Catch block and invoke a compensating Lambda (rollback logic), such as deleting partially inserted records or updating the file status.
async/await and Multithreading| Async/Await | Multithreading |
|---|---|
| Used for I/O-bound operations | Used for CPU-bound operations |
| Does not create a new thread by itself | Creates or uses multiple threads |
| Improves application responsiveness | Improves parallel execution |
Uses Task and await | Uses Thread, Task.Run(), or the Thread Pool |
| Ideal for API calls, database calls, file I/O | Ideal for heavy calculations, image processing, encryption |
1. Transient (AddTransient)
- A new instance is created every time it is requested.
- Best for lightweight, stateless services.
Real-time examples:
- Email service
- SMS service
- PDF generation service
- Tax calculation service
- Validation service
builder.Services.AddTransient<IEmailService, EmailService>();
Example:
- User registration sends an email.
- Password reset sends another email.
-
Each request gets a new
EmailServiceinstance.
2. Scoped (AddScoped)
- One instance is created per HTTP request.
- All components within the same request share the same instance.
Real-time examples:
-
DbContext(Entity Framework Core) - Repository classes
- Unit of Work
builder.Services.AddScoped<AppDbContext>();
Example:
- An API request updates an order.
-
Controller, repository, and service all use the same
DbContextinstance during that request.
3. Singleton (AddSingleton)
- Only one instance is created for the entire application lifetime.
- Shared across all users and requests.
Real-time examples:
- Cache service
- Configuration service
- Logging service
- Feature flag service
builder.Services.AddSingleton<ICacheService, CacheService>();
Example:
- A cache stores product data.
- Every request uses the same cache instance instead of creating a new one.
How it works
- API A authenticates itself using its Client ID and Client Secret.
- The Identity Provider (Azure AD, AWS Cognito, Auth0, Okta, etc.) issues an access token.
- API A sends that token to API B.
-
API B validates:
- Token signature
- Issuer
- Audience
- Client ID (or application identity)
API B doesn't need to know the end user. It only verifies that API A is an authorized client.
Q) Two web application servers, Web application A, web application B. They are hosting an application like MVC or something, and they are working in a distributed. Both are isolated to each other in the form of VPC. There is no connection directly between them, in the form of VPC, APIs. You cannot pass data directly between. Eventually, A has access to SQL Server, B won't have. Now, as soon as you query some data from, select star from manager where employee_id equals 1. That data will be visible in A. But what I want that data should also be visible in B. So, the question is, how you will set up that indirect communication between A and B so that data can be sent in a real time.
A) The expected answer is message-based asynchronous communication.
How it works
- User updates data through Application A.
- A saves the data in SQL Server.
-
After a successful commit, A publishes an event such as:
-
ManagerUpdated -
EmployeeUpdated
-
- The event is sent to a message broker (SQS/SNS, Kafka, RabbitMQ, Azure Service Bus, etc.).
- Application B subscribes to the event.
- B processes the event and updates its own data store or cache.
- Both applications now have consistent data without direct communication.
No comments:
Post a Comment