Transação Simples: arquitetura de amostra de microsserviços para aplicativos .Net Core
Exemplo do .Net Core 2.2 com C#.Net, EF e SQL Server
- Introdução
- [Arquitetura de aplicativos](#Arquitetura de aplicativos)
- Desenho de Microsserviço
- [Segurança: autenticação baseada em token JWT](#Segurança--autenticação baseada em token JWT)
- [Ambiente de Desenvolvimento](#Ambiente de Desenvolvimento)
- Tecnologias
- [Ferramentas de código aberto usadas](#Ferramentas de código aberto usadas)
- [Serviços de plataforma em nuvem](#Serviços de plataforma em nuvem)
- [Design de banco de dados](#Design de banco de dados)
- [Pontos de extremidade da WebApi](#Pontos de extremidade da WebApi)
- Estrutura da solução
- [Tratamento de exceção](#Manipulação de exceção)
- Manipulação de simultaneidade Db
- Azure AppInsights: registro e monitoramento
- Swagger: Documentação da API
- Coleção Postman
- Como executar o aplicativo
- Console App - Gateway Client
Introdução
Este é um aplicativo de exemplo .Net Core e um exemplo de como criar e implementar um sistema de back-end baseado em microsserviços para um recurso bancário automatizado simples, como Saldo, Depósito, Retirada em ASP.NET Core Web API com C#.Net, Entity Framework e SQLServer.
Arquitetura do aplicativo
O aplicativo de exemplo é construído com base na arquitetura de microsserviços. Existem várias vantagens na construção de um aplicativo usando a arquitetura de microsserviços, pois os serviços podem ser desenvolvidos, implantados e dimensionados independentemente. O diagrama abaixo mostra o design de alto nível da arquitetura de back-end.
- Identity Microservice - Autentica o usuário com base no nome de usuário, senha e emite um token JWT Bearer que contém informações de identidade baseadas em declarações.
- Microsserviço de transação - Lida com transações de conta como Obter saldo, depositar, sacar
- API Gateway - Atua como um ponto central de entrada para o aplicativo de back-end, fornece agregação de dados e caminho de comunicação para microsserviços.
Design de Microsserviço
Este diagrama mostra o design interno do microsserviço de transação. A lógica de negócios e a lógica de dados relacionadas ao serviço de transação são escritas em uma estrutura de processamento de transação separada. A estrutura recebe entrada via Web Api e processa essas solicitações com base em algumas regras simples. Os dados da transação são armazenados no banco de dados SQL.
Segurança: autenticação baseada em token JWT
A autenticação baseada em token JWT é implementada para proteger os serviços WebApi. Identity Microservice atua como um servidor Auth e emite um token válido após validar as credenciais do usuário. O API Gateway envia o token para o cliente. O aplicativo cliente usa o token para a solicitação subsequente.
Ambiente de desenvolvimento
Tecnologias
- C#.NET
- Núcleo da API WEB ASP.NET
- Servidor SQL
Ferramentas de código aberto usadas
- Automapper (para mapeamento objeto a objeto)
- Entity Framework Core (para acesso a dados)
- Swashbucke (para documentação da API)
- XUnit (para caso de teste de unidade)
- Ocelot (para API Gateway Aggregation)
Serviços de plataforma em nuvem
- Azure App Insights (para log e monitoramento)
- Banco de dados SQL do Azure (para armazenamento de dados)
Projeto de banco de dados
Pontos de extremidade da WebApi
O aplicativo tem quatro terminais de API configurados no API Gateway para demonstrar os recursos com opções de segurança baseadas em token ativadas. Essas rotas são expostas ao aplicativo cliente para consumir os serviços de back-end.
End-points configurados e acessíveis por meio do API Gateway
- Rota: "/user/authenticate" [HttpPost] - Para autenticar o usuário e emitir um token
- Rota: "/account/balance" [HttpGet] - Para recuperar o saldo da conta.
- Rota: "/account/deposit" [HttpPost] - Para depositar valor em uma conta.
- Rota: "/account/withdraw" [HttpPost] - Para retirar o valor de uma conta.
End-points implementados no nível do microsserviço
- Rota: "/api/user/authenticate" [HttpPost]- Para autenticar o usuário e emitir um token
- Rota: "/api/account/balance" [HttpGet]- Para recuperar o saldo da conta.
- Rota: "/api/account/deposit" [HttpPost]- Para depositar valor em uma conta.
- Rota: "/api/account/withdraw" [HttpPost]- Para retirar o valor de uma conta.
Estrutura da solução
- Identity.WebApi
- Manipula a parte de autenticação usando nome de usuário, senha como parâmetro de entrada e emite um token JWT Bearer com informações de identidade de declarações nele.
- Transaction.WebApi
- Suporta três métodos http 'Balance', 'Deposit' e 'Withdraw'. Recebe solicitação http para esses métodos.
- Lida com exceção através de um middleware
- Lê as informações de identidade do cabeçalho de autorização que contém o token do portador
- Chama a função apropriada na estrutura 'Transação'
- Retorna o resultado da resposta da transação para o cliente
- Transaction.Framework
- Define a interface para a camada de repositório (dados) e camada de serviço (negócios)
- Define o modelo de domínio (objetos de negócios) e o modelo de entidade (modelo de dados)
- Define as exceções de negócios e a validação do modelo de domínio
- Define os tipos de dados necessários para o framework 'Struct', 'Enum', 'Consants'
- Implementa a lógica de negócios para realizar as transações de conta necessárias
- Implementa a lógica de dados para ler e atualizar os dados de e para o banco de dados SQL
- Executa a tarefa de mapear o modelo de domínio para o modelo de entidade e vice-versa
- Lida com o conflito de simultaneidade de atualização do banco de dados
- Cadastra as Interfaces e sua Implementação na Coleta de Serviços através de injeção de dependência
- Gateway.WebApi
- Valida a solicitação Http recebida verificando o token JWT autorizado.
- Reencaminhar o pedido Http para um serviço downstream.
- SimpleBanking.ConsoleApp
- Um aplicativo cliente de console que se conecta ao Api Gateway, pode ser usado para fazer login com nome de usuário, senha e realizar transações como 'Saldo', 'Depósito' e 'Retirada' em uma conta.
Manipulação de exceção
Um Middleware é escrito para lidar com as exceções e é registrado na inicialização para ser executado como parte da solicitação http. Cada solicitação http passa por esse middleware de manipulação de exceção e, em seguida, executa o método de ação do controlador da API da Web.
- Se o método de ação for bem-sucedido, a resposta de sucesso será enviada de volta ao cliente.
- Se qualquer exceção for lançada pelo método de ação, a exceção será capturada e tratada pelo Middleware e a resposta apropriada será enviada de volta ao cliente.
Tarefa assíncrona pública InvokeAsync (contexto HttpContext, RequestDelegate a seguir)
{
tentar
{
aguarde próximo(contexto);
}
pegar (Exceção ex)
{
var mensagem = CreateMessage(contexto, ex);
_logger.LogError(mensagem, ex);
aguarde HandleExceptionAsync(contexto, ex);
}
}
Manipulação de simultaneidade de banco de dados
A simultaneidade do banco de dados está relacionada a um conflito quando várias transações tentam atualizar os mesmos dados no banco de dados ao mesmo tempo. No diagrama abaixo, se você perceber que a Transação 1 e a Transação 2 estão na mesma conta, uma tentando depositar o valor na conta e o outro sistema tentando Retirar o valor da conta ao mesmo tempo. A estrutura contém duas camadas lógicas, uma lida com a lógica de negócios e a outra com a lógica de dados.
Quando um dado é lido do BD e quando a lógica de negócio é aplicada aos dados, neste contexto, haverá três estados diferentes para os valores relativos ao mesmo registro.
- Valores do banco de dados são os valores atualmente armazenados no banco de dados.
- Valores originais são os valores que foram originalmente recuperados do banco de dados
- Valores atuais são os novos valores que o aplicativo está tentando gravar no banco de dados.
O estado dos valores em cada uma das transações produz um conflito quando o sistema tenta salvar as alterações e identifica usando o token de concorrência que os valores que estão sendo atualizados no banco de dados não são os valores originais que foram lidos do banco de dados e lança DbUpdateConcurrencyException .
Referência: docs.microsoft.com
A abordagem geral para lidar com o conflito de simultaneidade é:
- Capture DbUpdateConcurrencyException durante SaveChanges
- Use DbUpdateConcurrencyException.Entries para preparar um novo conjunto de alterações para as entidades afetadas.
- Atualize os valores originais do token de simultaneidade para refletir os valores atuais no banco de dados.
- Repita o processo até que nenhum conflito ocorra.
while (! isSaved)
{
tentar
{
aguarde _dbContext.SaveChangesAsync();
isSaved = verdadeiro;
}
catch (DbUpdateConcurrencyException ex)
{
foreach (var entrada em ex.Entries)
{
if (entry.Entity é AccountSummaryEntity)
{
var databaseValues = entrada.GetDatabaseValues();
if (databaseValues != null)
{
entrada.OriginalValues.SetValues(databaseValues);
CalculateNewBalance();
void CalculateNewBalance()
{
var saldo = (decimal)entry.OriginalValues["Saldo"];
var valor = accountTransactionEntity.Amount;
if (accountTransactionEntity.TransactionType == TransactionType.Deposit.ToString())
{
accountSummaryEntity.Balance =
saldo += valor;
}
else if (accountTransactionEntity.TransactionType == TransactionType.Withdrawal.ToString())
{
if(valor > saldo)
lançar novo InsufficientBalanceException();
accountSummaryEntity.Balance =
saldo -= montante;
}
}
}
outro
{
lançar novo NotSupportedException();
}
}
}
}
}
Azure AppInsights: log e monitoramento
Azure AppInsights integrado ao "Microsserviço de Transação" para coletar a Telemetria do aplicativo.
public void ConfigureServices(serviços IServiceCollection)
{
services.AddApplicationInsightsTelemetry(Configuration);
}
AppInsights SDK para Asp.Net Core fornece um método de extensão AddApplicationInsights em ILoggerFactory para configurar o log. Todas as transações relacionadas a depósito e retirada são registradas por meio do ILogger nos logs do AppInsights.
public void Configure (aplicativo IApplicationBuilder, IHostingEnvironment env, log ILoggerFactory)
{
log.AddApplicationInsights(app.ApplicationServices, LogLevel.Information);
}
Para usar o AppInsights, você precisa ter uma conta do Azure e criar uma instância do AppInsights no Portal do Azure para seu aplicativo, que fornecerá uma chave de instrumentação que deve ser configurada no appsettings.json
"Insights de aplicativos": {
"InstrumentationKey": "<Sua chave de instrumentação>"
},
Swagger: Documentação da API
Pacote Swashbuckle Nuget adicionado ao "Transaction Microservice" e Swagger Middleware configurado no startup.cs para documentação da API. ao executar o serviço WebApi, a IU do swagger pode ser acessada por meio do endpoint swagger "/swagger".
public void ConfigureServices(serviços IServiceCollection)
{
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new Info { Title = "Simple Transaction Processing", Version = "v1" });
});
}
public void Configure (aplicativo IApplicationBuilder, IHostingEnvironment env, log ILoggerFactory)
{
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Simple Transaction Processing v1");
});
}
Coleção do Postman
Baixe a coleção do Postman aqui para executar os endpoints da API por meio do gateway
Como executar o aplicativo
- Baixe o script Sql aqui,
- Execute o script no servidor SQL para criar as tabelas necessárias e os dados de amostra
- Abra a solução (.sln) no Visual Studio 2017 ou versão posterior
- Configure a string de conexão SQL em Transaction.WebApi -> Appsettings.json file
- Configure a chave de instrumentação do AppInsights em Transaction.WebApi -> arquivo Appsettings.json. Se você não tiver uma chave ou não precisar de logs, comente o código relacionado ao AppInsight no arquivo Startup.cs
- Verifique o arquivo Identity.WebApi -> UserService.cs para informações de identidade. Os detalhes do usuário são codificados para algumas contas no serviço de identidade, que podem ser usadas para executar o aplicativo. Mesmos detalhes mostrados na tabela abaixo.
- Execute os seguintes projetos na solução
- Identity.WebApi
- Transação.WebApi
- Gateway.WebApi
- SimpleBanking.ConsoleApp
- O host e a porta do gateway devem ser configurados corretamente no ConsoleApp
- O host e a porta do serviço de identidade e transação devem ser configurados corretamente no gateway -> configuration.json
- Dados de amostra para testar
Número da conta | Moeda | Nome de usuário | Senha |
---|---|---|---|
3628101 | EUR | espectro | teste@123 |
3637897 | EUR | gwoodhouse | pass@123 |
3648755 | EUR | jsmith | admin@123 |