O Azure Cosmos DB é um banco de dados NoSQL da Microsoft, projetado para alta escalabilidade e baixa latência, ideal para aplicações modernas que exigem resposta rápida e flexibilidade. Configurar o cliente do Cosmos DB corretamente é essencial para garantir segurança e eficiência, especialmente em cenários serverless. Neste artigo, demonstrarei como configurar uma Azure Function para se conectar ao Cosmos DB utilizando diferentes métodos de autenticação, aplicando boas práticas de Clean Code.
O que é o Azure Cosmos DB NoSQL?
Quando se trata de bancos de dados NoSQL, o Azure Cosmos DB é uma opção que realmente se destaca. É um serviço da Microsoft projetado para oferecer alta disponibilidade, baixa latência e escalabilidade global. O que eu mais gosto é que ele suporta vários modelos de dados, como documentos, chave-valor, grafos e famílias de colunas, o que o torna muito flexível para diferentes tipos de aplicações.
Principais Características:
- Distribuição Global: Um dos pontos fortes do Cosmos DB é a capacidade de replicar dados em múltiplas regiões do Azure, garantindo que os usuários tenham acesso rápido, independentemente de onde estejam.
- Multi-Modelo: Eu adoro que ele suporte várias APIs, incluindo SQL (Core), MongoDB, Cassandra, Gremlin (para grafos) e Table API. Isso me permite escolher o modelo que melhor se adapta às necessidades do meu projeto.
- Baixa Latência: Oferece tempos de resposta em milissegundos em operações de leitura e gravação, mesmo quando escalado. Isso é essencial para aplicações que precisam de desempenho rápido.
- Escalabilidade Automática: Outra vantagem é a capacidade de ajustar o throughput e o armazenamento de maneira dinâmica, sem downtime. Isso significa que posso adaptar meus recursos conforme as demandas do aplicativo mudam.
- Segurança e Conformidade: O Cosmos DB inclui recursos avançados de segurança, como criptografia em repouso e em trânsito, e está em conformidade com várias normas e regulamentações, o que é crucial para mim ao lidar com dados sensíveis.
Casos de Uso Comuns:
Eu vejo o Azure Cosmos DB sendo amplamente utilizado em:
- Aplicações web e móveis que requerem desempenho rápido e confiável.
- Sistemas de gerenciamento de conteúdo que precisam de flexibilidade no modelo de dados.
- Jogos online que exigem baixa latência e escalabilidade.
- Aplicações IoT que geram grandes volumes de dados.
O Azure Cosmos DB é uma solução robusta e escalável, ideal para lidar com as crescentes necessidades de dados em um ambiente digital que está sempre evoluindo.
Azure Cosmos DB: Melhores Práticas
Ao implementar o Azure Cosmos DB, aproveitar os princípios do Azure Well-Architected Framework (WAF) e do Cloud Adoption Framework (CAF) pode melhorar significativamente a eficácia e a confiabilidade da sua solução de banco de dados.
1. Eficiência de Desempenho
- Otimização de Throughput: Utilize o modelo de throughput provisionado de forma eficaz, escalando recursos com base na demanda da sua aplicação. Monitore padrões de uso e ajuste o throughput para manter o desempenho enquanto controla os custos.
- Particionamento de Dados: Projete seu modelo de dados considerando o particionamento para garantir uma distribuição equilibrada e minimizar a latência das requisições. Escolha chaves de partição apropriadas com base nos padrões de acesso.
2. Gestão de Custos
- Dimensionamento Correto: Avalie e ajuste regularmente o throughput e o armazenamento alocados às suas instâncias do Cosmos DB. Use as ferramentas de Gestão de Custos do Azure para analisar despesas e identificar áreas de otimização.
- Uso do Nível Gratuito e Escalabilidade: Se você está começando, aproveite o nível gratuito do Azure Cosmos DB para explorar e desenvolver sua aplicação sem incorrer em custos, escalando conforme necessário.
3. Segurança
- Identidades Gerenciadas: Utilize a Identidade Gerenciada do Azure para acesso seguro ao Cosmos DB sem a necessidade de codificar credenciais. Isso reduz o risco de exposição de credenciais.
- Segurança de Rede: Implemente endpoints de serviço de rede virtual ou links privados para restringir o acesso às suas contas do Cosmos DB, garantindo que apenas aplicações confiáveis possam se conectar.
4. Confiabilidade
- Replicação Multi-Regional: Aproveite as capacidades de distribuição global do Cosmos DB. Configure gravações em múltiplas regiões para garantir alta disponibilidade e recuperação de desastres, permitindo uma rápida recuperação em caso de falhas regionais.
- Estratégias de Backup: Realize backups regulares dos seus dados e implemente políticas de backup automatizadas para garantir a recuperação dos dados em caso de perda ou corrupção acidental.
5. Governança Operacional
- Monitoramento e Alertas: Use o Azure Monitor e o Application Insights para rastrear métricas de desempenho, configurar alertas para anomalias e garantir que suas instâncias do Cosmos DB estejam operando de forma otimizada.
- Conformidade: Assegure que o uso do Cosmos DB esteja em conformidade com as políticas organizacionais e requisitos regulatórios, utilizando o Azure Policy para impor padrões de governança.
Ao integrar essas práticas no seu uso do Azure Cosmos DB, você pode construir aplicações robustas, seguras e custo-efetivas que aproveitam ao máximo as capacidades da plataforma, alinhando-se às melhores práticas do WAF e do CAF. vamos a prática?
Passo 1: Configuração do Ambiente
Antes de começarmos, instale as dependências necessárias:
npm install @azure/cosmos @azure/identity
Passo 2: Criando a Abstração CredentialProvider
Vamos criar uma interface CredentialProvider que define um método getCredential.
import { TokenCredential } from "@azure/identity";
/**
* Interface for providing Azure credentials.
*/
interface CredentialProvider {
/**
* Retrieves the Azure credential.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential;
}
Passo 3: Implementando Provedores de Credenciais
Criaremos diferentes implementações de CredentialProvider para vários métodos de autenticação.
Método 1: SystemAssignedManagedIdentityCredentialProvider
import { DefaultAzureCredential } from "@azure/identity";
/**
* Provides credentials for system-assigned managed identity.
*/
class SystemAssignedManagedIdentityCredentialProvider implements CredentialProvider {
/**
* Retrieves the Azure credential for system-assigned managed identity.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential {
return new DefaultAzureCredential();
}
}
Método 2: UserAssignedManagedIdentityCredentialProvider
import { DefaultAzureCredential } from "@azure/identity";
/**
* Provides credentials for user-assigned managed identity.
*/
class UserAssignedManagedIdentityCredentialProvider implements CredentialProvider {
private clientId: string;
/**
* Creates an instance of UserAssignedManagedIdentityCredentialProvider.
* @param {string} clientId - The client ID of the user-assigned managed identity.
*/
constructor(clientId: string) {
this.clientId = clientId;
}
/**
* Retrieves the Azure credential for user-assigned managed identity.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential {
return new DefaultAzureCredential({
managedIdentityClientId: this.clientId
});
}
}
Método 3: ServicePrincipalCredentialProvider
import { ClientSecretCredential } from "@azure/identity";
/**
* Provides credentials for service principal authentication.
*/
class ServicePrincipalCredentialProvider implements CredentialProvider {
private tenantId: string;
private clientId: string;
private clientSecret: string;
/**
* Creates an instance of ServicePrincipalCredentialProvider.
* @param {string} tenantId - The tenant ID.
* @param {string} clientId - The client ID.
* @param {string} clientSecret - The client secret.
*/
constructor(tenantId: string, clientId: string, clientSecret: string) {
this.tenantId = tenantId;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
/**
* Retrieves the Azure credential for service principal authentication.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential {
return new ClientSecretCredential(this.tenantId, this.clientId, this.clientSecret);
}
}
Passo 4: Arquitetura com Provedores de Credenciais
Aqui está uma fábrica para instanciar o cliente CosmoDB NoSQL com o CredentialProvider.
import { CosmosClient } from "@azure/cosmos";
/**
* @class CosmosClientFactory
* Factory responsible for creating instances of CosmosClient.
* This class abstracts the client creation logic, allowing the use
* of different credential providers for authentication.
*/
class CosmosClientFactory {
private endpoint: string;
private credentialProvider: CredentialProvider;
/**
* @constructor
* @param {string} endpoint - URL of the Azure Cosmos DB resource.
* @param {CredentialProvider} credentialProvider - Credential provider to be used.
*/
constructor(endpoint: string, credentialProvider: CredentialProvider) {
this.endpoint = endpoint;
this.credentialProvider = credentialProvider;
}
/**
* Creates a new instance of the CosmosClient with the provided credentials.
* @returns {CosmosClient} Configured instance of the CosmosClient.
*/
createCosmosClient(): CosmosClient {
const credential = this.credentialProvider.getCredential();
return new CosmosClient({ endpoint: this.endpoint, aadCredentials: credential });
}
}
Passo 5: Utilizando a Fábrica em uma Azure Function
Vamos integrar isso em uma Azure Function, usando Managed Identity.
Exemplo 1: Azure Function com Managed Identity Atribuída ao Sistema
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { CosmosClientFactory } from "./CosmosClientFactory";
import { SystemAssignedManagedIdentityCredentialProvider } from "./SystemAssignedManagedIdentityCredentialProvider";
const endpoint = process.env.AZURE_COSMOS_RESOURCEENDPOINT;
/**
* Azure Function using system-assigned managed identity to access Azure CosmoDB NoSQL.
*
* @param context - The Azure Function context.
* @param req - The HTTP request.
*
* @returns A promise that resolves when the function is complete.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const credentialProvider = new SystemAssignedManagedIdentityCredentialProvider();
const cosmosClientFactory = new CosmosClientFactory(endpoint, credentialProvider);
const cosmosClient = cosmosClientFactory.createCosmosClient();
const database = cosmosClient.database("my-database");
const container = database.container("my-container");
const { resource } = await container.item("item-id").read();
context.res = {
status: 200,
body: resource
};
};
export default httpTrigger;
Exemplo 2: Azure Function com Managed Identity Atribuída ao Usuário
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { CosmosClientFactory } from "./CosmosClientFactory";
import { UserAssignedManagedIdentityCredentialProvider } from "./UserAssignedManagedIdentityCredentialProvider";
const endpoint = process.env.AZURE_COSMOS_RESOURCEENDPOINT;
const clientId = process.env.AZURE_MYSQL_CLIENTID;
/**
* Azure Function using user-assigned managed identity to access Azure CosmoDB NoSQL.
*
* @param context - The Azure Function context.
* @param req - The HTTP request.
*
* @returns A promise that resolves when the function is complete.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const credentialProvider = new UserAssignedManagedIdentityCredentialProvider(
clientId,
);
const cosmosClientFactory = new CosmosClientFactory(endpoint, credentialProvider);
const cosmosClient = cosmosClientFactory.createCosmosClient();
const database = cosmosClient.database("my-database");
const container = database.container("my-container");
const { resource } = await container.item("item-id").read();
context.res = {
status: 200,
body: resource
};
};
export default httpTrigger;
Exemplo 3: Azure Function com Service Principal
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { CosmosClientFactory } from "./CosmosClientFactory";
import { ServicePrincipalCredentialProvider } from "./ServicePrincipalCredentialProvider";
const endpoint = process.env.AZURE_COSMOS_RESOURCEENDPOINT;
const tenantId = process.env.AZURE_COSMOS_TENANTID;
const clientId = process.env.AZURE_COSMOS_CLIENTID;
const clientSecret = process.env.AZURE_COSMOS_CLIENTSECRET;
/**
* Azure Function using user-assigned managed identity to access Azure CosmoDB NoSQL.
*
* @param context - The Azure Function context.
* @param req - The HTTP request.
*
* @returns A promise that resolves when the function is complete.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const credentialProvider = new ServicePrincipalCredentialProvider(
tenantId, clientId, clientSecret,
);
const cosmosClientFactory = new CosmosClientFactory(endpoint, credentialProvider);
const cosmosClient = cosmosClientFactory.createCosmosClient();
const database = cosmosClient.database("my-database");
const container = database.container("my-container");
const { resource } = await container.item("item-id").read();
context.res = {
status: 200,
body: resource
};
};
export default httpTrigger;
Conclusão
Aplicando os princípios de Clean Code como modularidade e clareza, criamos um design escalável para conectar-se ao Cosmos DB.
Com essas diretrizes e a implementação apresentada, você estará apto a construir soluções robustas e seguras com o Azure Cosmos DB em um ambiente serverless.