A treia parte din seria despre Azure Container Apps. În partea a doua am configurat scaling rules. Aici vedem cum comunică serviciile între ele folosind Dapr.
Ce este Dapr și ce problemă rezolvă
Dapr (Distributed Application Runtime) e un runtime care oferă building blocks pentru aplicații distribuite: service invocation, pub/sub, state management, secrets, bindings. Ideea centrală: în loc să scrii cod specific fiecărei tehnologii (SDK Service Bus, SDK Cosmos DB, HTTP client cu retry etc.), interacționezi cu un API uniform, iar Dapr traduce către infrastructura reală.
Beneficiul concret în microservicii: separi ce face aplicația de cum comunică cu infrastructura. Schimbi broker-ul de mesaje din Service Bus în RabbitMQ modificând un fișier de configurare, nu codul.
În Container Apps, Dapr e built-in — nu instalezi nimic, doar îl activezi. ACA rulează sidecar-ul Dapr lângă containerul tău automat.
Activarea Dapr
az containerapp create \
--name order-service \
--resource-group my-rg \
--environment my-aca-env \
--image myregistry.azurecr.io/order-service:latest \
--target-port 8080 \
--ingress internal \
--enable-dapr \
--dapr-app-id order-service \
--dapr-app-port 8080
dapr-app-id e identificatorul prin care alte servicii adresează acest serviciu. dapr-app-port e portul pe care Dapr comunică cu aplicația ta. În .NET, adaugi SDK-ul Dapr:
dotnet add package Dapr.Client
dotnet add package Dapr.AspNetCore
Service invocation — apeluri între servicii
Fără Dapr, ca să apelezi alt serviciu ai nevoie de URL-ul lui, de un HttpClient configurat, de retry policy, de mTLS. Cu Dapr, adresezi serviciul după app-id și Dapr se ocupă de restul (service discovery, retry, mTLS între sidecar-uri).
// Program.cs
builder.Services.AddDaprClient();
// Apel catre alt serviciu prin app-id, nu prin URL
public class OrderService
{
private readonly DaprClient _dapr;
public OrderService(DaprClient dapr) => _dapr = dapr;
public async Task GetCustomerAsync(string customerId)
{
// Invoca „customer-service” -- Dapr rezolva unde ruleaza
return await _dapr.InvokeMethodAsync(
HttpMethod.Get,
appId: "customer-service",
methodName: $"customers/{customerId}");
}
}
Serviciul apelat nu are nevoie de nimic special — e un endpoint HTTP normal. Dapr injectează request-ul prin sidecar.
Pub/Sub cu Service Bus
Pub/sub e locul unde Dapr strălucește. Configurezi un component Dapr care indică spre Service Bus, iar codul publică și consumă mesaje printr-un API uniform.
Componentul Dapr (pub/sub)
# pubsub-servicebus.yaml
componentType: pubsub.azure.servicebus.topics
version: v1
metadata:
- name: namespaceName
value: "my-servicebus.servicebus.windows.net"
- name: azureClientId
value: "" # autentificare fara secret
scopes:
- order-service
- notification-service
# Inregistreaza componentul in environment
az containerapp env dapr-component set \
--name my-aca-env \
--resource-group my-rg \
--dapr-component-name orderpubsub \
--yaml pubsub-servicebus.yaml
Publicare
public async Task PublishOrderCreatedAsync(Order order)
{
await _dapr.PublishEventAsync(
pubsubName: "orderpubsub",
topicName: "order-created",
data: order);
}
Consum (subscribe)
// Program.cs
app.MapSubscribeHandler(); // endpoint pentru inregistrarea subscriptiilor Dapr
// Controller care primeste evenimentele
[ApiController]
public class NotificationController : ControllerBase
{
[Topic("orderpubsub", "order-created")]
[HttpPost("order-created")]
public async Task OnOrderCreated(Order order)
{
await _emailService.SendConfirmationAsync(order);
return Ok();
}
}
Atributul [Topic] leagă metoda de topic-ul Dapr. Când un mesaj ajunge pe order-created, Dapr îl livrează la această metodă. Fără cod de Service Bus SDK, fără gestionare manuală de conexiuni.
State management cu Cosmos DB
Dapr oferă și un store de stare cheie-valoare abstract, cu Cosmos DB (sau Redis, sau altele) în spate.
# statestore-cosmos.yaml
componentType: state.azure.cosmosdb
version: v1
metadata:
- name: url
value: "https://my-cosmos.documents.azure.com:443/"
- name: database
value: "StateDb"
- name: collection
value: "state"
- name: azureClientId
value: ""
scopes:
- order-service
// Salveaza si citeste stare printr-un API uniform
public async Task SaveCartAsync(string userId, Cart cart)
{
await _dapr.SaveStateAsync("statestore", $"cart-{userId}", cart);
}
public async Task GetCartAsync(string userId)
{
return await _dapr.GetStateAsync("statestore", $"cart-{userId}");
}
Observă din nou azureClientId în toate componentele: Dapr se autentifică la Service Bus și Cosmos DB prin Managed Identity, fără niciun connection string. Fundația de securitate din articolele anterioare se aplică integral.
Când folosești Dapr și când nu
Dapr adaugă valoare reală la o arhitectură de microservicii cu comunicare între servicii, pub/sub și nevoi de portabilitate. Pentru un singur serviciu monolitic, e overhead nejustificat — SDK-urile native Azure sunt suficiente. Ca întotdeauna în serie: adopți complexitatea doar când rezolvă o problemă concretă.
Ce urmează
În partea a patra și ultima punem totul cap la cap cu un pipeline complet de CI/CD: GitHub Actions care construiește imaginea, o împinge în Azure Container Registry și face deploy în Container Apps — totul cu autentificare fără secrete prin OIDC.
Întrebări? Scrie-mi la contact@ludoprogramming.com.