Back to Blog
6 min read

Leveraging Azure Architecture Center Reference Architectures

Introduction

The Azure Architecture Center is a comprehensive resource for cloud architecture guidance. It provides reference architectures, best practices, and design patterns that help architects and developers build robust solutions. This post explores how to effectively use these resources and implement key patterns.

Key Reference Architectures

Web Application Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         Azure Front Door                         │
│                    (Global Load Balancing + WAF)                │
└────────────────────────────┬────────────────────────────────────┘

        ┌────────────────────┴────────────────────┐
        │                                         │
   ┌────▼────┐                              ┌────▼────┐
   │ Region 1│                              │ Region 2│
   │ Primary │                              │Secondary│
   └────┬────┘                              └────┬────┘
        │                                         │
   ┌────▼────────────────┐                 ┌────▼────────────────┐
   │   App Service       │                 │   App Service       │
   │   (Web + API)       │                 │   (Web + API)       │
   └────┬────────────────┘                 └────┬────────────────┘
        │                                         │
   ┌────▼────────────────┐                 ┌────▼────────────────┐
   │   Azure SQL         │◄───Geo-Repl────►│   Azure SQL         │
   │   (Primary)         │                 │   (Secondary)       │
   └─────────────────────┘                 └─────────────────────┘

Implementing Multi-Region Architecture

// multi-region-webapp.bicep
param primaryRegion string = 'australiaeast'
param secondaryRegion string = 'australiasoutheast'
param appName string

// Primary Region Resources
module primaryApp 'modules/webapp-region.bicep' = {
  name: 'primary-region'
  params: {
    location: primaryRegion
    appName: '${appName}-primary'
    isPrimary: true
  }
}

// Secondary Region Resources
module secondaryApp 'modules/webapp-region.bicep' = {
  name: 'secondary-region'
  params: {
    location: secondaryRegion
    appName: '${appName}-secondary'
    isPrimary: false
  }
}

// Azure Front Door
resource frontDoor 'Microsoft.Cdn/profiles@2021-06-01' = {
  name: 'fd-${appName}'
  location: 'Global'
  sku: {
    name: 'Premium_AzureFrontDoor'
  }
}

resource frontDoorEndpoint 'Microsoft.Cdn/profiles/afdEndpoints@2021-06-01' = {
  parent: frontDoor
  name: appName
  location: 'Global'
  properties: {
    enabledState: 'Enabled'
  }
}

resource originGroup 'Microsoft.Cdn/profiles/originGroups@2021-06-01' = {
  parent: frontDoor
  name: 'webapp-origins'
  properties: {
    loadBalancingSettings: {
      sampleSize: 4
      successfulSamplesRequired: 3
      additionalLatencyInMilliseconds: 50
    }
    healthProbeSettings: {
      probePath: '/health'
      probeRequestType: 'GET'
      probeProtocol: 'Https'
      probeIntervalInSeconds: 30
    }
  }
}

resource primaryOrigin 'Microsoft.Cdn/profiles/originGroups/origins@2021-06-01' = {
  parent: originGroup
  name: 'primary'
  properties: {
    hostName: primaryApp.outputs.hostname
    httpPort: 80
    httpsPort: 443
    priority: 1
    weight: 1000
    enabledState: 'Enabled'
  }
}

resource secondaryOrigin 'Microsoft.Cdn/profiles/originGroups/origins@2021-06-01' = {
  parent: originGroup
  name: 'secondary'
  properties: {
    hostName: secondaryApp.outputs.hostname
    httpPort: 80
    httpsPort: 443
    priority: 2
    weight: 1000
    enabledState: 'Enabled'
  }
}

Microservices Reference Architecture

Service Mesh with Azure Kubernetes Service

# aks-microservices.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  labels:
    app: order-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "order-service"
        dapr.io/app-port: "80"
    spec:
      containers:
      - name: order-service
        image: myregistry.azurecr.io/order-service:latest
        ports:
        - containerPort: 80
        env:
        - name: ASPNETCORE_ENVIRONMENT
          value: "Production"
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
        livenessProbe:
          httpGet:
            path: /health/live
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
  ports:
  - port: 80
    targetPort: 80

API Gateway Pattern

// API Gateway using YARP
public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddReverseProxy()
            .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

        builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Authority = builder.Configuration["AzureAd:Authority"];
                options.Audience = builder.Configuration["AzureAd:Audience"];
            });

        builder.Services.AddRateLimiter(options =>
        {
            options.AddFixedWindowLimiter("api", opt =>
            {
                opt.Window = TimeSpan.FromMinutes(1);
                opt.PermitLimit = 100;
                opt.QueueLimit = 10;
            });
        });

        var app = builder.Build();

        app.UseAuthentication();
        app.UseAuthorization();
        app.UseRateLimiter();

        app.MapReverseProxy();

        app.Run();
    }
}
// appsettings.json - YARP configuration
{
  "ReverseProxy": {
    "Routes": {
      "orders-route": {
        "ClusterId": "orders-cluster",
        "Match": {
          "Path": "/api/orders/{**catch-all}"
        },
        "Transforms": [
          { "PathRemovePrefix": "/api/orders" }
        ]
      },
      "products-route": {
        "ClusterId": "products-cluster",
        "Match": {
          "Path": "/api/products/{**catch-all}"
        },
        "Transforms": [
          { "PathRemovePrefix": "/api/products" }
        ]
      }
    },
    "Clusters": {
      "orders-cluster": {
        "Destinations": {
          "destination1": {
            "Address": "http://order-service/"
          }
        },
        "HealthCheck": {
          "Active": {
            "Enabled": true,
            "Interval": "00:00:10",
            "Path": "/health"
          }
        }
      },
      "products-cluster": {
        "Destinations": {
          "destination1": {
            "Address": "http://product-service/"
          }
        }
      }
    }
  }
}

Event-Driven Architecture

Event Grid with Azure Functions

// Event publisher
public class OrderService
{
    private readonly EventGridPublisherClient _eventGridClient;

    public async Task PublishOrderCreatedAsync(Order order)
    {
        var cloudEvent = new CloudEvent(
            source: "/orders",
            type: "Order.Created",
            data: new OrderCreatedEvent
            {
                OrderId = order.Id,
                CustomerId = order.CustomerId,
                TotalAmount = order.TotalAmount,
                CreatedAt = DateTime.UtcNow
            });

        await _eventGridClient.SendEventAsync(cloudEvent);
    }
}

// Event handler (Azure Function)
public class OrderEventHandler
{
    [Function("HandleOrderCreated")]
    public async Task Run(
        [EventGridTrigger] CloudEvent cloudEvent,
        FunctionContext context)
    {
        var logger = context.GetLogger<OrderEventHandler>();
        var orderEvent = cloudEvent.Data.ToObjectFromJson<OrderCreatedEvent>();

        logger.LogInformation("Processing order: {OrderId}", orderEvent.OrderId);

        // Process the order
        await ProcessOrderAsync(orderEvent);
    }
}

Data Architecture Patterns

Data Lake Architecture

// data-lake.bicep
param location string = 'australiaeast'
param dataLakeName string

// Data Lake Storage Gen2
resource dataLake 'Microsoft.Storage/storageAccounts@2021-06-01' = {
  name: dataLakeName
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  properties: {
    isHnsEnabled: true
    accessTier: 'Hot'
    minimumTlsVersion: 'TLS1_2'
    supportsHttpsTrafficOnly: true
  }
}

// Containers for medallion architecture
resource bronzeContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {
  name: '${dataLake.name}/default/bronze'
  properties: {
    publicAccess: 'None'
  }
}

resource silverContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {
  name: '${dataLake.name}/default/silver'
  properties: {
    publicAccess: 'None'
  }
}

resource goldContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {
  name: '${dataLake.name}/default/gold'
  properties: {
    publicAccess: 'None'
  }
}

// Azure Synapse Analytics
resource synapse 'Microsoft.Synapse/workspaces@2021-06-01' = {
  name: 'synapse-${dataLakeName}'
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    defaultDataLakeStorage: {
      accountUrl: 'https://${dataLake.name}.dfs.core.windows.net'
      filesystem: 'synapse'
    }
  }
}

Hub-Spoke Network Topology

// hub-spoke-network.bicep

// Hub VNet
resource hubVnet 'Microsoft.Network/virtualNetworks@2021-03-01' = {
  name: 'vnet-hub'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: ['10.0.0.0/16']
    }
    subnets: [
      {
        name: 'GatewaySubnet'
        properties: {
          addressPrefix: '10.0.0.0/24'
        }
      }
      {
        name: 'AzureFirewallSubnet'
        properties: {
          addressPrefix: '10.0.1.0/24'
        }
      }
      {
        name: 'SharedServices'
        properties: {
          addressPrefix: '10.0.2.0/24'
        }
      }
    ]
  }
}

// Spoke VNets
resource spokeVnet1 'Microsoft.Network/virtualNetworks@2021-03-01' = {
  name: 'vnet-spoke-prod'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: ['10.1.0.0/16']
    }
    subnets: [
      {
        name: 'app'
        properties: {
          addressPrefix: '10.1.1.0/24'
        }
      }
      {
        name: 'data'
        properties: {
          addressPrefix: '10.1.2.0/24'
        }
      }
    ]
  }
}

// VNet Peering
resource hubToSpoke1Peering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2021-03-01' = {
  parent: hubVnet
  name: 'hub-to-spoke-prod'
  properties: {
    remoteVirtualNetwork: {
      id: spokeVnet1.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: true
  }
}

resource spoke1ToHubPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2021-03-01' = {
  parent: spokeVnet1
  name: 'spoke-prod-to-hub'
  properties: {
    remoteVirtualNetwork: {
      id: hubVnet.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    useRemoteGateways: true
  }
}

Architecture Decision Records

# ADR-001: Use Azure Front Door for Global Load Balancing

## Status
Accepted

## Context
We need to distribute traffic across multiple regions for our web application
to ensure low latency for global users and high availability.

## Decision
We will use Azure Front Door Premium for:
- Global load balancing with latency-based routing
- Web Application Firewall (WAF) protection
- SSL offloading at the edge
- Health probes for automatic failover

## Consequences
- Additional cost for Front Door Premium SKU
- Need to configure proper health endpoints
- SSL certificates managed at Front Door level
- Reduced latency for users globally

## Alternatives Considered
- Azure Traffic Manager: DNS-based only, no WAF
- Application Gateway: Regional only
- Third-party CDN: Additional vendor management

Conclusion

The Azure Architecture Center provides invaluable guidance for building cloud solutions. By following reference architectures and adapting them to your specific needs, you can avoid common pitfalls and implement proven patterns. Always consider the trade-offs of each architectural decision and document them for future reference.

References

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.