Back to Blog
4 min read

Event-Driven Architecture with Azure Event Grid

Event-driven architectures enable loosely coupled systems that can scale independently. Azure Event Grid provides a fully managed event routing service that simplifies building reactive applications. Here is how to leverage it effectively.

Understanding Event Grid

Event Grid connects event sources to event handlers:

  • Publishers - Azure services or custom applications
  • Topics - Endpoints for publishing events
  • Subscriptions - Routes events to handlers
  • Event Handlers - Azure Functions, Logic Apps, Webhooks, etc.

Creating a Custom Topic

# Create a custom topic
az eventgrid topic create \
    --name events-myapp \
    --resource-group rg-events \
    --location australiaeast

# Get the endpoint
az eventgrid topic show \
    --name events-myapp \
    --resource-group rg-events \
    --query endpoint

# Get the access key
az eventgrid topic key list \
    --name events-myapp \
    --resource-group rg-events

Publishing Events

using Azure.Messaging.EventGrid;

public class EventPublisher
{
    private readonly EventGridPublisherClient _client;

    public EventPublisher(string topicEndpoint, string accessKey)
    {
        var credential = new AzureKeyCredential(accessKey);
        _client = new EventGridPublisherClient(new Uri(topicEndpoint), credential);
    }

    public async Task PublishOrderCreatedAsync(Order order)
    {
        var events = new List<EventGridEvent>
        {
            new EventGridEvent(
                subject: $"orders/{order.Id}",
                eventType: "Order.Created",
                dataVersion: "1.0",
                data: new OrderCreatedEvent
                {
                    OrderId = order.Id,
                    CustomerId = order.CustomerId,
                    TotalAmount = order.TotalAmount,
                    Items = order.Items.Select(i => new OrderItemEvent
                    {
                        ProductId = i.ProductId,
                        Quantity = i.Quantity,
                        Price = i.Price
                    }).ToList()
                })
        };

        await _client.SendEventsAsync(events);
    }

    public async Task PublishBatchAsync<T>(string eventType, IEnumerable<T> items)
    {
        var events = items.Select(item => new EventGridEvent(
            subject: $"{eventType.ToLower()}/{Guid.NewGuid()}",
            eventType: eventType,
            dataVersion: "1.0",
            data: item
        )).ToList();

        // Event Grid supports up to 1MB per request
        await _client.SendEventsAsync(events);
    }
}

public class OrderCreatedEvent
{
    public string OrderId { get; set; }
    public string CustomerId { get; set; }
    public decimal TotalAmount { get; set; }
    public List<OrderItemEvent> Items { get; set; }
}

Creating Event Subscriptions

Azure Function Handler

# Create subscription to Azure Function
az eventgrid event-subscription create \
    --name order-processor-sub \
    --source-resource-id "/subscriptions/{sub}/resourceGroups/rg-events/providers/Microsoft.EventGrid/topics/events-myapp" \
    --endpoint "/subscriptions/{sub}/resourceGroups/rg-functions/providers/Microsoft.Web/sites/order-processor-func/functions/ProcessOrder" \
    --endpoint-type azurefunction \
    --event-delivery-schema eventgridschema

Webhook Handler

# Create subscription to webhook
az eventgrid event-subscription create \
    --name webhook-sub \
    --source-resource-id "/subscriptions/{sub}/resourceGroups/rg-events/providers/Microsoft.EventGrid/topics/events-myapp" \
    --endpoint "https://myapi.azurewebsites.net/api/events" \
    --endpoint-type webhook \
    --included-event-types "Order.Created" "Order.Updated"

Azure Function Event Handler

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using Azure.Messaging.EventGrid;

public class OrderEventFunctions
{
    private readonly IOrderService _orderService;
    private readonly ILogger<OrderEventFunctions> _logger;

    public OrderEventFunctions(IOrderService orderService, ILogger<OrderEventFunctions> logger)
    {
        _orderService = orderService;
        _logger = logger;
    }

    [FunctionName("ProcessOrderCreated")]
    public async Task ProcessOrderCreated(
        [EventGridTrigger] EventGridEvent eventGridEvent)
    {
        _logger.LogInformation("Processing event: {EventType}, Subject: {Subject}",
            eventGridEvent.EventType, eventGridEvent.Subject);

        var orderEvent = eventGridEvent.Data.ToObjectFromJson<OrderCreatedEvent>();

        await _orderService.ProcessNewOrderAsync(orderEvent);
    }

    [FunctionName("ProcessOrderEvents")]
    public async Task ProcessOrderEvents(
        [EventGridTrigger] EventGridEvent[] events)
    {
        foreach (var eventGridEvent in events)
        {
            switch (eventGridEvent.EventType)
            {
                case "Order.Created":
                    await HandleOrderCreatedAsync(eventGridEvent);
                    break;
                case "Order.Shipped":
                    await HandleOrderShippedAsync(eventGridEvent);
                    break;
                default:
                    _logger.LogWarning("Unknown event type: {EventType}", eventGridEvent.EventType);
                    break;
            }
        }
    }
}

Webhook Handler (ASP.NET Core)

[ApiController]
[Route("api/[controller]")]
public class EventsController : ControllerBase
{
    private readonly ILogger<EventsController> _logger;

    public EventsController(ILogger<EventsController> logger)
    {
        _logger = logger;
    }

    [HttpPost]
    public async Task<IActionResult> HandleEvent()
    {
        using var reader = new StreamReader(Request.Body);
        var requestBody = await reader.ReadToEndAsync();

        var events = JsonSerializer.Deserialize<EventGridEvent[]>(requestBody);

        foreach (var eventGridEvent in events)
        {
            // Handle subscription validation
            if (eventGridEvent.EventType == "Microsoft.EventGrid.SubscriptionValidationEvent")
            {
                var validationData = eventGridEvent.Data.ToObjectFromJson<SubscriptionValidationEventData>();
                return Ok(new { validationResponse = validationData.ValidationCode });
            }

            // Handle actual events
            await ProcessEventAsync(eventGridEvent);
        }

        return Ok();
    }

    private async Task ProcessEventAsync(EventGridEvent eventGridEvent)
    {
        _logger.LogInformation("Received event: {EventType}", eventGridEvent.EventType);

        switch (eventGridEvent.EventType)
        {
            case "Order.Created":
                var orderData = eventGridEvent.Data.ToObjectFromJson<OrderCreatedEvent>();
                // Process order
                break;
        }
    }
}

Event Filtering

Apply advanced filters:

az eventgrid event-subscription create \
    --name filtered-sub \
    --source-resource-id "/subscriptions/{sub}/resourceGroups/rg-events/providers/Microsoft.EventGrid/topics/events-myapp" \
    --endpoint "https://myapi.azurewebsites.net/api/events" \
    --advanced-filter data.totalAmount NumberGreaterThan 1000 \
    --advanced-filter data.customerId StringContains "premium"

System Topics for Azure Events

React to Azure resource events:

# Create system topic for blob storage events
az eventgrid system-topic create \
    --name blob-events-topic \
    --resource-group rg-events \
    --source "/subscriptions/{sub}/resourceGroups/rg-storage/providers/Microsoft.Storage/storageAccounts/mystore" \
    --topic-type Microsoft.Storage.StorageAccounts \
    --location australiaeast

# Subscribe to blob created events
az eventgrid system-topic event-subscription create \
    --name blob-created-sub \
    --system-topic-name blob-events-topic \
    --resource-group rg-events \
    --endpoint "/subscriptions/{sub}/resourceGroups/rg-functions/providers/Microsoft.Web/sites/blob-processor/functions/ProcessBlob" \
    --endpoint-type azurefunction \
    --included-event-types Microsoft.Storage.BlobCreated

Dead Letter Configuration

Handle failed deliveries:

az eventgrid event-subscription create \
    --name resilient-sub \
    --source-resource-id "/subscriptions/{sub}/resourceGroups/rg-events/providers/Microsoft.EventGrid/topics/events-myapp" \
    --endpoint "https://myapi.azurewebsites.net/api/events" \
    --deadletter-endpoint "/subscriptions/{sub}/resourceGroups/rg-storage/providers/Microsoft.Storage/storageAccounts/mystore/blobServices/default/containers/deadletters" \
    --max-delivery-attempts 10 \
    --event-ttl 1440

Event Grid provides the foundation for building scalable, event-driven applications on Azure.

Michael John Peña

Michael John Peña

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