Neste post, compartilho mais algumas ideias que tenho adotado, com êxito, em meus projetos envolvendo Microsserviços e que podem ajudar você a ser mais efetivo em suas implementações.
Se você ainda não leu, recomendo que acesse um post anterior sobre esse tema, com mais recomendações, antes de avançar.
1. Comece pelos comandos e pelas consultas que vai suportar
Antes de definir qualquer aspecto mais técnico, ter clareza sobre quais comandos e consultas seu microsserviço deverá suportar irá ajudar você a ser mais efetivo e mais alinhado com as necessidades do negócio.
No exemplo, apenas ilustrativo, o microsserviço irá manter um “contador”.
Tenho optado (não faz muito tempo) por usar o Mediatr para intermediar a chamada dos handlers, orquestrar validação das mensagegens, etc.
using System.Threading; using System.Threading.Tasks; using CountingMicroservice.Application.Services; using MediatR; namespace CountingMicroservice.Application.CommandSide.Commands { public class IncrementCommandHandler : IRequestHandler<IncrementCommand, bool> { private readonly ICounterRepository _repository; public IncrementCommandHandler(ICounterRepository repository) { _repository = repository; } public Task Handle( IncrementCommand request, CancellationToken cancellationToken ) { if (request == null) { return Task.FromResult(false); } _repository.Increment(); return Task.FromResult(true); } } }
2. Mantenha sua WebApi simples (se essa for sua interface)
Manter um projeto específico para a aplicação permite que o projeto da Web API seja extremamente simplificado. Caberá a WebAPI, apenas, fazer as devidas inicializações e prover features que “chamam” os comandos e consultas no momento apropriado.
Aliás, para aliviar acoplamento, tenho adotado por hábito fazer inicializações específicas da aplicação no código da aplicação (fora da web api)
using CountingMicroservice.Application.Services; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ServiceStack.Redis; namespace CountingMicroservice.Application { public static class StartupExtensions { public static IServiceCollection AddCounterApplication( this IServiceCollection services, IConfiguration configuration ) { var redisConnectionString = configuration["REDIS_CONNECTIONSTRING"] ?? "-counter.data"; services .AddSingleton(sp => new RedisManagerPool(redisConnectionString) ) .AddSingleton<ICounterRepository, RedisCounterRepository>(); return services; } public static IApplicationBuilder UseCounterApplication( this IApplicationBuilder app ) { return app; } } }
Fica dentro da WebAPI apenas inicializações que são específicas dela (Swagger, por exemplo)
using System; using Autofac; using Autofac.Extensions.DependencyInjection; using CountingMicroservice.Application; using CountingMicroservice.Application.CommandSide.Commands; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Swashbuckle.AspNetCore.Swagger; namespace CountingMicroservice.WebApi { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.RespectBrowserAcceptHeader = true; }); services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new Info() { Title = "Counting Microservice", Version = "v1", Description = "Basic Microservice Example" }); }); services.AddCounterApplication(Configuration); var container = new ContainerBuilder(); container.RegisterModule( MediatrModule.Create(typeof(StartupExtensions).Assembly) ); container.Populate(services); return new AutofacServiceProvider(container.Build()); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvcWithDefaultRoute(); app .UseSwagger() .UseSwaggerUI(options => { options.SwaggerEndpoint( "/swagger/v1/swagger.json", "Counting Microservice v1" ); }); app.UseCounterApplication(); } } }
3. Use e abuse do Docker
Provavelmente seu microsserviço irá rodar em um container;. Aproveite-se do suporte do Visual Studio para Docker e já comece seu projeto rodando e depurando em containers.
O efeito positivo dessa escolha é que ficará muito mais fácil gerenciar dependências de softwares tecerceiros. Veja o docker-compose.yml da aplicação exemplo
version: '3' services: countingmicroservice.webapi: image: countingmicroservice.webapi build: context: . dockerfile: CountingMicroservice.WebApi/Dockerfile depends_on: - redis redis: image: redis
No exemplo, não preciso mais me preocupar em ter um servidor redis rodando em meu Windows. Ao começar a rodar minha aplicação, uma imagem do Redis irá ser baixada e um container iniciado.
4. Use variáveis de ambiente para configuração
É muito mais fácil configurar sua aplicação usando variáveis de ambiente do que utilizando arquivos de configuração. No Asp.net Core, é simples fazer isso.
namespace CountingMicroservice.WebApi { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() .ConfigureAppConfiguration((builderContext, config) => { config.AddEnvironmentVariables(); }) .Build(); } }
Além disso, variáveis de ambiente são perfeitas combinadas ao Docker. No exemplo, configuro endereço do Redis no arquivo de composição. Isso simplificará muito o deploy mais tarde.
version: '3' services: countingmicroservice.webapi: environment: - ASPNETCORE_ENVIRONMENT=Development - REDIS_CONNECTIONSTRING=redis ports: - "5000:80" redis: ports: - "6379:6379"
Concluindo
Novamente, “são as cicatrizes que contam a história do guerreiro”.
- Sempre achei tedioso fazer o link entre meus Controllers e o código do domínio. Mediatr é uma “mão na roda” e a separação do microsserviço em projetos “adaptadores” e um “core” me ajuda a manter focado no negócio.
- A ideia de implementar comandos e consultas me faz manter o foco nas histórias de negócio ao invés de dados colecionados.
- Sempre achei péssimo instalar bancos de dados e outros artefatos em meu computador apenas para desenvolver – Docker é vida!
- Sempre odiei o web.config! Felizmente, não preciso mais dele. Adoro a ideia de poder usar simples variáveis de ambiente para configurar minha aplicação.
Novamente, há bem mais que poderia ser compartilhado. Entretanto, mais uma vez, é hora de pedir a sua opinião.