Neste post, gostaria de compartilhar a estrutura que venho adotando em meus projetos com microsserviços. São algumas ideias que tenho adotado com êxito e que podem ajudar você a ter mais sucesso em seus próprios projetos.
Se tiver interesse em entender mais sobre microsserviços, recomendo que acesse o Guia de Conteúdo para Microsserviços deste site.
1. Adote arquitetura hexagonal
Gosto muito do conceito de arqutitetura hexagonal (leia o post relacionado se não está familiarizado com o cenceito) e, para microsserviços, entendo que o conceito se ajusta perfeitamente.
Seguindo essa ideia, sempre que começo a criar um microsserviço em .NET, crio dois projetos. Um que irá operar conter a aplicação (entenda-se: código do domínio) e outro que irá expor essa aplicação para meios externos (geralmente um cliente de mensageria ou uma web api)
2. Organize seu código em Feature folders
Nunca me senti confortável com a separação de código em pastas técnicas (entenda-se Controllers, Views e Models). No lugar disso, prefiro organizar meu código por Features.
A idéia é manter Controller, Views, InputModels e ViewModels todos em uma única pasta agrupados pela feature que está sendo implementada.
Asp.Net Core é suficientemente inteligente para conseguir entender o que está ocorrendo com os Controllers e com as Models automaticamente. O único ajuste necessário é para explicitar onde estão as Views. De qualquer forma, algo simples:
using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.Razor; namespace Web.Features { public class FeaturesLocationExpander : IViewLocationExpander { public void PopulateValues(ViewLocationExpanderContext context) { // nothing } public IEnumerable ExpandViewLocations( ViewLocationExpanderContext context, IEnumerable viewLocations) { return new[] { "/Features/{1}/{0}.cshtml", // feature specific content "/Features/Shared/{0}.cshtml" // shared }; } } }
Este código instrui o ASP.net core sobre onde localizar as Views. Para ativar esse código, basta modificar a configuração do Razor.
public void ConfigureServices(IServiceCollection services) { services.AddIdentityApplication(_configuration); // services.AddMvc(); services.Configure<RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new FeaturesLocationExpander()); }); }
3. Identifique claramente as interfaces da aplicação (CommandSide e QuerySide)
Sou adepto a ideia de separar claramente interfaces de comando (que mudam o estado da aplicação) e consulta (que apenas recuperam informações) – entenda-se CQRS.
Essa segregação explícita ajuda a explicar melhor o conceito e tornar tudo mais evidente para quem vai interpretar o código.
4. Adote Swagger
Documentar a API é fundamental. Por isso, defendo a ideia de já começar o projeto usando Swagger. Por convenção, todos os meus microsserviços (apenas API), em seu endereço raiz, redirecionam para a interface Swagger.
using Microsoft.AspNetCore.Mvc; namespace WebApi.Features.Home { public class HomeController : Controller { public IActionResult Index() => new RedirectResult("~/swagger"); } }
Além disso, utilizo os pacotes Swashbuckle.AspNetCore e Swashbuckle.AspNetCore.Examples para fornecer uma experiência mais rica, fornecendo, inclusive, exemplos para dados que precisam ser enviados para o servidor.
[Authorize] [Route("addpf")] [HttpPut] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ClientePF), (int)HttpStatusCode.OK)] [SwaggerRequestExample(typeof(IncluirClientePFCommand), typeof(IncluirClientePFCommandExample))] public async Task<IActionResult> AddPF( [FromBody] IncluirClientePFCommand data, [FromHeader(Name = "x-requestid")] string requestId ) { if (!ModelState.IsValid) { return ModelState.GenerateBadRequestFromExceptions(); } Guid.TryParse(requestId, out var guid); var commandResult = await _mediator.Send(new IdentifiedCommand<IncluirClientePFCommand, ClientePF>( data, guid )); return commandResult != null ? (IActionResult)Ok(commandResult) : BadRequest(); }
Aliás, todos meus projetos de API contem uma pasta dedicada para códigos de exemplo para Swagger.
Concluindo
Sempre afirmo que “São as Cicatrizes que contam a história do guerreiro”. Essas recomendações são oriundas de algumas cicatrizes. Há muitas outras que poderia compartilhar, mas, por agora, gostaria de saber sua opinião. Quais dessas práticas você já adota?