.NET - Apresentando a arquitetura de Microsserviços - II


  Neste artigo vamos iniciar a apresentação da arquitetura de Microsserviços e mostrar uma  implementação na plataforma .NET

Continuando o artigo anterior vou apresentar um exemplo básico de utilização de arquitetura de microsserviços na plataforma .NET.

A figura a seguir ilustra a arquitetura da aplicação que iremos criar :

Neste exemplo teremos 3 microsserviços representados por aplicações asp.net core web api definidos da seguinte forma:

  1. O microsserviço para Customers que vai atender na porta 7281 e que vai usar o banco de dados MySQL
  2. O microsserviço para Orders que vai atender na porta 7147 e que vai usar o banco de dados SQLite
  3. O microsserviço para Products que vai atender na porta 7089 e que vai usar o banco de dados SQL Server

Nestes microsserviços, para simplificar, eu estou realizando o CRUD básico onde cada um desses microsserviços será um projeto WebAPI independente, cada um com seus próprios bancos de dados e hospedados em servidores diferentes.

Teremos tambem o projeto
Ecommerce-APIGateway onde vamos definir a API Gateway responsável por redirecionar a solicitação recebida do cliente que vai atender na porta 7133.

E para isso vamos usar o OCELOT.

A API Gateway é o ponto de entrada para clientes onde todas as chamadas do cliente serão atendidas, analisadas e encaminhadas para os serviços apropriados.

Em vez de chamar cada microsserviço individualmente, vamos configurar o APIGateway como um host e o cliente se conecta ao gateway, que por sua vez redireciona os dados/endpoints de/para os microsserviços.

O Ocelot é um API Gateway leve, recomendado para abordagens mais simples sendo uma API Gateway de código aberto baseado em .NET Core feito especialmente para arquiteturas de microsserviços que precisam de pontos de entrada unificados em seus sistemas. É leve, rápido e escalonável e fornece roteamento e autenticação, entre muitos outros recursos.

O principal motivo para escolher o Ocelot é porque ele é um API Gateway leve do .NET Core que você pode implantar no mesmo ambiente de implantação do aplicativo onde está implantando seus microsserviços/contêineres, como um Docker Host, Kubernetes , etc. E como é baseado no .NET Core, é multiplataforma, permitindo a implantação em Linux ou Windows.

No projeto BusinessObjects que é um projeto do tipo Class Library é onde definimos as entidades usadas no projeto : Customer, Order e Product.

E no projeto Ecommerce-APIGateway definimos as configurações dos serviços do OCELOT no arquivo apiGateway.json :

{
  "GlobalConfiguration": {
    "BaseUrl": "https://localhost:7133"
  },
  "Routes": [
    {
      // Products Microservice Route
      "DownstreamPathTemplate": "/api/products",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 7089
        }
      ],
      "UpstreamPathTemplate": "/gateway/product",
      "RateLimitOptions": {
        "ClientWhiteList": [],
        "EnableRateLimiting": true,
        "Period": "1s",
        "PeriodTimespan": 1,
        "Limit": 1
      },
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ]
    },
    {
      "DownstreamPathTemplate": "/api/products/{id}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 7089
        }
      ],
      "UpstreamPathTemplate": "/gateway/product/{id}",
      "UpstreamHttpMethod": [ "GET", "DELETE" ]
    },
    // Orders Microservice Route
    {
      "DownstreamPathTemplate": "/api/orders",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 7147
        }
      ],
      "UpstreamPathTemplate": "/gateway/order",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ]
    },
    {
      "DownstreamPathTemplate": "/api/orders/{id}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 7147
        }
      ],
      "UpstreamPathTemplate": "/gateway/order/{id}",
      "UpstreamHttpMethod": [ "GET", "DELETE" ]
    },
    // Customers Microservice Route
    {
      "DownstreamPathTemplate": "/api/customers",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 7281
        }
      ],
      "UpstreamPathTemplate": "/gateway/customer",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ]
    },
    {
      "DownstreamPathTemplate": "/api/customers/{id}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 7281
        }
      ],
      "UpstreamPathTemplate": "/gateway/customer/{id}",
      "UpstreamHttpMethod": [ "GET", "DELETE" ]
    }
  ]
}

O código acima define a configuração do Ocelot que define rotas para redirecionar solicitações para microsserviços individuais, neste caso, os microsserviços "Product", "Order" e "Customer".

Vejamos como funciona:

  1. Configuração Global:
  2. Rotas para o Microsserviço "Product":
  3. Rotas para o Microsserviço "Order":
  4. Rotas para o Microsserviço "Customer":

Neste projeto temos na classe Program a configuração para leitura do arquivo json e a utilização do cache para o OCELOT:

using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Cache.CacheManager;
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("apiGateway.json", optional: false, 
                                   reloadOnChange: true);
builder.Services.AddOcelot(builder.Configuration)
    .AddCacheManager(x =>
    {
        x.WithDictionaryHandle();
    });
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
await app.UseOcelot();
app.Run();

Microsserviços

Cada microsserviço vai possuir um arquivo de contexto do EF Core definindo o mapeamento ORM :

1- CustomerServiceContext

public class CustomerServiceContext : DbContext
{
    public CustomerServiceContext(DbContextOptions<CustomerServiceContext> options)
        : base(options)
    {
    }
    public DbSet<BusinessObjects.Customer>? Customers { get; set; }
}

2- OrderServiceContext

public class OrderServiceContext : DbContext
{
    public OrderServiceContext(DbContextOptions<OrderServiceContext> options)
        : base(options)
    {
    }
    public DbSet<BusinessObjects.Order>? Orders { get; set; }
}

3- ProductServiceContext

public class ProductServiceContext : DbContext
{
    public ProductServiceContext(DbContextOptions<ProductServiceContext> options)
        : base(options)
    {
    }
    public DbSet<BusinessObjects.Product>? Products { get; set; }
}

A seguir temos a configuração da string de conexão no arquivo appsettings e do serviço do contexto na classe Program para cada microsserviço :

1- CustomerServiceContext - MySQL

appsettings.json

"ConnectionStrings": {
"CustomerServiceContext": "Server=localhost;DataBase=CustomersDB;Uid=root;Pwd=*****"
}
...

Program:

string mySqlConnection = builder.Configuration.GetConnectionString("CustomerServiceContext")
                         ?? throw new
                         InvalidOperationException("Connection string 'CustomerServiceContext' not found.");
builder.Services.AddDbContext<CustomerServiceContext>(options =>
                    options.UseMySql(mySqlConnection,
                    ServerVersion.AutoDetect(mySqlConnection)));

2- OrderServiceContext - SQLite

appsettings.json

"ConnectionStrings": {
 
"OrderServiceContext": "Data Source=OrdersDB.db"
}
...

Program:

// Add services to the container.
builder.Services.AddDbContext<OrderServiceContext>(Options =>
{
    Options.UseSqlite(builder.Configuration.GetConnectionString("OrderServiceContext"));
});

3- ProductServiceContext - SQL Server

appsettings.json

"ConnectionStrings": {
"ProductServiceContext": "Data Source=;Database=ProductsDB;Trusted_Connection=True;MultipleActiveResultSets=true;
TrustServerCertificate=True;"

}...

Program:

...
builder.Services.AddDbContext<ProductServiceContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("ProductServiceContext") 
    ?? throw new InvalidOperationException("Connection string 'ProductServiceContext' not found.")));                            
...

Em cada projeto de cada microsserviços temos a definição de um controlador contendo os métodos Action para realizar as operações CRUD:

O código de cada controlador poderá ser consultado no código fonte pois foi gerado via scafold.

Para executar o projeto devemos configurar o modo Multiple startup projects conforme abaixo:

A figura a seguir ilustra o funcionamento da aplicação e da API Gateway implementada usando o OCELOT :

Pegue o código do projeto aqui:  https://github.com/macoratti/Microservices-NET

"No dia seguinte João viu a Jesus, que vinha para ele, e disse: Eis o Cordeiro de Deus, que tira o pecado do mundo."
João 1:29

Referências:


José Carlos Macoratti