Cell CMS — Validando Inputs e Mapeamento Automático
Tags: dotnet, rest, csharp, automapper
Published: 2020-06-24 23:59:13

---
title: Cell CMS — Validando Inputs e Mapeamento Automático
published: true
date: 2020-04-09 01:45:40 UTC
tags: dotnet,rest,csharp,automapper
canonical_url: 
series: CellCms
---

### Cell CMS — Validando Inputs e Mapeamento Automático

![](https://cdn-images-1.medium.com/max/979/1*I9Sfn7ezDH_U7SWPFbnCwQ.png)<figcaption>Cell-CMS, rodando no Kestrel</figcaption>

No [último post](https://dev.to/ardc_overflow/cell-cms-design-patterns-e-endpoints-86a-temp-slug-1020308) falamos sobre o **MediatR** e o _design pattern_ **Mediator,** implementando os endpoints com Commands e Queries. No post de hoje vamos continuar de onde paramos e adicionar **validação** aos nossos **Commands** e realizar o **mapeamento automático** dos Commands e seus campos para nossos  **Models**.

Hoje faremos dois novos branches: feature/fluent-validation e feature/auto-mapper, um deles será para a instalação e configuração do **FluentValidation** e o outro para a instalação de configuração do **AutoMapper**.

**Validando inputs com a sintaxe padrão**

Por padrão, no universo .NET, temos os DataAnnotations para realizar a validação automática de nossos objetos. A documentação completa para eles pode ser encontrada [neste link](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=netcore-3.1).

Utilizando estes componentes temos de, em nossas classes, adicionar alguns Attributes para realizar as validações. Por exemplo, em um Command, eu poderia realizar a validação da seguinte maneira:

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%28171%252C%2520184%252C%2520195%252C%25201%29%26t%3Done-dark%26wt%3Dnone%26l%3Dtext%252Fx-csharp%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dusing%252520System%25253B%25250Ausing%252520System.ComponentModel.DataAnnotations%25253B%25250A%25250Ausing%252520CellCms.Api.Models%25253B%25250A%25250Ausing%252520MediatR%25253B%25250A%25250Anamespace%252520CellCms.Api.Features.Feeds%25250A%25257B%25250A%252520%252520%252520%252520%25252F%25252F%25252F%252520%25253Csummary%25253E%25250A%252520%252520%252520%252520%25252F%25252F%25252F%252520Command%252520para%252520criar%252520um%252520novo%252520Feed.%25250A%252520%252520%252520%252520%25252F%25252F%25252F%252520%25253C%25252Fsummary%25253E%25250A%252520%252520%252520%252520public%252520class%252520CreateFeed%252520%25253A%252520IRequest%25253CFeed%25253E%25250A%252520%252520%252520%252520%25257B%25250A%252520%252520%252520%252520%252520%252520%252520%252520%25252F%25252F%25252F%252520%25253Csummary%25253E%25250A%252520%252520%252520%252520%252520%252520%252520%252520%25252F%25252F%25252F%252520Nome%252520do%252520novo%252520feed.%25250A%252520%252520%252520%252520%252520%252520%252520%252520%25252F%25252F%25252F%252520%25253C%25252Fsummary%25253E%25250A%252520%252520%252520%252520%252520%252520%252520%252520%25255BRequired%28AllowEmptyStrings%252520%25253D%252520false%25252C%252520ErrorMessage%252520%25253D%252520%252522Um%252520nome%252520deve%252520ser%252520informado%252522%29%25255D%25250A%252520%252520%252520%252520%252520%252520%252520%252520public%252520string%252520Nome%252520%25257B%252520get%25253B%252520set%25253B%252520%25257D%25250A%252520%252520%252520%252520%25257D%25250A%25257D&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%28171%25252C%252520184%25252C%252520195%25252C%2525201%29%26t%3Done-dark%26wt%3Dnone%26l%3Dtext%25252Fx-csharp%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dusing%25252520System%2525253B%2525250Ausing%25252520System.ComponentModel.DataAnnotations%2525253B%2525250A%2525250Ausing%25252520CellCms.Api.Models%2525253B%2525250A%2525250Ausing%25252520MediatR%2525253B%2525250A%2525250Anamespace%25252520CellCms.Api.Features.Feeds%2525250A%2525257B%2525250A%25252520%25252520%25252520%25252520%2525252F%2525252F%2525252F%25252520%2525253Csummary%2525253E%2525250A%25252520%25252520%25252520%25252520%2525252F%2525252F%2525252F%25252520Command%25252520para%25252520criar%25252520um%25252520novo%25252520Feed.%2525250A%25252520%25252520%25252520%25252520%2525252F%2525252F%2525252F%25252520%2525253C%2525252Fsummary%2525253E%2525250A%25252520%25252520%25252520%25252520public%25252520class%25252520CreateFeed%25252520%2525253A%25252520IRequest%2525253CFeed%2525253E%2525250A%25252520%25252520%25252520%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525252F%2525252F%2525252F%25252520%2525253Csummary%2525253E%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525252F%2525252F%2525252F%25252520Nome%25252520do%25252520novo%25252520feed.%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525252F%2525252F%2525252F%25252520%2525253C%2525252Fsummary%2525253E%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525255BRequired%28AllowEmptyStrings%25252520%2525253D%25252520false%2525252C%25252520ErrorMessage%25252520%2525253D%25252520%25252522Um%25252520nome%25252520deve%25252520ser%25252520informado%25252522%29%2525255D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520public%25252520string%25252520Nome%25252520%2525257B%25252520get%2525253B%25252520set%2525253B%25252520%2525257D%2525250A%25252520%25252520%25252520%25252520%2525257D%2525250A%2525257D&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/0b97caf17dadc5cb014839cb690dbf6d/href">https://medium.com/media/0b97caf17dadc5cb014839cb690dbf6d/href</a></iframe>

**A vantagem** desta abordagem é que o .NET **MVC automaticamente identifica** estas anotações e as **executa** , permitindo que acessemos seu status através do ModelState! Desta maneira podemos, por exemplo, **verificar o ModelState em nossos endpoints e retornar** erros de validação como **BadRequests**.

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%28171%252C%2520184%252C%2520195%252C%25201%29%26t%3Done-dark%26wt%3Dnone%26l%3Dtext%252Fx-csharp%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%25255BHttpPost%25255D%25250A%25255BAuthorize%25255D%25250Apublic%252520async%252520Task%25253CIActionResult%25253E%252520Create%28%25255BFromBody%25255D%252520CreateFeed%252520command%29%25250A%25257B%25250A%252520%252520%252520%252520%25252F%25252F%252520O%252520ModelState%252520%2525C3%2525A9%252520automaticamente%252520criado%252520pelo%252520MVC%252520Middleware%252520e%252520Populado%252520com%252520o%252520status%25250A%252520%252520%252520%252520if%252520%28%21ModelState.IsValid%29%25250A%252520%252520%252520%252520%25257B%25250A%252520%252520%252520%252520%252520%252520%252509%25252F%25252F%252520Caso%252520n%2525C3%2525A3o%252520seja%252520v%2525C3%2525A1lido%252520o%252520ModelState%252520pode%252520ser%252520retornado%252520em%252520uma%252520BadRequest%252520com%252520os%252520erros%252520de%252520valida%2525C3%2525A7%2525C3%2525A3o%21%25250A%252520%252520%252520%252520%252520%252520%252520%252520return%252520BadRequest%28ModelState%29%25253B%25250A%252520%252520%252520%252520%25257D%25250A%25250A%252520%252520%252520%252520try%25250A%252520%252520%252520%252520%25257B%25250A%252520%252520%252520%252520%252520%252520%252520%252520var%252520result%252520%25253D%252520await%252520_mediator.Send%28command%29%25253B%25250A%252520%252520%252520%252520%252520%252520%252520%252520return%252520Created%28string.Empty%25252C%252520result%29%25253B%25250A%252520%252520%252520%252520%25257D%25250A%252520%252520%252520%252520catch%252520%28Exception%252520ex%29%25250A%252520%252520%252520%252520%25257B%25250A%252520%252520%252520%252520%252520%252520%252520%252520_logger.LogError%28ex%25252C%252520%252522Erro%252520ao%252520tentar%252520criar%252520um%252520novo%252520Feed%25253A%252520%25257B%252540Command%25257D%252522%25252C%252520command%29%25253B%25250A%252520%252520%252520%252520%252520%252520%252520%252520throw%25253B%25250A%252520%252520%252520%252520%25257D%25250A%25257D&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%28171%25252C%252520184%25252C%252520195%25252C%2525201%29%26t%3Done-dark%26wt%3Dnone%26l%3Dtext%25252Fx-csharp%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%2525255BHttpPost%2525255D%2525250A%2525255BAuthorize%2525255D%2525250Apublic%25252520async%25252520Task%2525253CIActionResult%2525253E%25252520Create%28%2525255BFromBody%2525255D%25252520CreateFeed%25252520command%29%2525250A%2525257B%2525250A%25252520%25252520%25252520%25252520%2525252F%2525252F%25252520O%25252520ModelState%25252520%252525C3%252525A9%25252520automaticamente%25252520criado%25252520pelo%25252520MVC%25252520Middleware%25252520e%25252520Populado%25252520com%25252520o%25252520status%2525250A%25252520%25252520%25252520%25252520if%25252520%28%21ModelState.IsValid%29%2525250A%25252520%25252520%25252520%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252509%2525252F%2525252F%25252520Caso%25252520n%252525C3%252525A3o%25252520seja%25252520v%252525C3%252525A1lido%25252520o%25252520ModelState%25252520pode%25252520ser%25252520retornado%25252520em%25252520uma%25252520BadRequest%25252520com%25252520os%25252520erros%25252520de%25252520valida%252525C3%252525A7%252525C3%252525A3o%21%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520return%25252520BadRequest%28ModelState%29%2525253B%2525250A%25252520%25252520%25252520%25252520%2525257D%2525250A%2525250A%25252520%25252520%25252520%25252520try%2525250A%25252520%25252520%25252520%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520var%25252520result%25252520%2525253D%25252520await%25252520_mediator.Send%28command%29%2525253B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520return%25252520Created%28string.Empty%2525252C%25252520result%29%2525253B%2525250A%25252520%25252520%25252520%25252520%2525257D%2525250A%25252520%25252520%25252520%25252520catch%25252520%28Exception%25252520ex%29%2525250A%25252520%25252520%25252520%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520_logger.LogError%28ex%2525252C%25252520%25252522Erro%25252520ao%25252520tentar%25252520criar%25252520um%25252520novo%25252520Feed%2525253A%25252520%2525257B%25252540Command%2525257D%25252522%2525252C%25252520command%29%2525253B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520throw%2525253B%2525250A%25252520%25252520%25252520%25252520%2525257D%2525250A%2525257D&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/a16692cc8c37122cd620e1225ccbdb0e/href">https://medium.com/media/a16692cc8c37122cd620e1225ccbdb0e/href</a></iframe>

**A desvantagem** desta abordagem é que estamos **misturando lógicas de validação** com lógicas de **armazenamento** de nossas classes (_se você for da galera SOLID, podemos dizer que fere o Single Resposability de nossa classe_). **Outra desvantagem** é que esta abordagem é prática **apenas para validações mais simples**. Se, em algum momento, você decidir realizar uma **validação assincrona ou mais complexa deverá criar seu próprio Attribute**.

> Mas Rodolpho, existe outra maneira que possamos realizar validações sem criar nosso próprio framework ou poluir o código de execução com infinitos IFs!?

Sim! Existem alternativas prontas para utilizarmos! Uma delas é o **FluentValidation!**

**Validando inputs com sintaxe Fluent — FluentValidation**

Utilizando a biblioteca **FluentValidation** podemos escrever nossas **validações em classes separadas** , **integrar estas validações** com o middleware **MVC** e **utilizar** o mesmo **ModelState** para armazenar nossos resultados e erros de validação!

No Projeto da API adicione o NuGetFluentValidation.AspNetCore às dependências. Primeiros vamos configurar a integração com o MVC! Acesse o seu Startup.cs e realize as seguintes alterações:

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%2FFvTCCweDwgY6sFYLv31i%3F&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2FFvTCCweDwgY6sFYLv31i&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/621794af1d506a884da60c4d6796ffe0/href">https://medium.com/media/621794af1d506a884da60c4d6796ffe0/href</a></iframe>

Agora precisamos escrever nossas regras de validação. Utilizarei o CreateFeed como exemplo. **Crie uma nova classe** (pode ser em um novo arquivo ou no CreateFeed.cs) chamada **CreateFeedValidator** e **herde** a classe **AbstractValidator<CreateFeed>.** Agora, graças ao AbstractValidator temos acesso **à sintaxe Fluent dentro do Construtor** da nossa classe. Com isso podemos escrever nossas regras de validação:

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%2FABsWPRCj8wyA5Vhc8zaD%3F&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2FABsWPRCj8wyA5Vhc8zaD&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/98defd0c7d54db49279ec9b18cfd1ed6/href">https://medium.com/media/98defd0c7d54db49279ec9b18cfd1ed6/href</a></iframe>

Outras vantagens do FluentValidations são:

- Criação de **Rulesets** para agrupar as validações (por exp: _Ruleset para validações síncronas e assíncronas_)
- Validação **condicionais** (por exp: _Se informar X, Y não pode ser null_)
- Validações **assíncronas** (por exp: _Consultar no Context se existe alguém com a Propriedade X ou Y_)

Por exemplo uma regra assíncrona para verificar que não existe um Content com o mesmo Nome na base de dados:

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%2FAOZxZSmZ29bVpzgCctVo%3F&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2FAOZxZSmZ29bVpzgCctVo&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/f3f0c67e78fd87ceb07c796f20460aa0/href">https://medium.com/media/f3f0c67e78fd87ceb07c796f20460aa0/href</a></iframe>

A documentação completa está disponível em [https://fluentvalidation.net/](https://fluentvalidation.net/) e sugerido uma boa olhada! A biblioteca é bem ampla e possui várias funcionalidades úteis.

Só para não deixar passar vazio temos um exemplo da validação para uma request inválida:

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%2Fhy8d9edfJy4wXDEDLOYP%3F&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2Fhy8d9edfJy4wXDEDLOYP&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/ab5dfc1ca3fd881ee8ec443921ccb36b/href">https://medium.com/media/ab5dfc1ca3fd881ee8ec443921ccb36b/href</a></iframe>

**Mapeando propriedades manualmente**

Atualmente realizamos a “tradução” de nossos Commands de maneira manual. Ou seja, **criamos um novo Model** e o **preenchemos com os valores que vieram do nosso Command.** Por exemplo, na atualização de uma Tag:

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%2FvmpuibWT2OILeHVQjb04%3F&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2FvmpuibWT2OILeHVQjb04&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fapi%2Fimage%2FvmpuibWT2OILeHVQjb04&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/600dce8952265d1131f6521cba29bd28/href">https://medium.com/media/600dce8952265d1131f6521cba29bd28/href</a></iframe>

Isso, com o passar do tempo, torna-se repetitivo e meio braçal. Uma solução seria escrevermos **Adaptors** ou **Factories** (_design patterns_) para separar a lógica do mapeamento da lógica de execução, porém **ainda temos o problema original** , salvo que **escrevamos algo que realiza o mapeamento através de Reflection,** com base nos nomes das Propriedades de Origem e Destino.

Felizmente alguém já escreveu isto para nós! Do mesmo criador que o **MediatR** temos o famosíssimo **AutoMapper**!

**Mapeando propriedades automaticamente — AutoMapper**

Utilizando a biblioteca **AutoMapper** podemos contar com **o mapeamento automático** de propriedades **através de padrões de nomenclatura e Reflection.**

Para começarmos vamos adicionar o NuGet AutoMapper.Extensions.Microsoft.DependencyInjection à API e alterar nosso Startup.cs para registrar o AutoMapper nos services injetáveis:

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%2FS8iIjuOAePH2OqPL0JFC%3F&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2FS8iIjuOAePH2OqPL0JFC&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/c911a184182bfb24c2616d1f2a844ae9/href">https://medium.com/media/c911a184182bfb24c2616d1f2a844ae9/href</a></iframe>

Agora precisamos criar nossos **Mapeamentos**. O AutoMapper, ao ser utilizado com injeção de dependência, requer que criemos **“Profiles”** para organizar nossos mapeamentos. Estes Profiles irão conter o “registro” para que o IMapper saiba entre quais objetos pode atuar!

Comecei criando o Profile para nossos Feeds no arquivo Features/Feeds/FeedProfile.cs:

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%2FpNX7XGq2unUIai8eWrN5%3F&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2FpNX7XGq2unUIai8eWrN5&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/2417ce1e26aa2c545339673cbe8f76a5/href">https://medium.com/media/2417ce1e26aa2c545339673cbe8f76a5/href</a></iframe>

Para utilizarmos o Profile basta injetar o IMapper através do construtor e utilizar os métodos Map<T>() para delegar ao AutoMapper o trabalho de popular as propriedades!

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%2Fcrb6DEgHfjxtpO1bv6Ok%3F&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2Fcrb6DEgHfjxtpO1bv6Ok&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/8f1c8241899635e2fa32d1423c30aa93/href">https://medium.com/media/8f1c8241899635e2fa32d1423c30aa93/href</a></iframe>

Assim como o FluentValidation o **AutoMapper** é amplamente customizável e você pode dar uma olhada na documentação completa em [https://automapper.org/](https://automapper.org/)!

**Considerações Finais**

Hoje vimos duas bibliotecas bem úteis para o universo .NET, seja para o Frontend ou para o Backend estas bibliotecas nos auxiliam a manter o código limpo e organizado!

Para o próximo post a ideia será melhorarmos o processo de **Log de Erros** utilizando o **Serilog,** obter métricas automaticamente com o **Application Insights** e adicionarmos **Healthchecks** à nossa API e banco de dados!

Obrigado por lerem mais este post e até a próxima! Abraços!