Skip to main content

API Management & Messaging Patterns

Learn to design robust APIs and event-driven systems using Azure’s messaging services. Azure Messaging Architecture

What You’ll Learn

By the end of this chapter, you’ll understand:
  • What APIs and messaging are (and why they’re different from direct function calls)
  • How services communicate (sync vs async, when to use each)
  • Azure API Management (protecting and managing your APIs)
  • Service Bus vs Event Hub (and why choosing wrong costs money)
  • Event-driven architectures (Saga pattern, Event Sourcing)
  • Real-world patterns with actual costs and trade-offs

Introduction: What Are APIs and Messaging?

Start Here if You’re Completely New

API = How one application talks to another application Messaging = Sending messages between applications asynchronously Think of it like communication methods: API (Synchronous) = Phone Call
You: "Hey, what's the weather?"
Friend: *immediately responds* "It's sunny!"
You: *wait on the line until friend responds*

Characteristics:
- Immediate response (or you wait)
- Blocking (can't do anything else while waiting)
- If friend doesn't answer = you're stuck ❌
Messaging (Asynchronous) = Text Message
You: *send text* "Hey, what's the weather?"
You: *continue doing other things*
Friend: *responds 10 minutes later* "It's sunny!"
You: *see message when convenient*

Characteristics:
- Delayed response (you don't wait)
- Non-blocking (you continue working)
- If friend is offline = message waits in queue ✅

Why This Matters: The Cost of Tight Coupling

Real-World Failure Example

Target Data Breach (2013)
  • Bad Architecture: All systems directly connected (no API gateway, no security layer)
  • What happened: Hackers accessed HVAC system → Used it to access payment system
  • Result: 40 million credit cards stolen
  • Cost: $292 million
  • Prevention: API Gateway with authentication + network segmentation
  • Prevention cost: ~$500,000
  • ROI: 584x return on investment ✅
More Examples:
CompanyProblemCostSolution
Knight Capital (2012)No message queue for orders$440M (bankruptcy)Service Bus queue
Amazon (2018)Synchronous API calls (cascading failure)$13M lost revenueAsync messaging + circuit breakers
Uber (2017)No event sourcing (data corruption)100+ hours fixingEvent Hub for audit trail
The Pattern: Proper APIs and messaging cost thousands. Tight coupling costs millions.

Synchronous vs Asynchronous Communication (From Scratch)

Let’s understand the fundamental difference:

Synchronous (API Calls) - “Phone Call” Model

Frontend Application
  ↓ HTTP POST /orders
Order API (waits for response...)
  ↓ HTTP POST /charge
Payment API (processing...)
  ↓ (3 seconds later)
Payment API → "Success!"

Order API → "Order created!"

Frontend → "Thank you for your order!"

Total Time: 3+ seconds (frontend waits entire time) ⏱️
If Payment API is down → Order API fails → Frontend shows error ❌
When to Use Synchronous:
  • ✅ Need immediate response (user login)
  • ✅ Short operations (<1 second)
  • ✅ User is waiting for result
  • ✅ Simple request/response
When NOT to Use:
  • ❌ Long operations (video encoding, report generation)
  • ❌ External dependencies that might be slow
  • ❌ Operations that can fail frequently

Asynchronous (Messaging) - “Text Message” Model

Frontend Application
  ↓ HTTP POST /orders
Order API
  ↓ Send message to queue "new-order"
Order API → "Order received! We'll process it."

Frontend → "Order submitted! Check email for confirmation."

Total Time: 50ms (frontend gets immediate response) ✅

Meanwhile (in the background):
Service Bus Queue: "new-order"
  ↓ (Worker picks up message)
Payment Service (processing... 3 seconds)
  ↓ (Publishes event "payment-successful")
Email Service (listening for events)
  ↓ Sends confirmation email

User experience: Instant response + email arrives 5 seconds later ✅
If Payment Service is down → Message stays in queue → Retries later ✅
When to Use Asynchronous:
  • ✅ Long operations (>1 second)
  • ✅ Don’t need immediate response
  • ✅ Need reliability (retries, durability)
  • ✅ High throughput (millions of operations)
  • ✅ Decoupling services (payment fails ≠ order API fails)
Real-World Example: E-commerce checkout
Synchronous (Bad):
User clicks "Checkout"
  → Create order (50ms)
  → Charge payment (3s)
  → Update inventory (500ms)
  → Send confirmation email (2s)
  → Generate invoice PDF (5s)
Total: 10.5 seconds (user waits) ❌

Asynchronous (Good):
User clicks "Checkout"
  → Create order (50ms)
  → Send "order-created" message
  → Return "Order submitted!" ✅
Total: 50ms (user sees confirmation)

Background workers process:
  → Charge payment (3s)
  → Update inventory (500ms)
  → Send email (2s)
  → Generate PDF (5s)
User gets email 10 seconds later (but didn't wait) ✅

Common Mistake: Using Wrong Communication Method

Mistake #1: Synchronous for Long Operations

The Trap:
User uploads 1 GB video
  ↓ HTTP POST /videos/upload
API receives video
  ↓ (Encodes video... 10 minutes)
  ↓ HTTP times out after 30 seconds ❌
User sees error ❌
The Fix (Asynchronous):
User uploads 1 GB video
  ↓ HTTP POST /videos/upload
API receives video → Sends message to queue
  ↓ Returns immediately: "Video uploaded! Processing..."
User sees: "We're processing your video" ✅

Background worker:
  ↓ Picks up message from queue
  ↓ Encodes video (10 minutes)
  ↓ Sends notification when done
User gets email: "Video is ready!" ✅
Cost Impact:
  • Synchronous: 90% of uploads fail (timeout) → Lost users
  • Asynchronous: 99.9% success rate → Happy users ✅

Mistake #2: Asynchronous for User Login

The Trap:
User enters username/password
  ↓ Sends message to queue "login-requests"
User sees: "Login request submitted! Wait for confirmation email"
  ↓ (10 seconds later)
User receives email: "You're logged in!" ❌

Problem: User expects immediate response ❌
The Fix (Synchronous):
User enters username/password
  ↓ HTTP POST /auth/login
Auth API validates (50ms)
  ↓ Returns JWT token immediately
User is logged in instantly ✅
Decision Tree: Sync or Async?
Q1: Does user need immediate response?

├─ Yes (login, search, view page)
│  └─ Use Synchronous (API call)

└─ No (upload, report generation, batch processing)
   ├─ Operation < 5 seconds
   │  └─ Use Synchronous (API call with longer timeout)

   └─ Operation > 5 seconds
      └─ Use Asynchronous (Message queue)

Understanding Azure’s Messaging Services (Simplified)

Azure has 3 main messaging services. Here’s how to choose:

The Restaurant Analogy

Azure Service Bus = Restaurant Order Ticket
Customer orders food

Waiter writes ticket → Hangs on kitchen board

Chef takes ticket (one chef processes one ticket)

Chef makes food → Marks ticket complete

Characteristics:
- Guaranteed delivery (ticket doesn't disappear)
- FIFO order (first ticket processed first)
- One chef processes each ticket
- If chef is busy → Ticket waits
- Critical for: Money transactions, orders
Use Service Bus When:
  • ✅ Need guaranteed delivery (can’t lose messages)
  • ✅ Need order preservation (FIFO)
  • ✅ Critical business operations (orders, payments)
  • ✅ Need transactional guarantees
Cost: 10/month+10/month + 0.05 per million operations
Azure Event Hub = Security Camera Footage
100 cameras recording 24/7

Millions of video frames per hour

Multiple systems watching footage:
  ├─ Security team (watches live)
  ├─ AI system (detects motion)
  └─ Archive system (stores for 90 days)

Characteristics:
- High throughput (millions of events/second)
- Multiple consumers read same data
- No guaranteed FIFO (partitioned)
- Lossy (if consumer is slow, events can be skipped)
- Critical for: Telemetry, logs, analytics
Use Event Hub When:
  • ✅ Need high throughput (millions of events)
  • ✅ Multiple consumers need same data
  • ✅ Telemetry and logging
  • ✅ OK to lose occasional event (not critical)
Cost: 11/month+11/month + 0.028 per million events
Azure Event Grid = Building Fire Alarm
Fire detected

Fire alarm triggers

Notifies multiple systems instantly:
  ├─ Fire department (sends trucks)
  ├─ Building management (evacuates)
  └─ HVAC system (shuts down)

Characteristics:
- Event-driven (reacts to events)
- Pub/sub (multiple subscribers)
- Serverless (no infrastructure)
- Low latency (&lt;1 second)
- Critical for: Automation, serverless workflows
Use Event Grid When:
  • ✅ React to Azure resource events (blob uploaded, VM created)
  • ✅ Serverless workflows (trigger Azure Functions)
  • ✅ Simple event routing
  • ✅ Need low latency
Cost: $0.60 per million events (cheapest!)

Decision Matrix: Which Service?

NeedServiceExample
Process orders reliablyService Bus QueueE-commerce orders, payment processing
Broadcast event to multiple servicesService Bus TopicOrder created → Email, Inventory, Analytics
High-volume telemetryEvent HubIoT sensor data, application logs, clickstream
React to Azure eventsEvent GridBlob uploaded → Trigger Azure Function
Complex workflows with compensationService Bus + SagaMulti-step transactions with rollback
Event sourcing / audit trailEvent HubBanking transactions, compliance logging

Real-World Cost Example: Choosing Wrong Service

Scenario: IoT application with 1,000 devices sending data every second Option 1: Service Bus (WRONG)
1,000 devices × 1 message/second = 1,000 msg/sec
1,000 msg/sec × 86,400 sec/day = 86.4 million msg/day
86.4 million × 30 days = 2.59 billion msg/month

Cost:
- Base: $10/month
- Operations: 2,590 million × $0.05 / million = $129.50/month
Total: $140/month

Problems:
- Service Bus throttles at high throughput ❌
- Not designed for streaming data ❌
- Expensive for this use case ❌
Option 2: Event Hub (CORRECT)
Same volume: 2.59 billion events/month

Cost:
- Base: $11/month
- Ingress: 2,590 million × $0.028 / million = $72.52/month
Total: $83.52/month ✅

Benefits:
- Designed for high throughput ✅
- 41% cheaper ($56.48 savings/month)
- Better performance ✅
Lesson: Using wrong service costs 70% more + worse performance!
[!TIP] Jargon Alert: API Gateway vs Service Mesh API Gateway (like Azure API Management) sits at the edge and handles external traffic—rate limiting, authentication, versioning. Service Mesh (like Istio) sits between microservices and handles internal traffic—retries, circuit breaking, observability.
[!WARNING] Gotcha: Service Bus vs Event Hub Confusion Choosing wrong can cost you! Service Bus = reliable messaging with FIFO guarantees (order processing). Event Hub = high-throughput streaming for telemetry. Using Service Bus for telemetry = expensive and slow. Using Event Hub for orders = lost data!

1. Azure API Management (APIM)

Azure API Management is a fully managed service to publish, secure, transform, maintain, and monitor APIs.

When to Use APIM

✅ Use APIM For

  • External API exposure
  • Rate limiting and quotas
  • API versioning and monetization
  • Request/response transformation
  • OAuth/JWT validation
  • Developer portal

❌ Don't Use APIM For

  • Internal microservice communication (use Service Mesh)
  • Simple reverse proxy (use App Gateway)
  • Real-time streaming (use Event Hub)
  • High-latency tolerance apps (adds ~50ms)

APIM Architecture


2. APIM Core Concepts

API Gateway Policies

Policies are XML configurations that execute on API requests/responses.
<!-- Limit to 100 calls per minute per subscription -->
<policies>
    <inbound>
        <rate-limit calls="100" renewal-period="60" />
    </inbound>
</policies>

<!-- Advanced: Different limits per product -->
<policies>
    <inbound>
        <choose>
            <when condition="@(context.Product.Name == "Premium")">
                <rate-limit calls="1000" renewal-period="60" />
            </when>
            <when condition="@(context.Product.Name == "Basic")">
                <rate-limit calls="100" renewal-period="60" />
            </when>
        </choose>
    </inbound>
</policies>

API Versioning Strategies

Recommended: Most explicit and discoverable
GET https://api.contoso.com/v1/products
GET https://api.contoso.com/v2/products

Pros:
✅ Clear and explicit
✅ Easy to cache
✅ Works with all clients

Cons:
❌ Pollutes URI space

3. Azure Service Bus

Service Bus is a fully managed enterprise message broker with queues and publish-subscribe topics.

Service Bus vs Event Hub vs Event Grid

The Decision Tree:

Comparison Table

FeatureService Bus QueueService Bus TopicEvent HubEvent Grid
PatternPoint-to-pointPub/SubStreamingEvent routing
Message Size256 KB (std)
100 MB (premium)
256 KB (std)
100 MB (premium)
1 MB1 MB
ThroughputThousands/secThousands/secMillions/secMillions/sec
OrderingFIFO (with sessions)FIFO (with sessions)Per partitionNo
DeliveryAt-least-once
Exactly-once (sessions)
At-least-once
Exactly-once (sessions)
At-least-onceAt-least-once
RetentionUp to 14 daysUp to 14 days1-90 days24 hours
Dead LetterYesYesNoNo (retry with backoff)
Duplicate DetectionYesYesNoNo
TransactionsYesYesNoNo
Cost$10/month + ops$10/month + ops$11/month + ingress$0.60/million events
Use CaseCritical transactionsBroadcast to multiple servicesTelemetry, logsServerless triggers

4. Service Bus Patterns

Pattern 1: Queue (Point-to-Point)

Code Example:
// Send message to queue
using Azure.Messaging.ServiceBus;

var client = new ServiceBusClient(connectionString);
var sender = client.CreateSender("orders-queue");

var message = new ServiceBusMessage(JsonSerializer.Serialize(order))
{
    MessageId = order.Id.ToString(),
    SessionId = order.CustomerId.ToString(), // For FIFO per customer
    TimeToLive = TimeSpan.FromHours(24),
    ScheduledEnqueueTime = DateTimeOffset.UtcNow.AddMinutes(5) // Delayed delivery
};

await sender.SendMessageAsync(message);

// Receive and process
var processor = client.CreateProcessor("orders-queue", new ServiceBusProcessorOptions
{
    MaxConcurrentCalls = 10,
    AutoCompleteMessages = false, // Manual completion for reliability
    PrefetchCount = 20
});

processor.ProcessMessageAsync += async args =>
{
    try
    {
        var order = JsonSerializer.Deserialize<Order>(args.Message.Body);
        await ProcessOrder(order);

        // Complete the message (remove from queue)
        await args.CompleteMessageAsync(args.Message);
    }
    catch (Exception ex)
    {
        // Dead-letter if processing fails repeatedly
        if (args.Message.DeliveryCount > 3)
        {
            await args.DeadLetterMessageAsync(args.Message,
                "Processing failed after 3 attempts",
                ex.Message);
        }
        else
        {
            // Abandon to retry
            await args.AbandonMessageAsync(args.Message);
        }
    }
};

processor.ProcessErrorAsync += args =>
{
    Console.WriteLine($"Error: {args.Exception}");
    return Task.CompletedTask;
};

await processor.StartProcessingAsync();

Pattern 2: Topic/Subscription (Pub/Sub)

Code Example:
// Publish to topic
var sender = client.CreateSender("order-events");

var message = new ServiceBusMessage(JsonSerializer.Serialize(orderEvent))
{
    ApplicationProperties =
    {
        ["Priority"] = orderEvent.Priority,
        ["Amount"] = orderEvent.Amount,
        ["Region"] = orderEvent.Region
    }
};

await sender.SendMessageAsync(message);

// Create subscription with filter
var namespaceManager = new ServiceBusAdministrationClient(connectionString);

var subscription = new CreateSubscriptionOptions("order-events", "large-orders")
{
    DefaultMessageTimeToLive = TimeSpan.FromDays(1)
};

var rule = new CreateRuleOptions
{
    Name = "LargeOrdersFilter",
    Filter = new SqlRuleFilter("Amount > 1000")
};

await namespaceManager.CreateSubscriptionAsync(subscription, rule);

// Subscribe and process
var processor = client.CreateProcessor("order-events", "large-orders");

processor.ProcessMessageAsync += async args =>
{
    var orderEvent = JsonSerializer.Deserialize<OrderEvent>(args.Message.Body);
    await SendToFraudDetection(orderEvent);
    await args.CompleteMessageAsync(args.Message);
};

await processor.StartProcessingAsync();

Pattern 3: Request-Reply

// Request with reply-to address
var replyQueue = "order-responses";
var message = new ServiceBusMessage(JsonSerializer.Serialize(request))
{
    ReplyTo = replyQueue,
    CorrelationId = Guid.NewGuid().ToString()
};

await sender.SendMessageAsync(message);

// Listen for response
var responseProcessor = client.CreateProcessor(replyQueue);
responseProcessor.ProcessMessageAsync += async args =>
{
    if (args.Message.CorrelationId == message.CorrelationId)
    {
        var response = JsonSerializer.Deserialize<OrderResponse>(args.Message.Body);
        // Process response
    }
};

5. Azure Event Hub

Event Hub is a big data streaming platform and event ingestion service.

Event Hub Use Cases

Telemetry Ingestion

  • IoT device data
  • Application logs
  • Performance metrics
  • Clickstream data

Real-Time Analytics

  • Live dashboards
  • Anomaly detection
  • Fraud detection
  • Stream processing

Event Sourcing

  • Append-only event log
  • Event replay
  • Audit trail
  • Time travel queries

Data Pipelines

  • ETL workflows
  • Data lake ingestion
  • Cross-region replication
  • Archive to storage

Event Hub Architecture

Code Example:
// Send events to Event Hub
using Azure.Messaging.EventHubs;
using Azure.Messaging.EventHubs.Producer;

var producer = new EventHubProducerClient(connectionString, eventHubName);

// Batch send for efficiency
var batch = await producer.CreateBatchAsync();

foreach (var telemetry in telemetryData)
{
    var eventData = new EventData(JsonSerializer.Serialize(telemetry))
    {
        // Partition key ensures ordering for same device
        PartitionKey = telemetry.DeviceId
    };

    if (!batch.TryAdd(eventData))
    {
        // Batch full, send and create new batch
        await producer.SendAsync(batch);
        batch = await producer.CreateBatchAsync();
        batch.TryAdd(eventData);
    }
}

await producer.SendAsync(batch);

// Process events
using Azure.Messaging.EventHubs.Consumer;

var consumer = new EventHubConsumerClient(
    consumerGroup: "analytics",
    connectionString: connectionString,
    eventHubName: eventHubName);

await foreach (PartitionEvent partitionEvent in consumer.ReadEventsAsync())
{
    var telemetry = JsonSerializer.Deserialize<Telemetry>(partitionEvent.Data.Body);
    await ProcessTelemetry(telemetry);

    // Checkpoint to track progress
    await partitionEvent.CheckpointAsync();
}

6. Event-Driven Architecture Patterns

Pattern 1: Saga Pattern (Distributed Transactions)

Problem: How to maintain data consistency across microservices? Solution: Coordinate a sequence of local transactions with compensating actions. Implementation:
// Orchestrator (Saga Coordinator)
public class OrderSaga
{
    private readonly ServiceBusClient _serviceBus;

    public async Task Handle(OrderCreatedEvent @event)
    {
        var sagaState = new SagaState
        {
            OrderId = @event.OrderId,
            Step = SagaStep.PaymentPending
        };

        try
        {
            // Step 1: Process payment
            await PublishCommand(new ProcessPaymentCommand(@event.OrderId));
            await WaitForEvent<PaymentSuccessfulEvent>();
            sagaState.Step = SagaStep.InventoryPending;

            // Step 2: Reserve inventory
            await PublishCommand(new ReserveInventoryCommand(@event.OrderId));
            await WaitForEvent<InventoryReservedEvent>();
            sagaState.Step = SagaStep.ShippingPending;

            // Step 3: Create shipment
            await PublishCommand(new CreateShipmentCommand(@event.OrderId));
            await WaitForEvent<ShipmentCreatedEvent>();
            sagaState.Step = SagaStep.Completed;

            await PublishEvent(new OrderCompletedEvent(@event.OrderId));
        }
        catch (Exception ex)
        {
            // Compensate in reverse order
            await CompensateSaga(sagaState);
        }
    }

    private async Task CompensateSaga(SagaState state)
    {
        if (state.Step >= SagaStep.InventoryPending)
        {
            await PublishCommand(new ReleaseInventoryCommand(state.OrderId));
        }

        if (state.Step >= SagaStep.PaymentPending)
        {
            await PublishCommand(new RefundPaymentCommand(state.OrderId));
        }

        await PublishEvent(new OrderCancelledEvent(state.OrderId));
    }
}

Pattern 2: Event Sourcing

Store state as a sequence of events, not snapshots.
// Event store
public class OrderEventStore
{
    private readonly EventHubProducerClient _eventHub;

    public async Task AppendEvent(OrderEvent @event)
    {
        var eventData = new EventData(JsonSerializer.Serialize(@event))
        {
            // Partition by aggregate ID for ordering
            PartitionKey = @event.OrderId.ToString()
        };

        await _eventHub.SendAsync(new[] { eventData });
    }
}

// Rebuild state from events
public class Order
{
    public Guid Id { get; private set; }
    public OrderStatus Status { get; private set; }
    public decimal Total { get; private set; }

    public static Order FromEvents(IEnumerable<OrderEvent> events)
    {
        var order = new Order();

        foreach (var @event in events)
        {
            order.Apply(@event);
        }

        return order;
    }

    private void Apply(OrderEvent @event)
    {
        switch (@event)
        {
            case OrderCreatedEvent e:
                Id = e.OrderId;
                Status = OrderStatus.Created;
                break;

            case OrderItemAddedEvent e:
                Total += e.Price * e.Quantity;
                break;

            case OrderCancelledEvent e:
                Status = OrderStatus.Cancelled;
                break;
        }
    }
}

// Query events
var events = await GetEventsFromEventHub(orderId);
var order = Order.FromEvents(events);

7. API Gateway Patterns

Pattern 1: API Aggregation (Backend for Frontend)

Problem: Mobile app needs data from 5 different microservices. Solution: Create a BFF (Backend for Frontend) API that aggregates responses.
[ApiController]
[Route("api/mobile")]
public class MobileAggregatorController : ControllerBase
{
    private readonly IProductService _products;
    private readonly IRecommendationService _recommendations;
    private readonly IInventoryService _inventory;

    [HttpGet("homepage")]
    public async Task<IActionResult> GetHomePage()
    {
        // Parallel calls to multiple services
        var tasks = new[]
        {
            _products.GetFeaturedProductsAsync(),
            _recommendations.GetPersonalizedAsync(User.Id),
            _inventory.GetAvailabilityAsync()
        };

        await Task.WhenAll(tasks);

        return Ok(new
        {
            FeaturedProducts = tasks[0].Result,
            Recommendations = tasks[1].Result,
            Availability = tasks[2].Result
        });
    }
}

Pattern 2: Circuit Breaker in APIM

<!-- Circuit breaker policy -->
<policies>
    <inbound>
        <send-request mode="new" response-variable-name="backendHealth" timeout="5">
            <set-url>https://backend-api.azurewebsites.net/health</set-url>
            <set-method>GET</set-method>
        </send-request>

        <choose>
            <when condition="@(((IResponse)context.Variables["backendHealth"]).StatusCode != 200)">
                <!-- Return cached response or error -->
                <return-response>
                    <set-status code="503" reason="Service Unavailable" />
                    <set-body>@{
                        return "Backend service is temporarily unavailable. Please try again later.";
                    }</set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
</policies>

8. Interview Questions

Beginner Level

Answer:Service Bus Queue (Point-to-point):
  • Single consumer processes each message
  • Order processing, payment processing
  • Competing consumers for scale
Service Bus Topic (Pub/Sub):
  • Multiple subscribers receive each message
  • Notification systems (email, SMS, push)
  • Event broadcasting to multiple services
Example: Order created event → Topic sends to inventory, shipping, and analytics services
Answer:Event Hub:
  • High throughput (millions events/sec)
  • Streaming and telemetry
  • Partition-based ordering
  • No dead letter queue
  • Use: IoT telemetry, logs, clickstream
Service Bus:
  • Reliable messaging (thousands/sec)
  • Transactional guarantees
  • FIFO ordering (with sessions)
  • Dead letter queue for failed messages
  • Use: Critical business transactions

Intermediate Level

Answer:Setup:
  1. Create API version set in APIM
  2. Add multiple API versions (v1, v2)
  3. Choose versioning scheme (URI, query, header)
Example (URI versioning):
# Create version set
az apim api versionset create \
  --resource-group rg-apim \
  --service-name my-apim \
  --name product-api-versions \
  --versioning-scheme Segment

# Add v1
az apim api create \
  --api-id product-api-v1 \
  --path products \
  --api-version v1 \
  --api-version-set-id product-api-versions

# Add v2
az apim api create \
  --api-id product-api-v2 \
  --path products \
  --api-version v2 \
  --api-version-set-id product-api-versions
Result:
  • /v1/products → Backend API v1
  • /v2/products → Backend API v2
Answer:Architecture:
Service Bus Queue
  ├─ MaxDeliveryCount: 3
  ├─ LockDuration: 5 minutes
  └─ Dead Letter Queue

Processing Logic:
1. Receive message (lock acquired)
2. Process with try/catch
3. Success → Complete message
4. Transient error → Abandon (retry)
5. Permanent error → Dead letter
6. Lock expires → Auto-abandon

Dead Letter Handling:
- Separate processor monitors DLQ
- Analyze failure reason
- Fix data and resubmit OR alert humans
Code:
processor.ProcessMessageAsync += async args =>
{
    try
    {
        await ProcessMessage(args.Message);
        await args.CompleteMessageAsync(args.Message);
    }
    catch (TransientException ex)
    {
        // Retry (abandon)
        await args.AbandonMessageAsync(args.Message);
    }
    catch (PermanentException ex)
    {
        // Dead letter with reason
        await args.DeadLetterMessageAsync(args.Message,
            "Permanent failure",
            ex.Message);
    }
};

Advanced Level

Answer:See Section 6 - Saga Pattern above for complete implementation.Key Points:
  • No distributed 2PC (two-phase commit)
  • Each service has local transaction
  • Compensating actions for rollback
  • Saga coordinator tracks state
  • Eventual consistency
Challenges:
  • Compensating actions must be idempotent
  • What if compensation fails? (Retry with exponential backoff)
  • Partial failures require careful state management
Answer:Tiered Rate Limiting:
<policies>
    <inbound>
        <choose>
            <when condition="@(context.Product.Name == "Enterprise")">
                <rate-limit calls="10000" renewal-period="60" />
                <quota calls="1000000" renewal-period="2592000" />
            </when>
            <when condition="@(context.Product.Name == "Professional")">
                <rate-limit calls="1000" renewal-period="60" />
                <quota calls="100000" renewal-period="2592000" />
            </when>
            <when condition="@(context.Product.Name == "Basic")">
                <rate-limit calls="100" renewal-period="60" />
                <quota calls="10000" renewal-period="2592000" />
            </when>
            <otherwise>
                <!-- Free tier -->
                <rate-limit calls="10" renewal-period="60" />
                <quota calls="1000" renewal-period="2592000" />
            </otherwise>
        </choose>

        <!-- Set response headers -->
        <set-header name="X-RateLimit-Limit" exists-action="override">
            <value>@(context.Product.Name == "Enterprise" ? "10000" : "100")</value>
        </set-header>
        <set-header name="X-RateLimit-Remaining" exists-action="override">
            <value>@(context.Response.Headers.GetValueOrDefault("RateLimit-Remaining", "0"))</value>
        </set-header>
    </inbound>
</policies>
Response when limit exceeded:
HTTP 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1642003200

9. Key Takeaways

Choose the Right Tool

APIM for APIs, Service Bus for messaging, Event Hub for streaming. Don’t use a hammer for a screw.

Decouple Services

Asynchronous messaging prevents cascading failures and enables independent scaling.

Handle Failures Gracefully

Dead letter queues, retries, circuit breakers, and compensating transactions are essential.

Design for Idempotency

Messages can be delivered more than once. Make sure processing is safe to repeat.

Monitor Everything

Track message latency, queue depth, dead letter count, and API response times.

Version Your APIs

Breaking changes require new versions. Use URI versioning for clarity.

Next Steps

Continue to Chapter 17

Master SRE practices, SLIs/SLOs, error budgets, and production excellence