Modern enterprise applications increasingly rely on microservices to scale development, deployments, and operations. But splitting business logic into multiple services comes at a cost: data is now distributed across those services.
How do you gather all the data needed to render a single UI screen (like an order summary page) without overloading the client with dozens of calls?
Enter the API Composition Pattern — a design that aggregates data from multiple microservices into a single, unified API call, simplifying both client-side development and improving system performance.
In this blog, we’ll explore:
What is the API Composition Pattern?
Why and when should you use it?
How to implement it in .NET (with code!)
Best practices for real-world success
What is the API Composition Pattern?
The API Composition Pattern acts as a middle layer (or facade) that:
Aggregates data from multiple microservices.
Builds a single, unified response.
Reduces client-side complexity.
Instead of the client making multiple API calls to different services — Orders, Payments, Shipping — the API Composer orchestrates those calls on the server side and composes the final response.
Why Use API Composition?
✅ Simplified Client Logic — Clients call a single endpoint instead of multiple services.
✅ Better Performance — Server-side calls can be parallelized, reducing overall latency.
✅ Decoupling — Each microservice focuses on its domain; composition logic is centralized.
✅ Optimized Data Shape — Return exactly the data the client needs.
Implementing API Composition in .NET
Let’s walk through a practical example using an e-commerce scenario. Suppose we need to build an Order Summary API that returns:
Order details from
OrderService
Payment status from
PaymentService
Shipping status from
ShippingService
Step 1️⃣ — Define Data Contracts
https://dotnetfullstackdev.medium.com/
public class OrderSummaryDto
{
public OrderDto Order { get; set; }
public PaymentDto Payment { get; set; }
public ShippingDto Shipping { get; set; }
}
public class OrderDto { public int Id; public decimal Total; }
public class PaymentDto { public int OrderId; public string Status; }
public class ShippingDto { public int OrderId; public string TrackingNumber; }
Step 2️⃣ — Create API Composition Service
public class OrderSummaryService
{
private readonly HttpClient _httpClient;
public OrderSummaryService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<OrderSummaryDto> GetOrderSummaryAsync(int orderId)
{
var orderTask = _httpClient.GetFromJsonAsync<OrderDto>($"https://orderservice/api/orders/{orderId}");
var paymentTask = _httpClient.GetFromJsonAsync<PaymentDto>($"https://paymentservice/api/payments/{orderId}");
var shippingTask = _httpClient.GetFromJsonAsync<ShippingDto>($"https://shippingservice/api/shipping/{orderId}");
await Task.WhenAll(orderTask, paymentTask, shippingTask);
return new OrderSummaryDto
{
Order = await orderTask,
Payment = await paymentTask,
Shipping = await shippingTask
};
}
}
Step 3️⃣ — Expose a Controller Endpoint
[ApiController]
[Route("api/ordersummary")]
public class OrderSummaryController : ControllerBase
{
private readonly OrderSummaryService _summaryService;
public OrderSummaryController(OrderSummaryService summaryService)
{
_summaryService = summaryService;
}
[HttpGet("{orderId}")]
public async Task<ActionResult<OrderSummaryDto>> Get(int orderId)
{
var summary = await _summaryService.GetOrderSummaryAsync(orderId);
return Ok(summary);
}
}
How It Works
1️⃣ The client calls GET /api/ordersummary/1234
.
2️⃣ The OrderSummaryService
concurrently calls all dependent services: Order, Payment, and Shipping.
3️⃣ Once all responses arrive, the service composes a single DTO and returns it to the client.
Challenges and Considerations
🔸 Performance — For high-volume systems, ensure dependencies are fast. Use caching or partial composition if needed.
🔸 Error Handling — What if one service fails? Return partial results? Fail the request?
🔸 Consistency — Data might be slightly stale or inconsistent across services.
🔸 Security and Authorization — Ensure sensitive data is filtered based on user roles.
Best Practices
✅ Use parallel calls (Task.WhenAll
) to reduce latency.
✅ Implement circuit breakers (Polly) to handle failing services gracefully.
✅ Use caching for data that doesn’t change often.
✅ Log and monitor calls to detect slow dependencies.
✅ Keep composition logic separate from business logic — for example, using a dedicated API Gateway or BFF (Backend-for-Frontend).
Conclusion
The API Composition Pattern is a powerful strategy for building unified, user-friendly APIs in distributed .NET systems. By aggregating data on the server side, you:
Reduce client complexity
Improve performance
Keep your microservices focused and independent
In .NET, implementing API Composition is straightforward — using HttpClient, DTOs, and a single controller layer. Add resilience with Polly, and you’re ready to build reliable, scalable, and elegant distributed systems.
Can you make a tutorial about how to integrate Azure Peraonalizer in to dotnet API please ?