Building microservices on Azure requires understanding key architectural patterns that ensure scalability, resilience, and maintainability. In this guide, we'll explore essential patterns every cloud-native developer should know.
API Gateway Pattern
The API Gateway acts as a single entry point for all client requests, providing:
- Request routing to appropriate microservices
- Authentication and authorization centralization
- Rate limiting and throttling
- Request/response transformation
csharp1// Azure API Management example 2public class ApiGatewayService 3{ 4 private readonly IHttpClientFactory _httpClientFactory; 5 6 public ApiGatewayService(IHttpClientFactory httpClientFactory) 7 { 8 _httpClientFactory = httpClientFactory; 9 } 10 11 public async Task<T> RouteRequest<T>(string serviceName, string endpoint) 12 { 13 var client = _httpClientFactory.CreateClient(serviceName); 14 var response = await client.GetAsync(endpoint); 15 16 if (response.IsSuccessStatusCode) 17 { 18 var content = await response.Content.ReadAsStringAsync(); 19 return JsonSerializer.Deserialize<T>(content); 20 } 21 22 throw new ServiceUnavailableException($"Service {serviceName} is unavailable"); 23 } 24}
Circuit Breaker Pattern
Prevent cascading failures by implementing circuit breakers using Polly:
csharp1public class CircuitBreakerService 2{ 3 private readonly IAsyncPolicy<HttpResponseMessage> _circuitBreakerPolicy; 4 5 public CircuitBreakerService() 6 { 7 _circuitBreakerPolicy = Policy 8 .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode) 9 .CircuitBreakerAsync( 10 handledEventsAllowedBeforeBreaking: 3, 11 durationOfBreak: TimeSpan.FromSeconds(30), 12 onBreak: (result, timespan) => { 13 Console.WriteLine($"Circuit breaker opened for {timespan}"); 14 }, 15 onReset: () => { 16 Console.WriteLine("Circuit breaker reset"); 17 }); 18 } 19 20 public async Task<HttpResponseMessage> CallServiceAsync(string url) 21 { 22 return await _circuitBreakerPolicy.ExecuteAsync(async () => 23 { 24 using var client = new HttpClient(); 25 return await client.GetAsync(url); 26 }); 27 } 28}
Service Discovery with Azure Service Fabric
Implement dynamic service discovery for your microservices:
csharp1public class ServiceDiscovery 2{ 3 private readonly FabricClient _fabricClient; 4 5 public ServiceDiscovery() 6 { 7 _fabricClient = new FabricClient(); 8 } 9 10 public async Task<Uri> DiscoverServiceAsync(string serviceName) 11 { 12 var serviceList = await _fabricClient.QueryManager.GetServiceListAsync( 13 new Uri($"fabric:/{serviceName}")); 14 15 var service = serviceList.FirstOrDefault(); 16 if (service != null) 17 { 18 var partitions = await _fabricClient.QueryManager.GetPartitionListAsync(service.ServiceName); 19 var partition = partitions.FirstOrDefault(); 20 21 if (partition != null) 22 { 23 var replicas = await _fabricClient.QueryManager.GetReplicaListAsync(partition.PartitionInformation.Id); 24 var replica = replicas.OfType<StatelessServiceInstanceQueryResult>().FirstOrDefault(); 25 26 return new Uri(replica?.Address ?? ""); 27 } 28 } 29 30 throw new ServiceNotFoundException($"Service {serviceName} not found"); 31 } 32}
Event-Driven Architecture with Azure Service Bus
Implement asynchronous communication between microservices:
csharp1public class EventBusService 2{ 3 private readonly ServiceBusClient _serviceBusClient; 4 private readonly ServiceBusSender _sender; 5 6 public EventBusService(string connectionString, string topicName) 7 { 8 _serviceBusClient = new ServiceBusClient(connectionString); 9 _sender = _serviceBusClient.CreateSender(topicName); 10 } 11 12 public async Task PublishEventAsync<T>(T eventData) where T : class 13 { 14 var message = new ServiceBusMessage(JsonSerializer.Serialize(eventData)) 15 { 16 ContentType = "application/json", 17 Subject = typeof(T).Name 18 }; 19 20 await _sender.SendMessageAsync(message); 21 } 22 23 public async Task SubscribeToEventsAsync<T>( 24 string subscriptionName, 25 Func<T, Task> handler) where T : class 26 { 27 var processor = _serviceBusClient.CreateProcessor(topicName, subscriptionName); 28 29 processor.ProcessMessageAsync += async args => 30 { 31 var eventData = JsonSerializer.Deserialize<T>(args.Message.Body.ToString()); 32 await handler(eventData); 33 await args.CompleteMessageAsync(args.Message); 34 }; 35 36 processor.ProcessErrorAsync += args => 37 { 38 Console.WriteLine($"Error: {args.Exception}"); 39 return Task.CompletedTask; 40 }; 41 42 await processor.StartProcessingAsync(); 43 } 44}
Conclusion
These patterns form the foundation of successful microservices architecture on Azure. By implementing API Gateway, Circuit Breaker, Service Discovery, and Event-Driven patterns, you can build resilient, scalable applications that leverage Azure's cloud-native capabilities.
Remember to monitor your services with Azure Application Insights and implement proper logging and observability practices for production deployments.