Criando Web APIs modernas, autônomas e rastreáveis com .Net Core utilizando arquitetura de microsserviços – Parte 1. Construindo o projeto

Com a crescente adoção da arquitetura de microsserviços, inúmeras vantagens são agregadas à rotina dos desenvolvedores de software. Estas vantagens contribuem para a qualidade final dos produtos de software construídos.

No entanto, algumas complexidades são, inevitavelmente acrescentadas.

Vantagens Desvantagens
Processo de implantação independente Maior complexidade operacional
Possibilidade de diversidade tecnológica Necessidade de construir documentação para diversos microssistemas
Feedbacks mais rápidos Maior dificuldade para encontrar pontos de falhas
Facilidade o processo de entrega contínua Custo de serialização e desserialização

Estas, são apenas algumas das vantagens e desvantagens deste padrão arquitetural. A intenção desta série de artigos, é te auxiliar a mitigar uma parte desta complexidade.

1. O que você irá aprender nesta série de artigos?

Enquanto desenvolvemos nossos sistemas utilizando a arquitetura tradicional, também conhecida como arquitetura monolítica, geralmente, não nos preocupamos com alguns fatores, que, à medida que a solução cresce, necessitam de uma atenção especial, são eles:

1.1. Documentação automática das Web APIs

Conforme ganhamos experiência na atividade de desenvolvimento de software, veremos o quão difícil é manter uma documentação clara, objetiva e o mais importante, perfeitamente sincronizada com as inúmeras funcionalidades que criamos diariamente, ou as funcionalidades alteradas, à medida que os requisitos do sistema vão sendo atualizados.

Em um momento ou outro, devido a prazos apertados ou outros fatores que fogem do nosso controle, algum tópico da documentação não estará mais de acordo com a funcionalidade documentada, a partir deste ponto, caso não seja possível atualizar esta parte da documentação, a documentação como um todo não fará mais sentido, pois perdeu a sincronia com a solução. O esforço e o investimento para manter o sincronismo será cada vez maior, pois será difícil ter certeza de que todos os tópicos da documentação estão atualizados.

Este não é um problema exclusivo da arquitetura baseada em microsserviços, porém quando trabalhamos com a arquitetura monolítica, toda a nossa API é disponibilizada em uma única unidade de software, em uma única url (endereço da web), onde serão disponibilizados todos os endpoints que entregam as funcionalidades desenvolvidas. Isto por si só, já torna mais fácil para um membro novo da equipe, ter um entendimento geral do funcionamento do software. Além de ser mais fácil também, para o consumidor externo de nossa API, encontrar em um só local toda a lista de serviços disponíveis. 

Atenção: É uma prática recomendada, a adoção do padrão de Gateway de API, para o fornecimento centralizado de funcionalidades de nossas APIs em um único endereço da web, por mais que a implementação esteja distribuída em vários microsserviços, porém este assunto não é tema deste artigo. Sobre isto, você poderá aprender mais aqui.

À medida que avançamos na adoção da arquitetura de microsserviços, as funcionalidades de nossa API não serão mais entregues em um único endereço da web, mas em vários. Com isto, torna-se necessário que desenvolvamos uma documentação específica, focada no assunto de cada microsserviço, para cada componente de software que agregamos à nossa solução. Com a adoção dos microsserviços, não deveremos mais construir uma única base de documentação, mas uma para cada componente novo de software que for construído.

Felizmente, temos disponíveis para nos auxiliar na documentação de nossas APIs, uma especificação amplamente utilizada e consolidada, chamada OpenAPI Specification, originalmente conhecida como Swagger Specification.

Com poucas linhas de código e algumas configurações, teremos uma documentação dinâmica e viva, que, além de explicitar todas as funcionalidades de cada Web API, ainda nos permite testar todas as funcionalidades da API, em uma interface gráfica interativa, gerada automaticamente, à medida que criamos novos endpoints em nossa solução.

Temos ainda, a opção de utilizar parte dos comentários incluídos no código fonte como parte desta documentação. Explicarei este assunto mais à frente nesta série de artigos.

Para a nosso projeto de demonstração, utilizaremos a seguinte implementação da especificação OpenAPI para projetos .NET:

  • NSwag ⇒ Cadeia de ferramentas Swagger/OpenAPI para .NET, ASP.NET Core e TypeScript.

Existe outra implementação, amplamente utilizada e igualmente difundida, chamada Swashbuckle. Você pode fazer um comparativo entre estas implementações e escolher a que fizer mais sentido para o seu contexto. Porém para efeito prático, neste artigo, utilizaremos o NSwag.

1.2. Depuração e rastreamento de falhas

Enquanto sua Web API é um monólito, ou seja, construído com apenas um componente de software (1 – Solution ⇒ 1 – Projetct), basta executar o projeto em uma janela do seu IDE, incluir os breakpoints e iniciar a depuração linha a linha, à procura da falha.

Porém, quando iniciamos a migração para a arquitetura de microsserviços, nossa solução começa a ser fragmentada (granularidade), à medida que novos componentes (microsserviços) são acrescentados. Este aumento do número de componentes em si, não é um problema, mas indiretamente, começa a tornar a depuração e descoberta de erros cada vez mais difícil.

Antes, na arquitetura monolítica, existia apenas uma unidade de software potencialmente, causadora de problemas. Mas com a adoção da arquitetura de microsserviços, temos um leque cada vez maior de possíveis pontos de falhas.

Abrir uma instância do IDE para depurar um projeto, procurando as falhas? Ok. Agora tente acessar 05, 10 ou 15 instâncias do seu IDE, cada uma com um projeto diferente e constate na prática que não é uma prática sustentável.

Felizmente, nem tudo está perdido. Existe um conjunto de práticas e ferramentas, as quais, nos auxiliam neste processo de depuração descentralizado, também conhecido como distributed tracing (rastreamento distribuído).

Neste artigo, abordaremos uma possível solução deste cenário, utilizando as seguintes especificações/ferramentas:

OpenTracing ⇒ Trata-se de uma especificação de API, estruturas e bibliotecas que implementaram a especificação e documentação para o projeto. O OpenTracing permite que os desenvolvedores adicionem instrumentação ao código do aplicativo usando APIs que não os bloqueiam em nenhum produto ou fornecedor em particular; 

OpenTracing API for .NET ⇒ Implementação da especificação do OpenTracing para C# (.NET); 

Jaeger UI ⇒ Interface gráfica para visualização dos eventos registrados pelo OpenTracing API for .NET

Imagem 1

2. Projeto de exemplo 

Para demonstrar a utilização das técnicas e ferramentas apresentadas anteriormente neste artigo, construiremos um grupo de Web APIs para gerenciamento de um sistema ERP. Constituído do seguinte conjunto de Web APIs em .NET Core: 

Projeto exemplo

2.1. Criando os projetos

Para a criação dos projetos, utilizarei o Microsoft Visual Studio 2019.

Dentro do Visual Studio, clique no menu File ⇒ New Project:

Novo projeto

Selecione a opção ASP.NET Core Web Application e clique em Next.

Novo projeto2

Dê o nome do primeiro projeto de Api.Erp.Clientes e o nome Api.Erp para a solution, conforme a figura anterior. Clique no botão Create.

Novo projeto3

Selecione o template API e clique no botão Create, para finalizar a criação da solution e do primeiro projeto.

A seguinte tela será exibida, com o primeiro projeto criado:

Primeiro projeto criadoClique com o botão direito no ícone da solution, selecione a opção Add ⇒ New Project. Selecione novamente a opção ASP.NET Core Web Application e clique em Next. Desta vez, dê o nome Api.Erp.Comercial para o novo projeto, clique em Next, e utilize novamente o template API.

Repita o passo anterior, porém desta vez, criando um novo projeto chamado Api.Erp.Fiscal.

No final destes passos, teremos uma solution com três projetos, no seguinte padrão:

Todas as APIs criadas

Exclua os arquivos WeatherForecast.cs e WeatherForecastController.cs de cada um dos três projetos criados. Pois são apenas demonstrações do template API criados automaticamente pelo Visual Studio.

2.1.1. Criando o projeto Api.Erp.Shared

Adicione um novo projeto chamado Api.Erp.Shared do tipo Class Library (.NET Core).

A função deste projeto, é armazenar conteúdos que serão comuns a todos os demais projetos. Uma boa prática para centralização de funcionalidades, evitando replicação de código desnecessária.

Após criar o projeto Api.Erp.Shared, delete o arquivo Class1.cs.

Teremos a seguinte estrutura neste momento:

Todos os projetos criados

Atenção: Como a nossa intenção aqui é simplesmente demostrar a criação de documentação automática com o NSwag e posteriormente habilitar o OpenTracing para monitoramento de nossas APIs, as deixaremos o mais simples possível. Portanto, não se atenha aos detalhes de implementação de cada uma delas bem como aspectos relacionados a boas práticas de arquitetura de software.

Clique com o botão direito no projeto Api.Erp.Shared, selecione Add à New Folder e digite o nome ViewModels para o diretório.

No diretório …Api.Erp\Api.Erp.Shared\ViewModels\ crie os seguintes arquivos:

1 – …Api.Erp\Api.Erp.Shared\ViewModels\ClienteVM.cs

namespace Api.Erp.Shared.ViewModels
{
    public class ClienteVM
    {
        public string id { get; set; }
        public string nome { get; set; }
        public string email { get; set; }
    }
}

2 – …Api.Erp\Api.Erp.Shared\ViewModels\VendaVMInput.cs

namespace Api.Erp.Shared.ViewModels

{
    public class VendaVmInput
    {
        public string id { get; set; }
        public string idCliente { get; set; }
        public decimal valorTotal { get; set; }
    }
}

3 – …Api.Erp\Api.Erp.Shared\ViewModels\VendaVMOutput.cs

namespace Api.Erp.Shared.ViewModels
{
    public class VendaVmOutput
    {
        public string id { get; set; }
        public ClienteVM cliente { get; set; }
        public decimal valorTotal { get; set; }
    }
}

4 – …Api.Erp\Api.Erp.Shared\ViewModels\NotaFiscalVmInput.cs

namespace Api.Erp.Shared.ViewModels
{
    public class NotaFiscalVmInput
    {
        public string id { get; set; }
        public string idVenda { get; set; }
        public decimal valorTotalICMS { get; set; }
        public decimal valorTotalNotaFiscal { get; set; }
    }
}

5 – …Api.Erp\Api.Erp.Shared\ViewModels\NotaFiscalVmOutput.cs

namespace Api.Erp.Shared.ViewModels
{
    public class NotaFiscalVmOutput
    {
        public string id { get; set; }
        public VendaVmOutput Venda { get; set; }
        public decimal valorTotalICMS { get; set; }
        public decimal valorTotalNotaFiscal { get; set; }
    }
}

Estas são as view models que utilizaremos para a construção de nossas APIs. Como todos os projetos irão utilizá-las, este é o motivo de estas serem criadas no projeto Api.Erp.Shared.

2.1.1.1. Repositório de dados

Ainda no projeto Api.Erp.Shared, criaremos o nosso repositório de dados.

Como a intenção aqui é apenas demonstrar as funcionalidades de nossa API, não utilizaremos conexão com nenhum banco de dados. Em vez disso, persistiremos nossas informações em variáveis estáticas.

Crie o seguinte arquivo, do diretório Repository do projeto Api.Erp.Shared:

…Api.Erp\Api.Erp\Api.Erp.Shared\Repository\BaseRepository.cs

using Api.Erp.Shared.ViewModels;
using System.Collections.Generic;
 
namespace Api.Erp.Shared.Repository
{
    public static class BaseRepository
    {
        static BaseRepository()
        {
            CriarDadosModelo();
        }

        private static void CriarDadosModelo()
        {
            // Na primeira execução, inclui alguns clientes para teste
            if (RepositorioClientes == null)
            {
                RepositorioClientes = new List<ClienteVM>();
                ClienteVM cliente1 = new ClienteVM();
                cliente1.id = "10";
                cliente1.nome = "Silvair Leite Soares";
                cliente1.email = "[email protected]";
                RepositorioClientes.Add(cliente1);

                ClienteVM cliente2 = new ClienteVM();
                cliente2.id = "11";
                cliente2.nome = "José da Silva Sobrinho";
                cliente2.email = "[email protected]";
                RepositorioClientes.Add(cliente2);
            }

            // Na primeira execução, inclui algumas vendas para teste
            if (RepositorioVendas == null)
            {
                RepositorioVendas = new List<VendaVmInput>();
                VendaVmInput venda1 = new VendaVmInput();
                venda1.id = "1";
                venda1.idCliente = "10";
                venda1.valorTotal = 100;
                RepositorioVendas.Add(venda1);

                VendaVmInput venda2 = new VendaVmInput();
                venda2.id = "2";
                venda2.idCliente = "11";
                venda2.valorTotal = 1500;
                RepositorioVendas.Add(venda2);
            }

            // Na primeira execução, inclui algumas notas fiscais para teste
            if (RepositorioNotasFiscais == null)
            {
                RepositorioNotasFiscais = new List<NotaFiscalVmInput>();
                NotaFiscalVmInput notafiscal1 = new NotaFiscalVmInput();
                notafiscal1.id = "20";
                notafiscal1.idVenda = "1";
                notafiscal1.valorTotalICMS = 100;
                notafiscal1.valorTotalNotaFiscal = 110;
                RepositorioNotasFiscais.Add(notafiscal1);

                NotaFiscalVmInput notafiscal2 = new NotaFiscalVmInput();
                notafiscal2.id = "30";
                notafiscal2.idVenda = "2";
                notafiscal2.valorTotalICMS = 800;
                notafiscal2.valorTotalNotaFiscal = 100;
                RepositorioNotasFiscais.Add(notafiscal2);
            }
        }

        // Variável para persistência temporária de clientes, somente para demonstração
        public static ICollection<ClienteVM> RepositorioClientes;

        // Variável para persistência temporária de vendas, somente para demonstração
        public static ICollection<VendaVmInput> RepositorioVendas;

        // Variável para persistência temporária de notas fiscais, somente para demonstração
        public static ICollection<NotaFiscalVmInput> RepositorioNotasFiscais;
    }
}

2.1.1.2. Serviços HTTP

Criaremos agora, no projeto Api.Erp.Shared, alguns serviços http que serão responsáveis pela comunicação REST entre os microsserviços.

Crie os seguintes arquivos, do diretório Http do projeto Api.Erp.Shared:

1 – …Api.Erp\Api.Erp\Api.Erp.Shared\Http\RequestHttp.cs

Este será um serviço genérico, utilizado para efetuar requisições http GET, entre os microserviços.

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace Api.Erp.Shared.Http
{
    public static class RequestHttp
    {
        public static async Task<string> Request(string url)
        {
            HttpClient client = new HttpClient();

            client.BaseAddress = new Uri(url);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            var response = await client.GetStringAsync(url);
            return response;
        }
    }
}

2 – …Api.Erp\Api.Erp\Api.Erp.Shared\Http\ClienteServices.cs

Este será o serviço http que pesquisará um cliente específico por id, na Api.Erp.Clientes.

using Api.Erp.Shared.ViewModels;
using System.Text.Json;
using System.Threading.Tasks;

namespace Api.Erp.Shared.Http
{
    public static class ClienteServices
    {
        public static async Task<ClienteVM> PesquisarPorId(string id)
        {
            // Rota que retorna um cliente pelo id pesquisado
            var url = "http://localhost:5000/api/cliente/" + id;

            // Executa a requisição http
            var response = await RequestHttp.Request(url);

            // Converte o JSON retornado pela api em um objeto do tipo ClienteVM
            ClienteVM cliente = JsonSerializer.Deserialize<ClienteVM>(response);
            return cliente;
        }
    }
}

 3 – …Api.Erp\Api.Erp\Api.Erp.Shared\Http\FiscalServices.cs

Este será o serviço http que receberá um resumo de uma nota fiscal, obterá os dados da venda, na Api.Erp.Comercial. E retornará a nota fiscal completa, contendo os dados da venda, juntamente com os dados do cliente da venda (Nota Fiscal ⇒ Venda ⇒ Cliente)

using Api.Erp.Shared.ViewModels;
using System.Text.Json;
using System.Threading.Tasks;

namespace Api.Erp.Shared.Http
{
    public static class FiscalServices
    {
        public static async Task<NotaFiscalVmOutput> ObterNotaFiscalCompleta(NotaFiscalVmInput notafiscal)
        {
            // Pesquisa a venda da notafiscal na Api.Erp.Comercial
            VendaVmOutput vendaDaNotaFiscal = await PesquisarVendaPorId(notafiscal.idVenda);
            
            // Cria um objeto com os dados completos da notafiscal, para retornar para o usuário
            NotaFiscalVmOutput notafiscalCompleta = new NotaFiscalVmOutput();
            notafiscalCompleta.id = notafiscal.id;
            notafiscalCompleta.Venda = vendaDaNotaFiscal;
            notafiscalCompleta.valorTotalICMS = notafiscal.valorTotalICMS;
            notafiscalCompleta.valorTotalNotaFiscal = notafiscal.valorTotalNotaFiscal;

            return notafiscalCompleta;
        }

        private static async Task<VendaVmOutput> PesquisarVendaPorId(string id)
        {
            // Rota que retorna uma venda pelo id pesquisado
            var url = "http://localhost:5003/api/venda/" + id;

            // Executa a requisição http de forma assíncrona
            var response = await RequestHttp.Request(url);

            // Converte o JSON retornado pela api em um objeto do tipo VendaVmOutput
            VendaVmOutput venda = JsonSerializer.Deserialize<VendaVmOutput>(response);
            return venda;
        }
    }
}

4 – …Api.Erp\Api.Erp\Api.Erp.Shared\Http\ComercialServices.cs

Este será o serviço http que fará todas as operações relacionadas às vendas, registradas na Api.Erp.Comercial.

using Api.Erp.Shared.Repository;
using Api.Erp.Shared.ViewModels;
using System;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;

namespace Api.Erp.Shared.Http
{
    public static class ComercialServices
    {
        public static async Task<VendaVmOutput> PesquisarPorId(string id)
        {
            // Rota que retorna uma venda pelo id pesquisado
            var url = "http://localhost:5003/api/venda/" + id;

            // Executa a requisição http de forma assíncrona
            var response = await RequestHttp.Request(url);

            // Converte o JSON retornado pela api em um objeto do tipo VendaVmInput
            VendaVmInput venda = JsonSerializer.Deserialize<VendaVmInput>(response);

            return await ObterVendaCompleta(venda);
        }

        public static async Task<VendaVmOutput> ObterVendaCompleta(VendaVmInput venda)
        {
            // Pesquisa o cliente da venda na Api.Erp.Clientes
            ClienteVM clienteDaVenda = await ClienteServices.PesquisarPorId(venda.idCliente);

            // Cria um objeto com os dados completos da venda, para retornar para o usuário
            VendaVmOutput vendaCompleta = new VendaVmOutput();
            vendaCompleta.id = venda.id;
            vendaCompleta.cliente = clienteDaVenda;
            vendaCompleta.valorTotal = venda.valorTotal;

            return vendaCompleta;
        }

        public static async Task<VendaVmOutput> ObterVendaCompletaPorId(string id)
        {
            // Obtem os dados da venda, salvos no repositório de vendas
            var venda = BaseRepository.RepositorioVendas.Where(cli => cli.id == id).FirstOrDefault();

            return await ObterVendaCompleta(venda);
        }
    }
}

2.1.1.3. Referenciando o projeto Api.Erp.Shared nos demais projetos

O próximo passo é referenciar o projeto Api.Erp.Shared nos os demais projetos. Para isso, clique com o botão direito na opção Depencencies de cada um dos projetos (Api.Erp.Clientes, Api.Erp.Comercial e Api.Erp.Fiscal), selecione a opção Add Reference…, acesse a aba Projects, marque o projeto Api.Erp.Shared e clique em OK.

Referenciando o projeto Shared

Lembre-se de repetir este processo em todos os projetos (Api.Erp.Clientes, Api.Erp.Comercial e Api.Erp.Fiscal).

2.1.2. Criando funcionalidades no projeto Api.Erp.Clientes

Crie o seguinte arquivo no projeto Api.Erp.Clientes:

…\Api.Erp\Api.Erp.Clientes\Controllers\ClienteController.cs

Este será o controller que processará todas as requisições do Api.Erp.Clientes.

using Api.Erp.Shared.Repository;
using Api.Erp.Shared.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;

namespace Api.Erp.Clientes.Controllers
{
    [Route("api/cliente/")]
    public class ClienteController : Controller
    {
        [HttpPost]
        public ClienteVM Add([FromBody] ClienteVM DadosCliente)
        {
            BaseRepository.RepositorioClientes.Add(DadosCliente);
            return DadosCliente;
        }

        [HttpGet("{id}")]
        public ClienteVM GetClienteById(string id)
        {
            var cliente = BaseRepository.RepositorioClientes.Where(cli => cli.id == id).FirstOrDefault();
            return cliente;
        }

        [HttpGet]
        public ICollection<ClienteVM> GetAllClientes()
        {
            return BaseRepository.RepositorioClientes;
        }

        [HttpDelete("{id}")]
        public object Deletar(string id)
        {
            var cliente = BaseRepository.RepositorioClientes.Where(cli => cli.id == id).FirstOrDefault();
            if (BaseRepository.RepositorioClientes.Remove(cliente))
            {
                return new
                {
                    resultado = "Cliente: " + id + " excluido com sucesso!"
                };
            }
            else
            {
                return new
                {
                    resultado = "Erro ao excluir o cliente " + id
                };
            }
        }
    }
}

2.1.3. Criando funcionalidades no projeto Api.Erp.Comercial

Crie o seguinte arquivo no projeto Api.Erp.Comercial:

…\Api.Erp\Api.Erp.Comercial\Controllers\VendaController.cs

Este será o controller que processará todas as requisições do Api.Erp.Comercial.

using Api.Erp.Shared.Http;
using Api.Erp.Shared.Repository;
using Api.Erp.Shared.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Api.Erp.Comercial.Controllers
{
    [Route("api/venda/")]
    public class VendaController : Controller
    {
        [HttpPost]
        public async Task<VendaVmOutput> Add([FromBody] VendaVmInput DadosVenda)
        {
            BaseRepository.RepositorioVendas.Add(DadosVenda);
            return await ComercialServices.ObterVendaCompleta(DadosVenda);
        }

        [HttpGet("{id}")]
        public async Task<VendaVmOutput> GetVendaById(string id)
        {
            // Obtem os dados da venda, salvos no repositório de vendas
            var venda = BaseRepository.RepositorioVendas.Where(cli => cli.id == id).FirstOrDefault();
            VendaVmOutput vendaCompleta = await ComercialServices.ObterVendaCompleta(venda);

            return vendaCompleta;
        }

        [HttpGet]
        public async Task<ICollection<VendaVmOutput>> GetAllVendas()
        {
            ICollection<VendaVmOutput> vendasCompletas = new List<VendaVmOutput>();

            foreach (var item in BaseRepository.RepositorioVendas)
            {
                vendasCompletas.Add(await ComercialServices.ObterVendaCompleta(item));
            }

            return vendasCompletas;
        }

        [HttpDelete("{id}")]
        public object Deletar(string id)
        {
            var venda = BaseRepository.RepositorioVendas.Where(cli => cli.id == id).FirstOrDefault();
            if (BaseRepository.RepositorioVendas.Remove(venda))
            {
                return new
                {
                    resultado = "Venda: " + id + " excluida com sucesso!"
                };
            }
            else
            {
                return new
                {
                    resultado = "Erro ao excluir o venda " + id
                };
            }
        }
    }
}

2.1.4. Criando funcionalidades no projeto Api.Erp.Fiscal

Crie o seguinte arquivo no projeto Api.Erp.Fiscal:

…\Api.Erp\Api.Erp.Fiscal\Controllers\NotaFiscalController.cs

Este será o controller que processará todas as requisições do Api.Erp.Fiscal.

using Api.Erp.Shared.Http;
using Api.Erp.Shared.Repository;
using Api.Erp.Shared.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Api.Erp.Fiscal.Controllers
{
    [Route("api/notafiscal/")]
    public class NotaFiscalController : Controller
    {
        [HttpPost]
        public async Task<NotaFiscalVmOutput> Add([FromBody] NotaFiscalVmInput DadosNotaFiscal)
        {
            BaseRepository.RepositorioNotasFiscais.Add(DadosNotaFiscal);
            return await FiscalServices.ObterNotaFiscalCompleta(DadosNotaFiscal);
        }

        [HttpGet("{id}")]
        public async Task<NotaFiscalVmOutput> GetNotaFiscalById(string id)
        {
            // Obtem os dados da notafiscal, salvos no repositório de notas fiscais
            var notafiscal = BaseRepository.RepositorioNotasFiscais.Where(cli => cli.id == id).FirstOrDefault();
            NotaFiscalVmOutput notafiscalCompleta = await FiscalServices.ObterNotaFiscalCompleta(notafiscal);

            return notafiscalCompleta;
        }

        [HttpGet]
        public async Task<ICollection<NotaFiscalVmOutput>> GetAllNotaFiscais()
        {
            ICollection<NotaFiscalVmOutput> notafiscaisCompletas = new List<NotaFiscalVmOutput>();

            foreach (var item in BaseRepository.RepositorioNotasFiscais)
            {
                notafiscaisCompletas.Add(await FiscalServices.ObterNotaFiscalCompleta(item));
            }

            return notafiscaisCompletas;
        }

        [HttpDelete("{id}")]
        public object Deletar(string id)
        {
            var notafiscal = BaseRepository.RepositorioNotasFiscais.Where(cli => cli.id == id).FirstOrDefault();
            if (BaseRepository.RepositorioNotasFiscais.Remove(notafiscal))
            {
                return new
                {
                    resultado = "Nota fiscal: " + id + " excluida com sucesso!"
                };
            }
            else
            {
                return new
                {
                    resultado = "Erro ao excluir a nota fiscal " + id
                };
            }
        }
    }
}

2.1.5. Configurando a execução dos projetos

Vamos fazer algumas configurações para a execução de nossos projetos.

2.1.5.1. Configurando o projeto Api.Erp.Clientes

Clique com o botão direito no projeto Api.Erp.Clientes, selecione a opção Properties, na janela de propriedades do projeto, na aba Debug, altere o valor dos seguintes campos respectivamente:

  • Profile: Api.Erp.Clientes
  • Launch: Project
  • Launch browser:
  • App URL: https://localhost:5001;http://localhost:5000

2.1.5.1

2.1.5.2. Configurando o projeto Api.Erp.Comercial

Clique com o botão direito no projeto Api.Erp.Comercial, selecione a opção Properties, na janela de propriedades do projeto, na aba Debug, altere o valor dos seguintes campos respectivamente:

  • Profile: Api.Erp.Comercial
  • Launch: Project
  • Launch browser:
  • App URL: https://localhost:5002;http://localhost:5003

2.1.5.2

2.1.5.3. Configurando o projeto Api.Erp.Fiscal

Clique com o botão direito no projeto Api.Erp.Fiscal, selecione a opção Properties, na janela de propriedades do projeto, na aba Debug, altere o valor dos seguintes campos respectivamente:

  • Profile: Api.Erp.Fiscal
  • Launch: Project
  • Launch browser:
  • App URL: https://localhost:5004;http://localhost:5005

2.1.5.3

2.1.6. Configurações gerais

No método Configure do arquivo Startup.cs de cada um dos projetos (Api.Erp.Clientes, Api.Erp.Comercial e Api.Erp.Fiscal), comente a seguinte instrução:

app.UseHttpsRedirection();

2.1.5.4

Clique com o botão direito na Solution Api.Erp, selecione Properties, acesse a aba Common Properties  Startup Project, desabilite a opção Current selection, habilite Multiple startup projects:. Na coluna Action, selecione a opção Start, para as 3 APIs (Api.Erp.ClientesApi.Erp.Comercial e Api.Erp.Fiscal). De acordo com a seguinte imagem:

2.1.5.4_2

Com isto, concluímos a primeira etapa do nosso artigo. Fizemos uma apresentação dos problemas e criamos a nossa aplicação, com as APIs para exemplo.

Para ver todas as APIs em execução, basta pressionar a tecla F5.

Enquanto não habilitamos o Swagger em nossas APIs, você poderá utilizar o Postman para testar todos os serviços das APIs. Baixe a coleção com as requisições do postman aqui.

Obtenha o código completo da solução até o presente momento neste repositório do GitHub.

Links para a série completa:

Silvair Leite Soares

Mais artigos deste autor »

Analista de sistema, apaixonado por tecnologia, larga experiência com sistemas de automação comercial, projetos SPED e NF-e e bancos de dados.


Deixe seu comentário

Seu endereço de e-mail não será publicado. Campos com * são obrigatórios!