<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Hernani Almeida</title>
    <description>The latest articles on Forem by Hernani Almeida (@2020nani).</description>
    <link>https://forem.com/2020nani</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F771178%2Fb67b8091-5646-4a25-9b71-23367bc79e7f.png</url>
      <title>Forem: Hernani Almeida</title>
      <link>https://forem.com/2020nani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/2020nani"/>
    <language>en</language>
    <item>
      <title>Implementando Singleton, Factory e Observer com novas features versoes recentes Java</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Sun, 13 Apr 2025 21:41:51 +0000</pubDate>
      <link>https://forem.com/2020nani/implementando-singleton-factory-e-observer-com-novas-features-versoes-recentes-java-48jd</link>
      <guid>https://forem.com/2020nani/implementando-singleton-factory-e-observer-com-novas-features-versoes-recentes-java-48jd</guid>
      <description>&lt;p&gt;Neste post vamos aprender 3 dos design patterns essenciais para o desenvolvimento de software utilizando a linguagem ´Java´ e novas features adicionadas na versão 21 e 24 do java.&lt;br&gt;
Este projeto simula um sistema de cadastro de usuários, utilizando os padrões de design &lt;strong&gt;Singleton&lt;/strong&gt;, &lt;strong&gt;Factory&lt;/strong&gt; e &lt;strong&gt;Observer&lt;/strong&gt;. Cada padrão foi escolhido para resolver problemas específicos dentro da arquitetura.&lt;br&gt;
Primeiro criamos a classe &lt;code&gt;UsuarioController&lt;/code&gt; que tera a responsabilidade de expor nossa api para ser acessada via rest através do meto http POST.&lt;br&gt;
UsuarioController&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.designpatterns.application.controller;

import com.designpatterns.application.mapper.input.UsuarioRequest;
import com.designpatterns.application.mapper.output.UsuarioResponse;
import com.designpatterns.core.usuario.UsuarioService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UsuarioController {
    private final UsuarioService usuarioService;

    public UsuarioController(UsuarioService usuarioService) {
        this.usuarioService = usuarioService;
    }

    @PostMapping("usuario")
    public UsuarioResponse cadastraUsuario(@RequestBody UsuarioRequest request) {
        return usuarioService.createUser(request.converteUsuario()).converteResponse();
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A classe UsuarioController depende de um objeto do tipo &lt;code&gt;UsuarioService&lt;/code&gt; para realizar suas operações. Em vez de criar diretamente uma instância de UsuarioService (o que resultaria em alto acoplamento), definimos essa dependência como um parâmetro no construtor utilizando a injeção de dependência do spring, quando o Spring Container detecta essa classe, ele identifica o construtor e injeta automaticamente uma instância de UsuarioService, que já foi configurada no contexto da aplicação (geralmente definida como um Bean no Spring).&lt;br&gt;
&lt;strong&gt;Observação:&lt;/strong&gt; Nesse momento já estamos utilizando o design pattern &lt;code&gt;Singleton&lt;/code&gt; pois o Spring fornece essa instância única em toda a aplicação, que já foi criada e gerenciada pelo Container Spring, caso fosse necessário que tivesses uma classe nova todas as vezes que instanciada em um constructor e necessário utilizarmos a annotation &lt;code&gt;@Scope("prototype")&lt;/code&gt; para referenciar esse contexto ao Spring conforme a imagem abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfi9ndda516v2xq3ahed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfi9ndda516v2xq3ahed.png" alt="Image description" width="800" height="142"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Logo mais veremos outra forma de utilizar o design pattern Singleton.&lt;br&gt;
UsuarioService&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.designpatterns.core.usuario;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class UsuarioService {
    private final ApplicationEventPublisher eventPublisher;

    public UsuarioService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }


    public Usuario createUser(Usuario usuario) {
        System.out.println("Usuário cadastrado com sucesso: " + usuario.nome());
        eventPublisher.publishEvent(new UsuarioCadastradoEvent(this, usuario));
        return usuario;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dentro da nossa classe de serviço UsuarioService encontramos outro design pattern que podemos utilizar, o &lt;strong&gt;&lt;em&gt;Observer&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
O Observer é utilizado para desacoplar o envio de notificações do fluxo de cadastro de usuário. Quando um usuário é cadastrado, múltiplos observadores podem reagir a esse evento de maneira independente.&lt;br&gt;
Temos injetado em nossa classe de serviço a classe &lt;code&gt;ApplicationEventPublisher&lt;/code&gt; que é uma interface do Spring Framework responsável por publicar eventos dentro do contexto da aplicação, nos permitindo que o UsuarioService publique eventos de forma desacoplada. Ou seja, o service não precisa conhecer diretamente os objetos que vão reagir ao evento.&lt;br&gt;
&lt;strong&gt;OBS:&lt;/strong&gt; A implementação do padrão Observer utilizando o &lt;code&gt;ApplicationEventPublisher&lt;/code&gt;e uma abordagem moderna e desacoplada provida pelo Spring. Com isso, sempre que um novo usuário é cadastrado, um evento do tipo &lt;code&gt;UsuarioCadastradoEvent&lt;/code&gt; é publicado.&lt;/p&gt;

&lt;p&gt;Esse evento sera tratado pela classe &lt;code&gt;UsuarioEventListener&lt;/code&gt;, que atua como "observador", reagindo ao evento publicado para enviar notificações com base na preferência do usuário (SMS ou E-mail).&lt;/p&gt;

&lt;p&gt;Essa abordagem traz os benefícios do padrão Observer (desacoplamento e reatividade), utilizando os recursos do próprio Spring.&lt;/p&gt;

&lt;p&gt;Nesse caso, ela dispara o evento &lt;code&gt;UsuarioCadastradoEvent&lt;/code&gt; com o usuário recém-criado. O Spring então notifica automaticamente qualquer classe anotada com @EventListener e que esteja ouvindo esse tipo de evento.&lt;br&gt;
Vamos criar entao a classe UsuarioCadastradoEvent e também o listener responsável por ouvir esse evento e implementar o envio de notificação de email/sms.&lt;br&gt;
UsuarioCadastradoEvent&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.designpatterns.core.usuario;

import org.springframework.context.ApplicationEvent;

public class UsuarioCadastradoEvent extends ApplicationEvent {

    private final Usuario usuario;
    public UsuarioCadastradoEvent(Object source, Usuario usuario) {
        super(source);
        this.usuario = usuario;
    }

    public Usuario getUsuario() { return usuario; }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;UsuarioEventListener&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.designpatterns.core.usuario;

import com.designpatterns.core.notification.EmailNotification;
import com.designpatterns.core.notification.NotificationFactory;
import com.designpatterns.core.notification.SmsNotification;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class UsuarioEventListener {
    private final NotificationFactory factory;

    public UsuarioEventListener(NotificationFactory factory) {
        this.factory = factory;
    }

    @EventListener
    public void handle(UsuarioCadastradoEvent event) {
        Usuario usuario = event.getUsuario();
        factory.getNotificationsFor(usuario.preferencia())
               .forEach(n -&amp;gt; {
                   if (n instanceof EmailNotification email) {
                       email.notificate(usuario, "E-mail: Bem-vindo, " + usuario.nome() + "!");
                   } else if (n instanceof SmsNotification sms) {
                       sms.notificate(usuario, "SMS: Bem-vindo, " + usuario.nome() + "!");
                   }
               });
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Observação:&lt;/strong&gt; Note que utilizamos o &lt;code&gt;instanceof com pattern matching&lt;/code&gt;, o uso de pattern matching com instanceof facilita muito o código ao evitar castings explícitos. Isso torna o código mais legível, seguro e moderno, nos permitindo acessar diretamente a instância com cast implícito e mais legibilidade sendo muito útil quando queremos tratar notificações de forma personalizada por tipo uma das melhorias de linguagem do Java 24&lt;/p&gt;

&lt;p&gt;Queremos em nosso sistema notificar o usuário via &lt;code&gt;email&lt;/code&gt; e &lt;code&gt;sms&lt;/code&gt; que o cadastro foi efetuado com sucesso.&lt;br&gt;
Vamos utilizar um novo design pattern dentro da classe que esta ouvindo o email através da classe &lt;code&gt;NotificationFactory&lt;/code&gt;&lt;br&gt;
No projeto, a classe NotificationFactory contem instâncias de diferentes tipos de notificadores, como EmailNotification e SmsNotification, sem que o código cliente precise conhecer os detalhes específicos de cada implementação. Isso é alcançado através do design pattern Factory, que promove a criação de objetos de forma encapsulada e flexível.&lt;/p&gt;

&lt;p&gt;🧱 Factory Pattern com switch expression e Injeção de Dependência&lt;br&gt;
Aqui, vamos implementar uma Factory que retorna uma lista de notificações com base na preferência do usuário, como Email, SMS, ou ambos. Vamos usar o padrão Factory para encapsular a lógica de criação dessas instâncias.&lt;br&gt;
NotificationFactory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.designpatterns.core.notification;

import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class NotificationFactory {

    private final EmailNotification email;
    private final SmsNotification sms;

    public NotificationFactory(EmailNotification email, SmsNotification sms) {
        this.email = email;
        this.sms = sms;
    }
    public List&amp;lt;Notification&amp;gt; getNotificationsFor(String preferencia) {
        return switch (preferencia.toLowerCase()) {
            case "email" -&amp;gt; List.of(email);
            case "sms" -&amp;gt; List.of(sms);
            case "ambos" -&amp;gt; List.of(email, sms);
            default -&amp;gt; List.of();
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🏭 O que essa Factory faz?&lt;br&gt;
A NotificationFactory decide, com base na preferência do usuário (se ele deseja receber notificações por email, SMS ou ambos), qual(is) instância(s) de notificação retornar. A implementação do switch expression torna o código mais limpo, sem a necessidade de múltiplos if ou else.&lt;/p&gt;

&lt;p&gt;💡 O que foi aplicado de moderno aqui?&lt;br&gt;
Injeção de Dependência com Spring (@Component): As instâncias de EmailNotification e SmsNotification são injetadas automaticamente pelo Spring, facilitando o gerenciamento das dependências.&lt;/p&gt;

&lt;p&gt;switch expression (Java 14+): Melhorias no Java que tornam a seleção de valores mais legível e com menos código boilerplate, comparado ao switch tradicional.&lt;/p&gt;

&lt;p&gt;✅ Exemplo de uso da Factory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NotificationFactory factory = new NotificationFactory(email, sms);
List&amp;lt;Notification&amp;gt; notifications = factory.getNotificationsFor("ambos");

notifications.forEach(notification -&amp;gt; notification.send());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔄 O que mais podemos fazer?&lt;br&gt;
Flexibilidade: Caso surjam novos tipos de notificação, como Push ou WhatsApp, podemos facilmente estender a Factory para lidar com esses novos tipos.&lt;/p&gt;

&lt;p&gt;Extensibilidade: Adicionar novas opções ao método getNotificationsFor se torna trivial, mantendo o código limpo e expansível.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observação:&lt;/strong&gt; Criaremos a interface &lt;code&gt;Notification&lt;/code&gt; e as classes &lt;code&gt;EmailNotification&lt;/code&gt; e 'SmsNotification' que implementam essa interface.&lt;/p&gt;

&lt;p&gt;No projeto, usamos a seguinte construção moderna das versões recentes do java para criar a interface Notification&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdl39yf0bagvg6ftipwe4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdl39yf0bagvg6ftipwe4.png" alt="Image description" width="800" height="132"&gt;&lt;/a&gt;&lt;br&gt;
Essa linha define uma interface selada &lt;strong&gt;(sealed interface)&lt;/strong&gt;, que traz uma poderosa funcionalidade: restringir quais classes podem implementar ou estender a interface.&lt;/p&gt;

&lt;p&gt;Notification&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.designpatterns.core.notification;

import com.designpatterns.core.usuario.Usuario;

public sealed interface Notification permits EmailNotification, SmsNotification {
    void notificate(Usuario usuario, String mensagem);
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos criar as classes &lt;code&gt;EmailNotification&lt;/code&gt; e &lt;code&gt;SmsNotification&lt;/code&gt; que implementam a interface Notification e através do método notificate enviam a mensagem ao usuário.&lt;br&gt;
EmailNotification&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.designpatterns.core.notification;

import com.designpatterns.core.usuario.Usuario;
import org.springframework.stereotype.Component;

@Component
public final class EmailNotification implements Notification {
    public void notificate(Usuario usuario, String mensagem) {
        System.out.printf("Enviando EMAIL para %s (%s): %s%n",
                usuario.nome(), usuario.email(), mensagem);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SmsNotification&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.designpatterns.core.notification;

import com.designpatterns.core.usuario.Usuario;
import org.springframework.stereotype.Component;

@Component
public final class SmsNotification implements Notification {
    public void notificate(Usuario usuario, String mensagem) {
        System.out.printf("Enviando SMS para %s (%s): %s%n",
                usuario.nome(), usuario.telefone(), mensagem);
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos agora testar nossa aplicação e validar o envio das notificações de email e sms quando um usuário e cadastrado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfhvwfi1f3xvk8flgogt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfhvwfi1f3xvk8flgogt.png" alt="Image description" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakrhpgh4aq9g0bcw660h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakrhpgh4aq9g0bcw660h.png" alt="Image description" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Este projeto demonstra como combinar design patterns clássicos com os novos recursos das versoes mais recentes do Java, usando um sistema simples de cadastro de usuários com notificações via e-mail e SMS.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Padrões Utilizados
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔒 Singleton
&lt;/h3&gt;

&lt;p&gt;O padrão Singleton foi aplicado com o uso do Spring Boot para garantir que o serviço &lt;code&gt;UsuarioService&lt;/code&gt; tenham apenas uma instância durante todo o ciclo de vida da aplicação.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏭 Factory
&lt;/h3&gt;

&lt;p&gt;Utilizamos uma &lt;code&gt;NotificationFactory&lt;/code&gt; para encapsular a criação das instâncias de notificações (&lt;code&gt;EmailNotification&lt;/code&gt; e &lt;code&gt;SmsNotification&lt;/code&gt;) com base em um tipo definido na escolha do usuario.&lt;/p&gt;

&lt;h3&gt;
  
  
  👀 Observer
&lt;/h3&gt;

&lt;p&gt;O padrão Observer permite que diferentes canais de notificação sejam observadores do evento de cadastro de usuário. Assim, sempre que um novo usuário é registrado, todos os canais registrados recebem a notificação.&lt;/p&gt;

&lt;h2&gt;
  
  
  🆕 Features das versoes recentes do Java
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sealed interface&lt;/code&gt;: restringe quais classes podem implementar uma interface, trazendo mais segurança e controle ao design.&lt;/li&gt;
&lt;li&gt;Pattern matching com &lt;code&gt;instanceof&lt;/code&gt;: permite validações de tipo mais limpas e seguras.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;switch&lt;/code&gt; com enum: possibilita o tratamento completo dos tipos permitidos com checagem de exaustividade pelo compilador.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📦 Classes Principais
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Usuario&lt;/code&gt;: representa o modelo de dados do usuário, contendo informações como nome e preferência de notificação (e-mail ou SMS).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Notification&lt;/code&gt;: interface selada (&lt;code&gt;sealed interface&lt;/code&gt;) que define o contrato para os tipos de notificação. Ela é implementada por:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;EmailNotification&lt;/code&gt;: responsável por enviar notificações por e-mail.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SmsNotification&lt;/code&gt;: responsável por enviar notificações via SMS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;NotificationFactory&lt;/code&gt;: aplica o padrão &lt;strong&gt;Factory&lt;/strong&gt;, sendo responsável por fornecer a instância correta de &lt;code&gt;Notification&lt;/code&gt; com base na preferência informada pelo usuário (por exemplo, "email" ou "sms").&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;NotificationService&lt;/code&gt;: atua como &lt;strong&gt;publisher&lt;/strong&gt; de eventos no padrão Observer moderno via &lt;code&gt;ApplicationEventPublisher&lt;/code&gt;, enviando eventos de forma desacoplada para serem tratados por listeners.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;UsuarioService&lt;/code&gt;: encapsula a lógica de cadastro de usuários e é responsável por acionar a notificação através da publicação de um evento (&lt;code&gt;UsuarioCadastradoEvent&lt;/code&gt;) que será capturado posteriormente pelo listener.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;UsuarioEventListener&lt;/code&gt;: classe que observa os eventos de usuários cadastrados. Utiliza pattern matching com &lt;code&gt;switch&lt;/code&gt; para tratar dinamicamente o tipo de notificação a ser enviada, de forma segura e moderna.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;UsuarioCadastradoEvent&lt;/code&gt;: classe que representa o evento de domínio publicado após o cadastro de um novo usuário. Contém os dados do usuário para consumo pelos observers.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  ✅ Conclusão
&lt;/h2&gt;

&lt;p&gt;A combinação dos padrões de projeto com os novos recursos das recentes versões do Java  trouxe maior robustez, legibilidade e extensibilidade ao projeto. Essa abordagem é ideal para arquiteturas modernas e escaláveis.&lt;/p&gt;




&lt;p&gt;O projeto está disponível no GitHub. Sua estrela é muito bem-vinda se você achou o conteúdo relevante:&lt;br&gt;
🔗 &lt;a href="https://github.com/2020nani/designpatternj" rel="noopener noreferrer"&gt;DesignPatterns&lt;/a&gt;&lt;br&gt;
Desenvolvido para fins educacionais por Hernani Almeida&lt;/p&gt;

</description>
      <category>java</category>
      <category>designpatterns</category>
      <category>springboot</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Solucionar erro imagem module-federation</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Tue, 10 Dec 2024 00:07:29 +0000</pubDate>
      <link>https://forem.com/2020nani/solucionar-erro-imagem-module-federation-c3k</link>
      <guid>https://forem.com/2020nani/solucionar-erro-imagem-module-federation-c3k</guid>
      <description>&lt;p&gt;Este artigo visa corrigir o erro que temos quando utilizamos &lt;strong&gt;module-federation&lt;/strong&gt; para configurar aplicações micro-frontends com &lt;strong&gt;angular&lt;/strong&gt;&lt;br&gt;
Resumindo o erro ocorre pois quando rodamos as aplicações e o angular vai buscar o path da imagem, automaticamente, ele assume o path do host como base e, caso a imagem não esteja alocada no host, não encontra o caminho da imagem.&lt;br&gt;
Isso pode interferir nas vantagens de uso do microfrontend que seriam:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Independência Total: Cada equipe poder trabalhar em um microfrontend de forma autônoma, pois no caso de imagens teriam que estar adicionando a imagem na aplicação host.&lt;/li&gt;
&lt;li&gt;Deploy Independente: Cada microfrontend poder ser deployado de forma independente, pois sempre haverá a dependência de deploy da app host em caso de mudar ou adicionar imagens.
Veja este exemplo, tenho na aplicação &lt;strong&gt;MFE&lt;/strong&gt; a imagem e o path da imagem passado de maneira correta.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0wdrlv4r4rnle1nyseh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0wdrlv4r4rnle1nyseh.png" alt="Image description" width="800" height="468"&gt;&lt;/a&gt;&lt;br&gt;
 Porem quando acesso a aplicação rodando recebo um erro 404 ao buscar o caminho da imagem&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rob7hxv9h4qkt7ona53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rob7hxv9h4qkt7ona53.png" alt="Image description" width="800" height="265"&gt;&lt;/a&gt;&lt;br&gt;
Note na imagem que a aplicação utilizou o base path da aplicação host para buscar a imagem &lt;strong&gt;&lt;a href="http://localhost:4200/" rel="noopener noreferrer"&gt;http://localhost:4200/&lt;/a&gt;&lt;/strong&gt;, onde não temos a imagem alocada, por isso recebemos o erro.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyusf7lvyuoaq6ua3vg4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyusf7lvyuoaq6ua3vg4d.png" alt="Image description" width="800" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bora lá resolver isso então, vamos criar um pipe &lt;em&gt;UrlFormatterPipe&lt;/em&gt; na aplicação &lt;strong&gt;mfe&lt;/strong&gt; para forçarmos o angular utilizar o path correto ao buscar a imagem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'urlFormatter'
})
export class UrlFormatterPipe implements PipeTransform {

  transform(value: string): string {
    return __webpack_public_path__ + value;
  }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resumindo o que estamos fazendo no pipe e buscar o path publico da aplicação mfe através da variável global &lt;strong&gt;&lt;strong&gt;webpack_public_path&lt;/strong&gt;&lt;/strong&gt; (ou seja mesmo em ambiente produtivo ira nos retornar o base path da aplicação mfe rodando) e concatenamos com o path da imagem que recebemos dentro do parametro &lt;strong&gt;value&lt;/strong&gt;.&lt;br&gt;
Vamos utilizar o pipe no path da imagem agora e ver o resultado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F139uf5n5kduh1z6yk72r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F139uf5n5kduh1z6yk72r.png" alt="Image description" width="522" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3hrmdi9b5wxpg2vqkom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3hrmdi9b5wxpg2vqkom.png" alt="Image description" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voalá imagem apareceu com sucesso e podemos confirmar no devTools que o base path esta vindo da aplicação &lt;strong&gt;mfe&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;E isso ai devs mas um conhecimento passado, deixarei aqui link do artigo que mostro como configurar um micro frontend angular utilizando module federation, &lt;a href="https://dev.to/2020nani/desacoplando-frontend-com-module-federation-angular-parte-1-5enm"&gt;aplicação utilizada para explicar a correção do path da imagem&lt;/a&gt;, e link do repositório no meu &lt;a href="https://github.com/2020nani/mfe-article" rel="noopener noreferrer"&gt;github&lt;/a&gt; para quem quiser acessar o projeto&lt;br&gt;
Deixo aqui também meu &lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt; e &lt;a href="https://www.instagram.com/her_almeida40/" rel="noopener noreferrer"&gt;instagram&lt;/a&gt; para quem quiser me adicionar, grato a todos que leram este artigo e bora aprender galera.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>modulefederation</category>
      <category>microfrontend</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Api Rest .Net completa com JwtToken integração com api ViaCep utilizando padrão de arquitetura clean architecture - parte 3</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Thu, 01 Aug 2024 02:57:04 +0000</pubDate>
      <link>https://forem.com/2020nani/api-rest-net-completa-com-jwttoken-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-parte-3-21o9</link>
      <guid>https://forem.com/2020nani/api-rest-net-completa-com-jwttoken-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-parte-3-21o9</guid>
      <description>&lt;p&gt;Ferramentas necessárias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/pt-br/download/visual-studio-sdks" rel="noopener noreferrer"&gt;Sdk .net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/windows/install/#:~:text=%20Install%20Docker%20Desktop%20on%20Windows%20%F0%9F%94%97%20,to%20authorize%20the%20installer%20and%20proceed...%20More%20" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://visualstudio.microsoft.com/pt-br/vs/community/" rel="noopener noreferrer"&gt;Visual Studio Community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/2020nani/api-rest-net-completa-com-jwttoken-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-parte-2-im"&gt;Ter completado a implementação mostrada no artigo anterior&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seguimos com a construção da nossa api, neste artigo vamos estruturar a parte de segurança de nossa api, bloqueando acesso de quem não esta autenticado ou sem autorização para acessar algum recurso.&lt;br&gt;
Para realizar essa proteção vamos utilizar a autenticação via &lt;strong&gt;JWT&lt;/strong&gt; &lt;code&gt;JSON WEB TOKEN&lt;/code&gt; bora la.&lt;br&gt;
Para iniciar vamos instalar as bibliotecas necessárias, conforme vimos nos artigos anteriores vamos utilizar o gerenciador de pacotes do Nuget&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuffvqgikctcjgvas0koe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuffvqgikctcjgvas0koe.png" alt="Image description" width="800" height="434"&gt;&lt;/a&gt;&lt;br&gt;
Dentro do gerenciador busque e instale essas duas bibliotecas&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbrlv7yrboz9non586bk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbrlv7yrboz9non586bk.png" alt="Image description" width="800" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgn2pw3d0s56gx2eo5u7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgn2pw3d0s56gx2eo5u7r.png" alt="Image description" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feito isso, dentro da pasta &lt;code&gt;Controllers&lt;/code&gt; que fica na camada de &lt;code&gt;Infrastructure&lt;/code&gt; de nossa api (vide artigo 1 para entender a definição da arquitetura escolhida) vamos proteger nossas classes de acesso conforme imagem abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiof2ebim6rngo7pbgdz3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiof2ebim6rngo7pbgdz3.png" alt="Image description" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsixmvf7mlg4xsmght92o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsixmvf7mlg4xsmght92o.png" alt="Image description" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observação:&lt;/strong&gt; Como passamos a anotação &lt;code&gt;[Authorize(Roles = "ADMIN")]&lt;/code&gt; acima do nome da nossa classe isso faz com que todos os endpoints de acesso sejam protegidos, atraves do JWTToken e a role de permissão concedida ao usuario, caso passemos a anotação acima de um método especifico dentro da nossa classe (conforme imagem abaixo) apenas esse método será protegido o acesso.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd867yu51ux1w3zn3iwf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd867yu51ux1w3zn3iwf1.png" alt="Image description" width="752" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feito isso e necessário mais uma configuração dentro na nossa classe Program.cs, dentro do metodo &lt;code&gt;Main&lt;/code&gt; vamos instanciar uma variável &lt;code&gt;secretKey&lt;/code&gt;, essa assinatura é utilizada para garantir a integridade do token, no caso, se ele foi modificado e se realmente foi gerado por você, você pode gerar um &lt;a href="https://guidgenerator.com/" rel="noopener noreferrer"&gt;Guid&lt;/a&gt; para garantir a integridade dessa chave e que ninguem a conheça para decodificar seu token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnah369yr3g4gk291wp9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnah369yr3g4gk291wp9q.png" alt="Image description" width="766" height="116"&gt;&lt;/a&gt;&lt;br&gt;
Logo acima da configuração &lt;code&gt;app.UseAuthorization()&lt;/code&gt; instancia a config &lt;code&gt;app.UseAuthentication()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7srri1tc6nyg22iwp5oz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7srri1tc6nyg22iwp5oz.png" alt="Image description" width="334" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora adicionamos a configuração para geração do token conforme imagem abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl26gle57dlp5p8gxg9qw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl26gle57dlp5p8gxg9qw.png" alt="Image description" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ValidateIssuer = true&lt;/strong&gt;: Configura que na geração do token seja validado se o &lt;em&gt;Issuer&lt;/em&gt; contido no token e igual ao que passamos na config &lt;strong&gt;ValidIssuer&lt;/strong&gt;.&lt;br&gt;
&lt;strong&gt;ValidateAudience= true&lt;/strong&gt;: : Configura que na geração do token seja validado se o &lt;em&gt;Audience&lt;/em&gt; contido no token e igual ao que passamos na config &lt;strong&gt;ValidAudience&lt;/strong&gt;.&lt;br&gt;
&lt;strong&gt;ValidateLifetime = true&lt;/strong&gt;: Configura a validação de expiração do token gerado.&lt;br&gt;
&lt;strong&gt;ValidateIssuerSigningKey&lt;/strong&gt;: Valida se a chave secreta contida na geração do token e a mesma que passamos na variavel &lt;code&gt;secretKey&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Nossa classe &lt;code&gt;Main&lt;/code&gt; ficara conforme abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
using FirstApi.Application.UseCases.CasesEmployer.ConsultEmployer;
using FirstApi.Application.UseCases.CasesEmployer.DeleteEmployer;
using FirstApi.Application.UseCases.CasesEmployer.Register;
using FirstApi.Application.UseCases.CasesEmployer.UpdateEmployer;
using FirstApi.Application.UseCases.CasesUser.ConsultUser;
using FirstApi.Application.UseCases.CasesUser.DeleteUser;
using FirstApi.Application.UseCases.CasesUser.RegisterUser;
using FirstApi.Application.UseCases.CasesUser.UpdateUser;
using FirstApi.Application.UseCases.PasswordHasher;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.Data;
using FirstApi.Infrastructure.Handler;
using FirstApi.Infrastructure.Integration.ViaCep;
using FirstApi.Infrastructure.Integration.ViaCep.Refit;
using FirstApi.Infrastructure.Repositories;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Refit;
using System.Text;

namespace FirstApi
{
    public class Program
    {

        public static void Main(string[] args)
        {
            string secretKey = "e0606215-c2e4-46fe-8d73-a77e1fa4f45a";
            var builder = WebApplication.CreateBuilder(args);
            // Configure logging
            builder.Logging.ClearProviders();
            builder.Logging.AddConsole();
            builder.Logging.AddDebug();
            // Configure data base access
            builder.Services.AddDbContext&amp;lt;SystemDbContext&amp;gt;(
                options =&amp;gt; options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
            // Add repositories to the container.
            builder.Services.AddScoped&amp;lt;IEmployerRepository, EmployerRepository&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUserRepository, UserRepository&amp;gt;();
            // Add services to the container.
            // employer
            builder.Services.AddScoped&amp;lt;IRegisterEmployerService, RegisterEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUpdateEmployerService, UpdateEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IConsultEmployerService, ConsultEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IDeleteEmployerService, DeleteEmployerService&amp;gt;();
            // user
            builder.Services.AddScoped&amp;lt;IRegisterUserService, RegisterUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUpdateUserService, UpdateUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IConsultUserService, ConsultUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IDeleteUserService, DeleteUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IPasswordHasher, PasswordHasher&amp;gt;();
            // client viacep
            builder.Services.AddScoped&amp;lt;IViaCepIntegrationService, ViaCepIntegrationService&amp;gt;();
            // Add client refit
            builder.Services.AddRefitClient&amp;lt;IViaCepIntegrationRefit&amp;gt;().ConfigureHttpClient(c =&amp;gt;
            {
                c.BaseAddress = new Uri("https://viacep.com.br");
            }
            );

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            // Config authentication
            builder.Services.AddAuthentication(options =&amp;gt;
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =&amp;gt;
            {
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                {
                    ValidateIssuer = true,
                    ValidateAudience= true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = "emprise",
                    ValidAudience = "firstApi",
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
                };
            });

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseAuthorization();

            // global error handler
            app.UseMiddleware&amp;lt;GlobalExceptionHandler&amp;gt;();

            app.MapControllers();

            app.Run();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto nossa api ja esta protegida, se fizermos um request sem passar o token de acesso vamos receber um status code &lt;code&gt;401&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj78ndc6z7brt58e8hjkk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj78ndc6z7brt58e8hjkk.png" alt="Image description" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos agora implementar um endpoint para criar um login e gerar um &lt;code&gt;JsonToken&lt;/code&gt; para acessarmos nossa api, na nossa camada &lt;code&gt;Application&lt;/code&gt; vamos criar um caso de uso &lt;code&gt;PasswordHasher&lt;/code&gt; responsavel por criptografar o password do usuario e armazenar banco de dados e mais um caso de uso chamado Login conforme imagens abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6thtv6qxtosi331o9e4p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6thtv6qxtosi331o9e4p.png" alt="Image description" width="302" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqs1i6qb6mgiiys24mpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqs1i6qb6mgiiys24mpc.png" alt="Image description" width="336" height="191"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;IPasswordHasher&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace FirstApi.Application.UseCases.PasswordHasher
{
    public interface IPasswordHasher
    {
        public string Hash(string password);

        public bool Verify(string passwordHash, string password);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;PasswordHasher&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Security.Cryptography;

namespace FirstApi.Application.UseCases.PasswordHasher
{
    public class PasswordHasher : IPasswordHasher
    {
        private int SaltSize = 128 / 8;
        private int KeySize = 256 / 8;
        private int Iterations = 10000;
        private static HashAlgorithmName _hash = HashAlgorithmName.SHA256;
        private char Delimiter = ';'; 
        string IPasswordHasher.Hash(string password)
        {
            var salt = RandomNumberGenerator.GetBytes(SaltSize);
            var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations,_hash, KeySize);
            return string.Join(Delimiter, Convert.ToBase64String(salt), Convert.ToBase64String(hash));
        }

        bool IPasswordHasher.Verify(string passwordHash, string password)
        {
            var elements = passwordHash.Split(Delimiter);
            var salt = Convert.FromBase64String(elements[0]);
            var hash = Convert.FromBase64String(elements[1]);
            var hashInput = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, _hash, KeySize);
            return CryptographicOperations.FixedTimeEquals(hash, hashInput);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ILoginService&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace FirstApi.Application.UseCases.CasesAuth.Login
{
    public interface ILoginService
    {
        public LoginOutput Execute(LoginInput input);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;LoginService&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Application.UseCases.PasswordHasher;
using FirstApi.Domain.Entities;
using FirstApi.Domain.Enums;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.CustomException;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;

namespace FirstApi.Application.UseCases.CasesAuth.Login
{
    public class LoginService : ILoginService
    {
        private readonly IUserRepository _userRepository;
        private readonly IPasswordHasher _passwordHasher;

        public LoginService(IUserRepository userRepository, IPasswordHasher passwordHasher)
        {
            _userRepository = userRepository;
            _passwordHasher = passwordHasher;
        }

        LoginOutput ILoginService.Execute(LoginInput input)
        {
            User user = _userRepository.FindUserByEmail(input.Email).Result;
            if (user == null || user.Password == null || user.Email == null)
            {
                throw new AppNotFoundException("Credenciais Invalidas teste");
            }

            if (_passwordHasher.Verify(user.Password, input.Password))
            {
                return new LoginOutput().Convert(user, this.generateJwtToken(user.Roles));
            }

            throw new BadHttpRequestException("Credenciais Invalidas");

        }

        private string generateJwtToken(List&amp;lt;Roles&amp;gt; roles)
        {
            string secretKey = "e0606215-c2e4-46fe-8d73-a77e1fa4f45a";
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
            var claims = new List&amp;lt;Claim&amp;gt;
            {
                new Claim("login","admin"),
                new Claim("name", "System Administrator")
            };

            claims.AddRange(roles.Select(role =&amp;gt; new Claim(ClaimTypes.Role, role.ToString())));

            var token = new JwtSecurityToken(
                issuer: "emprise",
                audience: "firstApi",
                claims: claims,
                expires: DateTime.UtcNow.AddHours(2),
                signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
                );

            return new JwtSecurityTokenHandler().WriteToken(token);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;LoginInput&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace FirstApi.Application.UseCases.CasesAuth.Login
{
    public class LoginInput
    {
        public required string Email { get; set; }
        public required string Password { get; set; }


    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;LoginOutput&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Domain.Entities;

namespace FirstApi.Application.UseCases.CasesAuth.Login
{
    public class LoginOutput
    {
        public int Id { get; set; }
        public string? UserName { get; set; }
        public string? AccessToken { get; set; }

        internal LoginOutput Convert(User user, string token)
        {
            Id = user.Id;
            UserName = user.Name;
            AccessToken = token;
            return this;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Obs: Duvida sobre a implementação veja a primeira parte do artigo pois explico a responsabilidade de cada classe construido dentro do caso de uso.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Será necessário também mudarmos nossa entity &lt;code&gt;User.cs&lt;/code&gt; pois precisamos adicionar uma lista de &lt;code&gt;Roles&lt;/code&gt; para cada usuário salvo que será as permissões de acessos que esse usuário possui mesmo tendo um token &lt;code&gt;JwtToken&lt;/code&gt; gerado.&lt;br&gt;
Mude a classe User.cs para que fique assim&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Domain.Enums;
using FirstApi.Domain.ValueObjects;
using FirstApi.Infrastructure.Integration.ViaCep;

namespace FirstApi.Domain.Entities
{
    public class User
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Email { get; set; }
        public string? Password { get; set; }
        public List&amp;lt;Roles&amp;gt;? Roles { get; set; }
        public Endereco? Endereco { get; set; }

        public Endereco GetEndereco(ViaCepResponse response)
        {
            Endereco endereco = new Endereco();
            if(response is null)
            {
                return endereco;
            }
            endereco.Complemento = response.Complemento;
            endereco.Cep = response.Cep;
            endereco.Bairro = response.Bairro;
            endereco.Logradouro = response.Logradouro;
            endereco.Localidade = response.Localidade;
            endereco.Cep = response.Cep;
            endereco.Unidade = response.Unidade;
            endereco.Uf = response.Uf;
            return endereco;
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crie o enum contendo as roles de permissões que seu sistema possui conforme exemplo abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik3yj6n6d3m5qw86ntjk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik3yj6n6d3m5qw86ntjk.png" alt="Image description" width="800" height="232"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Obs:&lt;/strong&gt; Como estamos mudando nossa entity será necessário mudar o mapeamento dessa tabela na classe UserMap.cs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vph4qqnzy74dbg14zt3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vph4qqnzy74dbg14zt3.png" alt="Image description" width="548" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UserMap&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Domain.Entities;
using FirstApi.Domain.Enums;
using FirstApi.Domain.ValueObjects;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace FirstApi.Infrastructure.Data.Map
{
    public class UserMap : IEntityTypeConfiguration&amp;lt;User&amp;gt;
    {
        public void Configure(EntityTypeBuilder&amp;lt;User&amp;gt; builder)
        {
           var userRolesConverter = new ValueConverter&amp;lt;List&amp;lt;Roles&amp;gt;, string&amp;gt;(
           v =&amp;gt; string.Join(',', v.Select(e =&amp;gt; e.ToString())),
           v =&amp;gt; v.Split(new[] { ',' }, StringSplitOptions.None)
           .Select(e =&amp;gt; (Roles)Enum.Parse(typeof(Roles), e)).ToList());
            builder.HasKey(x =&amp;gt; x.Id);
            builder.Property(x =&amp;gt; x.Name).IsRequired().HasMaxLength(255);
            builder.Property(x =&amp;gt; x.Email).IsRequired();
            builder.Property(x =&amp;gt; x.Password).IsRequired();
            builder.Property(x =&amp;gt; x.Roles).HasConversion(userRolesConverter).IsRequired();
            builder.ToTable("Users")
                .OwnsOne(x =&amp;gt; x.Endereco, x =&amp;gt;
                {
                    x.Property(a =&amp;gt; a.Unidade)
                    .HasColumnName("unidade")
                    .IsRequired();
                    x.Property(a =&amp;gt; a.Complemento)
                    .HasColumnName("complemento")
                    .IsRequired();
                    x.Property(a =&amp;gt; a.Cep)
                    .HasColumnName("cep")
                    .IsRequired();
                    x.Property(a =&amp;gt; a.Bairro)
                    .HasColumnName("bairro")
                    .IsRequired();
                    x.Property(a =&amp;gt; a.Logradouro)
                    .HasColumnName("logradouro")
                    .IsRequired();
                    x.Property(a =&amp;gt; a.Uf)
                    .HasColumnName("uf")
                    .IsRequired();
                    x.Property(a =&amp;gt; a.Localidade)
                    .HasColumnName("localidade")
                    .IsRequired();
                });
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feito isso, para facilitar, apague todas as tabelas do seu banco de dados e exclua a pasta ´Migrations´ do seu projeto, apos isso rode os seguintes comandos em ordem dentro do console gerenciador de pacotes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1- Add-Migration InitialDB -Context &amp;lt;nome do contexto a ser migrado&amp;gt;
2- Update-Database -Context &amp;lt;nome do contexto a ser migrado&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud9oll9fq3y4mj4h760l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud9oll9fq3y4mj4h760l.png" alt="Image description" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como citamos nos artigos anteriores, por ter construído nossa app utilizando o padrão &lt;code&gt;Clean Architecture&lt;/code&gt; e o conceito de &lt;strong&gt;domínio não amenico&lt;/strong&gt;, nos traz uma facilidade de mudar alguma logica em nossa api. Precisamos mudar a logica de salvar o usuário em nosso banco de dados, pois e necessário agora salvar também as Roles de acesso que esse usuário terá para acessar nossa aplicação, para isso vamos fazer apenas mudanças na classe de input e output desse caso de uso, que ficara assim.&lt;br&gt;
&lt;strong&gt;RegisterUserInput&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Application.CustomValidations;
using FirstApi.Application.UseCases.PasswordHasher;
using FirstApi.Domain.Entities;
using FirstApi.Domain.Enums;
using FirstApi.Infrastructure.Integration.ViaCep;
using System.ComponentModel.DataAnnotations;

namespace FirstApi.Application.UseCases.CasesUser.RegisterUser
{
    public class RegisterUserInput
    {
        public string? Name { get; set; }

        [EmailAddress(ErrorMessage = "Invalid email address.")]
        public required string Email { get; set; }
        [Password(ErrorMessage = "Password must be at least 8 characters long and contain an uppercase letter, a lowercase letter, a number, and a special character.")]
        public string? Password { get; set; }
        public List&amp;lt;Roles&amp;gt;? Roles { get; set; }
        public string? Cep { get; set; }

        public User Convert(IPasswordHasher hasher, ViaCepResponse response)
        {

            User user = new User();
            user.Name = Name;
            user.Password = hasher.Hash(Password);
            user.Email = Email;
            user.Roles = Roles;
            user.Endereco = user.GetEndereco(response);
            return user;
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RegisterUserOutput&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Domain.Entities;
using FirstApi.Domain.ValueObjects;

namespace FirstApi.Application.UseCases.CasesUser.RegisterUser
{
    public class RegisterUserOutput
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Email { get; set; }
        public Endereco? Endereco { get; set; }

        public List&amp;lt;string&amp;gt;? Roles { get; set; }

        public RegisterUserOutput Convert(User user)
        {
            Id = user.Id;
            Name = user.Name;
            Email = user.Email;
            Endereco = user.Endereco;
            Roles = user.Roles.Select(x =&amp;gt; x.ToString()).ToList();
            return this;
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto, feito isso vamos implementar agora na camada de acesso externo de nossa aplicação um controller para fazer a autenticação/autorização do usuário.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkmui87x6qi0lmemnujl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkmui87x6qi0lmemnujl.png" alt="Image description" width="360" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AuthController&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Application.UseCases.CasesAuth.Login;
using Microsoft.AspNetCore.Mvc;

namespace FirstApi.Infrastructure.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController
    {
        private ILoginService _loginService;

        public AuthController(ILoginService loginService)
        {
            _loginService = loginService;
        }

        [HttpPost]
        public LoginOutput Login(LoginInput login)
        {
            return _loginService.Execute(login);
        }

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como adicionamos interfaces de services e necessário configurar a inejçao de dependência destes na nossa classe &lt;code&gt;Program.cs&lt;/code&gt; e também adicionar uma configuração para que o swagger entenda o fluxo de autenticação da nossa api. Sua classe &lt;code&gt;Program.cs&lt;/code&gt; devera ficar assim.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
using FirstApi.Application.UseCases.CasesAuth.Login;
using FirstApi.Application.UseCases.CasesEmployer.ConsultEmployer;
using FirstApi.Application.UseCases.CasesEmployer.DeleteEmployer;
using FirstApi.Application.UseCases.CasesEmployer.Register;
using FirstApi.Application.UseCases.CasesEmployer.UpdateEmployer;
using FirstApi.Application.UseCases.CasesUser.ConsultUser;
using FirstApi.Application.UseCases.CasesUser.DeleteUser;
using FirstApi.Application.UseCases.CasesUser.RegisterUser;
using FirstApi.Application.UseCases.CasesUser.UpdateUser;
using FirstApi.Application.UseCases.PasswordHasher;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.Data;
using FirstApi.Infrastructure.Handler;
using FirstApi.Infrastructure.Integration.ViaCep;
using FirstApi.Infrastructure.Integration.ViaCep.Refit;
using FirstApi.Infrastructure.Repositories;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Refit;
using System.Text;

namespace FirstApi
{
    public class Program
    {

        public static void Main(string[] args)
        {
            string secretKey = "e0606215-c2e4-46fe-8d73-a77e1fa4f45a";
            var builder = WebApplication.CreateBuilder(args);
            // Configure logging
            builder.Logging.ClearProviders();
            builder.Logging.AddConsole();
            builder.Logging.AddDebug();
            // Configure data base access
            builder.Services.AddDbContext&amp;lt;SystemDbContext&amp;gt;(
                options =&amp;gt; options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
            // Add repositories to the container.
            builder.Services.AddScoped&amp;lt;IEmployerRepository, EmployerRepository&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUserRepository, UserRepository&amp;gt;();
            // Add services to the container.
            // employer
            builder.Services.AddScoped&amp;lt;IRegisterEmployerService, RegisterEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUpdateEmployerService, UpdateEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IConsultEmployerService, ConsultEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IDeleteEmployerService, DeleteEmployerService&amp;gt;();
            // user
            builder.Services.AddScoped&amp;lt;IRegisterUserService, RegisterUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUpdateUserService, UpdateUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IConsultUserService, ConsultUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IDeleteUserService, DeleteUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IPasswordHasher, PasswordHasher&amp;gt;();
            builder.Services.AddScoped&amp;lt;ILoginService,LoginService&amp;gt;();
            // client viacep
            builder.Services.AddScoped&amp;lt;IViaCepIntegrationService, ViaCepIntegrationService&amp;gt;();
            // Add client refit
            builder.Services.AddRefitClient&amp;lt;IViaCepIntegrationRefit&amp;gt;().ConfigureHttpClient(c =&amp;gt;
            {
                c.BaseAddress = new Uri("https://viacep.com.br");
            }
            );

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen(sw =&amp;gt;
            {
                sw.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo {
                    Title = "ApiFirst", Version = "v1"
                });
                var securitySchema = new OpenApiSecurityScheme
                {
                    Name = "Jwt Authentication",
                    Description = "Enter with your Jwt Bearer Token",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.Http,
                    Scheme = "Bearer",
                    Reference = new OpenApiReference
                    {
                        Id = JwtBearerDefaults.AuthenticationScheme,
                        Type = ReferenceType.SecurityScheme
                    }
                };
                sw.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, securitySchema);
                sw.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    { securitySchema, new string[] { } }
                });
            });

            // Config authentication
            builder.Services.AddAuthentication(options =&amp;gt;
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =&amp;gt;
            {
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                {
                    ValidateIssuer = true,
                    ValidateAudience= true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = "emprise",
                    ValidAudience = "firstApi",
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
                };
            });

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseAuthorization();

            // global error handler
            app.UseMiddleware&amp;lt;GlobalExceptionHandler&amp;gt;();

            app.MapControllers();

            app.Run();
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto, agora e só rodar a aplicação e testarmos a parte de segurança no swagger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Teste usuario com token mas sem role de acesso&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Dados usuarios&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbc1u4svitj5d71njhdv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbc1u4svitj5d71njhdv.png" alt="Image description" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Gerando token usuario id 3, sem role de Admin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4dwvtzhf1hpatcejhm6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4dwvtzhf1hpatcejhm6b.png" alt="Image description" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnghoc3sa7dq79d3p0fl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnghoc3sa7dq79d3p0fl.png" alt="Image description" width="800" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Request endpoint lista usuários, necessário role de Admin para acesso&lt;/code&gt;&lt;br&gt;
Retorno statuscode 403, ou seja estamos autenticado porem nao autorizados a acessar o recurso.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvre6ndxbncj3pvagpag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvre6ndxbncj3pvagpag.png" alt="Image description" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Teste usuario com token e role de acesso&lt;/strong&gt;&lt;br&gt;
Vamos utilizar o mesmo token gerado com user id = 3, que possui a role USERCOMMON solicitada para buscar os dados de Employers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1w81r9cqp1x2g7w30tp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1w81r9cqp1x2g7w30tp.png" alt="Image description" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Request endpoint lista employers, necessário role de USERCOMMON para acesso&lt;/code&gt;&lt;br&gt;
Retorno statuscode 200, ou seja estamos autenticado e autorizados a acessar o recurso.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fli9256t8z442y6ap0jg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fli9256t8z442y6ap0jg4.png" alt="Image description" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finalizamos por aqui galerinha, deixe seu feedback por favor sobre o artigo e deixo aqui minhas redes sociais para quem quiser me adicionar e trocar uma ideia sobre desenvolvimento de softwares para aprendermos juntos, ate a próxima.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/2020nani" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>dotnet</category>
      <category>architecture</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>Api Rest .Net completa com JwtToken, integração com api ViaCep utilizando padrão de arquitetura clean architecture</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Sat, 27 Jul 2024 01:50:21 +0000</pubDate>
      <link>https://forem.com/2020nani/api-rest-net-completa-com-jwttoken-e-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-5b1n</link>
      <guid>https://forem.com/2020nani/api-rest-net-completa-com-jwttoken-e-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-5b1n</guid>
      <description>&lt;p&gt;Ferramentas necessárias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/pt-br/download/visual-studio-sdks" rel="noopener noreferrer"&gt;Sdk .net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/windows/install/#:~:text=%20Install%20Docker%20Desktop%20on%20Windows%20%F0%9F%94%97%20,to%20authorize%20the%20installer%20and%20proceed...%20More%20" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://visualstudio.microsoft.com/pt-br/vs/community/" rel="noopener noreferrer"&gt;Visual Studio Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neste artigo vamos implementar uma api rest utilizando o padrao &lt;strong&gt;Clean Architecture&lt;/strong&gt;, nessa api iremos &lt;strong&gt;integrar com uma api externa para buscar dados, configurar e acessar banco de dados postgres fazendo mapeamento e relacionamento de tabelas, criptografar dados para salvar banco de dados e configurar o swagger para realizar a autenticação e o crud em nossa api&lt;/strong&gt;, bora la.&lt;br&gt;
Para iniciar e necessário fazer o download e instalar o Sdk do .net e a ide Visual Studio Community, links logo acima.&lt;br&gt;
Dentro do terminal digite o comando &lt;code&gt;dotnet --version&lt;/code&gt; e devera retornar a versão instalada do sdk que você tem instalada.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjvzvrmjifwd2du3xbi5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjvzvrmjifwd2du3xbi5.png" alt="Image description" width="347" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feito isso abra o Visual Studio e crie um novo projeto api web conforme o passo a passo abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs60d9h16jva0ux2u3b21.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs60d9h16jva0ux2u3b21.png" alt="Image description" width="800" height="59"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0iaug9wfm0zduzi6qzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0iaug9wfm0zduzi6qzh.png" alt="Image description" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa4tvtq50t43llv87ayny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa4tvtq50t43llv87ayny.png" alt="Image description" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1iz29296nbdxo6sm168k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1iz29296nbdxo6sm168k.png" alt="Image description" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Abra esse projeto dentro do visual studio e ele ira ter a seguinte estrutura&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16s317wo11a6ig401w1w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16s317wo11a6ig401w1w.png" alt="Image description" width="484" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Antes de iniciarmos, ja vamos instalar dependencias que iremos utilizar em nossa aplicação que serão as seguintes&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyn6q9621ea51k8gg4rny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyn6q9621ea51k8gg4rny.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instale cada uma delas seguindo o passo a passo abaixo&lt;br&gt;
Clique botão direito na raiz da aplicação e entre em &lt;code&gt;Gerenciar pacotes do nuget&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndt7psb2gy1s64a21g4f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndt7psb2gy1s64a21g4f.png" alt="Image description" width="774" height="262"&gt;&lt;/a&gt;&lt;br&gt;
Na aba que ira abrir vá em &lt;code&gt;Procurar&lt;/code&gt; e digite cada dependencia acima para instalar ao projeto conforme abaixo&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l31t7juov2mjzvxhna0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l31t7juov2mjzvxhna0.png" alt="Image description" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos estruturar nossa api utilizando o padrão de arquitetura ´Clean Architecture´ o que nos possibilita ter um sistema coeso e de baixo acoplamento, trabalhando a separação de responsabilidades ao nosso sistema,o que facilita a manutenabilidade de um sistema complexo e robusto. &lt;br&gt;
Para isso vamos ter 3 camadas em nossa api fazendo a separação da mesma nas seguintes estruturas.&lt;br&gt;
&lt;strong&gt;Domain&lt;/strong&gt; Estrutura que será o nosso core, ou seja o núcleo da nossa api, dentro dela era conter o domínio da nossa aplicação e essa camada não depende de nenhuma camada do nosso sistema.&lt;br&gt;
&lt;strong&gt;Application&lt;/strong&gt; Essa camada conterá os casos de usos da nossa aplicação, contendo os serviços que manipulara o nosso domínio para realizar as transações de nossa api.&lt;br&gt;
Observação: Essa camada depende da camada de domínio porem não pode ter nenhuma dependência com a camada seguinte.&lt;br&gt;
&lt;strong&gt;Infrastructure&lt;/strong&gt; Essa camada conterá toda parte de tecnologia/infraestrutura do nosso sistema e será a camada responsável por disponibilizar acesso externo a nossa api.&lt;br&gt;
Observação: Essa camada tem dependência com as outras duas camadas citadas.&lt;br&gt;
Vamos seguir com nossa api, vamos criar 3 pastas em nossa aplicação que ira simbolizar as camadas citadas acima, a estrutura do nosso sistema devera ficar conforme a imagem abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetzgwzzgxlket8pniqfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetzgwzzgxlket8pniqfz.png" alt="Image description" width="483" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos iniciar nossa aplicação definindo nossa camada de &lt;code&gt;Domínio&lt;/code&gt;, vamos definir nossa entity &lt;code&gt;Employer&lt;/code&gt; conforme abaixo.&lt;br&gt;
&lt;strong&gt;Employer&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Domain.ValueObjects;

namespace FirstApi.Domain.Entities
{
    public class Employer
    {
        public int Id { get; set; }
        public string? Nome { get; set; }
        public string? Cargo { get; set; }

        public Payment? Payment { get; set; }
        public double? PaymentTotal { get; set; }

        public void AddPaymentByPremiation(double premiation)
        {
            PaymentTotal = PaymentTotal + premiation;
        }
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;**Observação: **Note que temos o método &lt;code&gt;addPaymentByPremiation&lt;/code&gt; em nossa entity, método e responsável por adicionar uma premiação ao pagamento do funcionário, ou seja, nossa classe de domínio não será anêmica.&lt;/p&gt;

&lt;p&gt;Vamos definir também em nossa camada de &lt;code&gt;Domínio&lt;/code&gt; uma interface com o contrato do nosso repositório ligado a entidade Employer e uma classe Payment que e um value-object da classe Employer.&lt;br&gt;
&lt;strong&gt;IEmployerRepository&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Domain.Entities;

namespace FirstApi.Domain.Repositories
{
    public interface IEmployerRepository
    {
        Task&amp;lt;List&amp;lt;Employer&amp;gt;&amp;gt; FindEmployers();
        Task&amp;lt;Employer&amp;gt; FindEmployer(int id);
        Task&amp;lt;Employer&amp;gt; Register(Employer employer);
        Task&amp;lt;Employer&amp;gt; UpdateEmployer(Employer employer);
        void DeleteEmployer(Employer employer);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Payment&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace FirstApi.Domain.ValueObjects
{
    public class Payment
    {
        public double Salary { get; set; }
        public double Benefits { get; set; }

        public double getPaymentTotal()
        {
            return Salary + Benefits;
        }
    }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A estrutura do nosso projeto agora ficou conforme a imagem abaixo, nossa camada de Domain é o núcleo da nossa api, portanto, ela não tem dependência com nenhuma outra camada do nosso sistema.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw4tiz207wbpy4y4y8kr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw4tiz207wbpy4y4y8kr8.png" alt="Image description" width="475" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seguimos agora para a camada &lt;code&gt;Application&lt;/code&gt; que e a responsável pelos serviços e implementação da regra de negocio de nossa api, na &lt;em&gt;Clean Architecture&lt;/em&gt; utilizamos &lt;code&gt;UseCases&lt;/code&gt;,ou seja, casos de uso para implementar e realizar uma ação em nossa api, nessa ação definimos o que iremos receber de dados em nossa request para realizar a ação e o que vamos retornar após a ação realizada, isso nos da a facilidade para alterarmos, se necessário, tanto os dados de entrada como os de saída da nossa api tornando nossa aplicação resiliente a mudanças.&lt;br&gt;
Para não estender o artigo não vou seguir passo a passo mas o codigo dessa camada pode ser visualizado no &lt;a href="https://github.com/2020nani/dotnet-article/tree/master/Application/UseCases/CasesEmployer" rel="noopener noreferrer"&gt;repositorio&lt;/a&gt;&lt;br&gt;
Nossa camada de &lt;code&gt;Application&lt;/code&gt; sera implementado 4 casos de uso ligados a entity Employer e ficara implementado conforme abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7l1z9r7pbo6bbnywza7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7l1z9r7pbo6bbnywza7.png" alt="Image description" width="433" height="602"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Observação:&lt;/strong&gt; Como já citamos antes essa camada tem dependência com a camada de &lt;code&gt;Domain&lt;/code&gt; pois utilizamos a entity &lt;code&gt;Employer&lt;/code&gt; e a interface &lt;code&gt;IEmployerRepository&lt;/code&gt; definida na camada de domínio que será implementada na camada mais externa da nossa aplicação, ou seja, a &lt;strong&gt;Infrastructure&lt;/strong&gt;.&lt;br&gt;
Para realizarmos a injeção da classe IEmployerRepository e dos services de cada casos de uso via construtor temos que adiciona-los ao escopo da nossa aplicação dentro da classe Program.cs, que ficara conforme abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Application.UseCases.CasesEmployer.ConsultEmployer;
using FirstApi.Application.UseCases.CasesEmployer.DeleteEmployer;
using FirstApi.Application.UseCases.CasesEmployer.Register;
using FirstApi.Application.UseCases.CasesEmployer.UpdateEmployer;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.Data;
using FirstApi.Infrastructure.Handler;
using FirstApi.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;

namespace FirstApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddScoped&amp;lt;IEmployerRepository, EmployerRepository&amp;gt;();
            builder.Services.AddScoped&amp;lt;IRegisterEmployerService, RegisterEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUpdateEmployerService, UpdateEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IConsultEmployerService, ConsultEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IDeleteEmployerService, DeleteEmployerService&amp;gt;();

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            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();

            // global error handler
            app.UseMiddleware&amp;lt;GlobalExceptionHandler&amp;gt;();

            app.MapControllers();

            app.Run();
        }
    }
}
A classe EmployerRepository iremos construir na camada de Infrastructure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após definirmos nossos &lt;code&gt;Usecases&lt;/code&gt; vamos seguir agora para a camada seguinte da nossa aplicação, a camada &lt;code&gt;Infrastructure&lt;/code&gt;, responsável pela parte tecnológica do nosso sistema e por disponibilizar acesso externo a nossa api, nessa camada fica as classes de Controllers, configurações banco de dados, implementações dos repositórios para acesso a transações no banco de dados, fluxo de mensagerias e etc...&lt;br&gt;
Vamos configurar agora a conexão ao nossa database postgres, vamos criar uma pasta &lt;code&gt;Data&lt;/code&gt; e dentro dela uma classe &lt;code&gt;SystemDbContext.cs&lt;/code&gt; que ira configurar um &lt;strong&gt;DbContext&lt;/strong&gt;, um tipo de ORM em dotnet.&lt;br&gt;
&lt;strong&gt;SystemDbContext&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Domain.Entities;
using FirstApi.Infrastructure.Data.Map;
using Microsoft.EntityFrameworkCore;

namespace FirstApi.Infrastructure.Data
{
    public class SystemDbContext : DbContext
    {
        public SystemDbContext(DbContextOptions&amp;lt;SystemDbContext&amp;gt; options)
            : base(options)
        {

        }

        public DbSet&amp;lt;Employer&amp;gt; Employers { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfiguration(new EmployerMap());
            base.OnModelCreating(modelBuilder);
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dentro da pasta &lt;code&gt;Data&lt;/code&gt; crie uma nova pasta &lt;code&gt;Map&lt;/code&gt; e dentro dela uma classe &lt;code&gt;EmployerMap.cs&lt;/code&gt; que ira mapear a tabela Employer para ser criada em nosso database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
using FirstApi.Domain.Entities;

namespace FirstApi.Infrastructure.Data.Map
{
    public class EmployerMap : IEntityTypeConfiguration&amp;lt;Employer&amp;gt;
    {
        public void Configure(EntityTypeBuilder&amp;lt;Employer&amp;gt; builder)
        {
            builder.HasKey(x =&amp;gt; x.Id);
            builder.Property(x =&amp;gt; x.Nome).IsRequired().HasMaxLength(255);
            builder.Property(x =&amp;gt; x.Cargo).IsRequired().HasMaxLength(255);
            builder.Property(x =&amp;gt; x.PaymentTotal).IsRequired();
            builder.ToTable("Employers")
                .OwnsOne(x =&amp;gt; x.Payment, x =&amp;gt;
                {
                    x.Property(a =&amp;gt; a.Salary)
                    .HasColumnName("salary")
                    .IsRequired();
                    x.Property(a =&amp;gt; a.Benefits)
                    .HasColumnName("benefits")
                    .IsRequired()
                    .HasDefaultValue(0);
                });
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após isso temos que definir nossa string de conexão ao database no arquivo appsetings.Development.json que ficara assim.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Port=5433;Database=net;Username=admin;Password=password"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos configurar na classe main da nossa aplicação &lt;code&gt;Program.cs&lt;/code&gt; as configurações de acesso ao banco conforme abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl89m9lkhkajhw3ib9hap.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl89m9lkhkajhw3ib9hap.png" alt="Image description" width="800" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos rodar dois comandos dentro do console Gerenciador de pacotes que ira criar uma migration e criar nossa tabela dentro do database.&lt;br&gt;
Va no console Gerenciador de pacotes que entre com o comando &lt;code&gt;Add-Migration InitialDB -Context SystemDbContext&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0e7wvu5ng2kaizx3xczy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0e7wvu5ng2kaizx3xczy.png" alt="Image description" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Danto tudo certo, em seguida entre com o comando &lt;code&gt;Update-Database -Context SystemDbContext&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgywunk0sl7gytbu73g28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgywunk0sl7gytbu73g28.png" alt="Image description" width="800" height="504"&gt;&lt;/a&gt;&lt;br&gt;
Crie uma pasta &lt;code&gt;Repositories&lt;/code&gt; e dentro dela a classe &lt;code&gt;EmployerRepository&lt;/code&gt; que ira implementar a classe &lt;code&gt;IEmployerRepository&lt;/code&gt; definida em nosso dominio.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;﻿using FirstApi.Domain.Entities;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace FirstApi.Infrastructure.Repositories
{
    public class EmployerRepository : IEmployerRepository
    {
        private readonly SystemDbContext _Dbcontext;

        public EmployerRepository(SystemDbContext context)
        {
            _Dbcontext = context;
        }

        async Task&amp;lt;Employer&amp;gt; IEmployerRepository.Register(Employer employer)
        {
            await _Dbcontext.Employers.AddAsync(employer);
            _Dbcontext.SaveChanges();
            return employer;
        }

        async void IEmployerRepository.DeleteEmployer(Employer employer)
        {
            _Dbcontext.Employers.Remove(employer);
            await _Dbcontext.SaveChangesAsync();
        }

        async Task&amp;lt;Employer&amp;gt; IEmployerRepository.UpdateEmployer(Employer employer)
        {
            _Dbcontext.Employers.Update(employer);
            await _Dbcontext.SaveChangesAsync();
            return employer;
        }

        async Task&amp;lt;Employer&amp;gt; IEmployerRepository.FindEmployer(int id)
        {
            return await _Dbcontext.Employers.FirstOrDefaultAsync(u =&amp;gt; u.Id == id);
        }

        async Task&amp;lt;List&amp;lt;Employer&amp;gt;&amp;gt; IEmployerRepository.FindEmployers()
        {
            return await _Dbcontext.Employers.ToListAsync();
        }

    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crie agora uma pasta &lt;code&gt;Controllers&lt;/code&gt; e dentro a classe &lt;code&gt;EmployerController.cs&lt;/code&gt; que será a responsável por disponibilizar acesso externo a nossa api via chamadas Http e ficara conforme abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;﻿using FirstApi.Application.UseCases.CasesEmployer;
using FirstApi.Application.UseCases.CasesEmployer.ConsultEmployer;
using FirstApi.Application.UseCases.CasesEmployer.DeleteEmployer;
using FirstApi.Application.UseCases.CasesEmployer.Register;
using FirstApi.Application.UseCases.CasesEmployer.UpdateEmployer;
using FirstApi.Domain.Entities;
using Microsoft.AspNetCore.Mvc;

namespace FirstApi.Infrastructure.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployerController
    {
        private  IRegisterEmployerService _registerService;
        private IUpdateEmployerService _updateService;
        private IConsultEmployerService _consultService;
        private IDeleteEmployerService _deleteService;

        public EmployerController(
            IRegisterEmployerService service,
            IUpdateEmployerService updateService,
            IConsultEmployerService consultService,
            IDeleteEmployerService deleteService)
        {
            _registerService = service;
            _updateService = updateService;
            _consultService = consultService;
            _deleteService = deleteService;
        }

        [HttpPost]
        public RegisterEmployerOutput Post([FromBody] RegisterEmployerInput input)
        {
            return _registerService.Execute(input);
        }

        [HttpPut]
        public UpdateEmployerOutput Put([FromBody] UpdateEmployerInput input)
        {
            return _updateService.Execute(input);
        }

        [HttpGet("{id}")]
        public ConsultEmployerOutput findEmployer(int id)
        {
            return _consultService.FindEmployerById(id);
        }

        [HttpGet]
        public List&amp;lt;ConsultEmployerOutput&amp;gt; findEmployers()
        {
            return _consultService.FindEmployers();
        }

        [HttpDelete("{id}")]
        public string Delete(int id)
        {
            return _deleteService.DeleteEmployer(id);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nossa camada &lt;code&gt;Infrastructure&lt;/code&gt; ficara assim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7r4umxo8fvbkcr4ik6aw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7r4umxo8fvbkcr4ik6aw.png" alt="Image description" width="356" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Crie um arquivo com o nome docke-compose.yaml na raiz do projeto, vamos configurar esse arquivo para subir um DB Postgres no docker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'

services:

 postgres:
   image: 'postgres:alpine'
   volumes:
     - postgres-volume:/var/lib/postgresql/data
   ports:
     - 5433:5432
   environment:
     POSTGRES_USER: admin
     POSTGRES_PASSWORD: password
     POSTGRES_DB: net
volumes:
  postgres-volume:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Na pasta raiz rode o comando docker-compose up -d conforme abaixo para subir o database no docker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzdwu53qpuoy3zrtf4y6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzdwu53qpuoy3zrtf4y6.png" alt="Image description" width="728" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2uceqpm8weon5jf7vky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2uceqpm8weon5jf7vky.png" alt="Image description" width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos rodar nossa aplicação clicando nesse botao que fica na parte de cima do visual studio&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xifj65v3u88ai9phd00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xifj65v3u88ai9phd00.png" alt="Image description" width="162" height="74"&gt;&lt;/a&gt;&lt;br&gt;
E já podemos ver o swagger da nossa aplicação.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuqy7mo2uvq3z4wcqnvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuqy7mo2uvq3z4wcqnvs.png" alt="Image description" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos testar nossa aplicação, primeiro vou realizar um registro&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvz0nxldbe17da08tfb1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvz0nxldbe17da08tfb1v.png" alt="Image description" width="800" height="408"&gt;&lt;/a&gt;&lt;br&gt;
 Agora vou buscar esse registro pelo Id&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs0nvqb4es84kcr82q5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs0nvqb4es84kcr82q5p.png" alt="Image description" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para seguirmos o projeto vamos ter a segunda parte desse artigo, onde vamos criar um fluxo para salvar um usuário obtendo seus dados de Endereço através de uma integração da nossa api com a api &lt;strong&gt;ViaCep&lt;/strong&gt;, ate la.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/2020nani/api-rest-net-completa-com-jwttoken-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-parte-2-im"&gt;Parte 2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/2020nani" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>restapi</category>
      <category>csharp</category>
      <category>security</category>
    </item>
    <item>
      <title>Api Rest .Net completa com JwtToken integração com api ViaCep utilizando padrão de arquitetura clean architecture - parte 2</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Sat, 27 Jul 2024 01:47:48 +0000</pubDate>
      <link>https://forem.com/2020nani/api-rest-net-completa-com-jwttoken-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-parte-2-im</link>
      <guid>https://forem.com/2020nani/api-rest-net-completa-com-jwttoken-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-parte-2-im</guid>
      <description>&lt;p&gt;Ferramentas necessárias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/pt-br/download/visual-studio-sdks" rel="noopener noreferrer"&gt;Sdk .net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/windows/install/#:~:text=%20Install%20Docker%20Desktop%20on%20Windows%20%F0%9F%94%97%20,to%20authorize%20the%20installer%20and%20proceed...%20More%20" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://visualstudio.microsoft.com/pt-br/vs/community/" rel="noopener noreferrer"&gt;Visual Studio Community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/2020nani/api-rest-net-completa-com-jwttoken-e-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-5b1n"&gt;Ter completado o crud da api mostrado no artigo anterior&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seguimos com a construção da nossa api, neste artigo vamos estruturar nossa api para salvar um usuário integrando com a api &lt;code&gt;ViaCep&lt;/code&gt; para buscar os dados de endereço desse usuário através do Cep.&lt;br&gt;
Vamos criar estrutura para salvar um usuário, vamos criar a entity &lt;code&gt;User&lt;/code&gt; que ira conter os dados de endereço do usuário através da classe &lt;code&gt;Endereco&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Domain.ValueObjects;
using FirstApi.Infrastructure.Integration.ViaCep;

namespace FirstApi.Domain.Entities
{
    public class User
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Email { get; set; }
        public string? Password { get; set; }
        public Endereco? Endereco { get; set; }

        public Endereco GetEndereco(ViaCepResponse response)
        {
            Endereco endereco = new Endereco();
            if(response is null)
            {
                return endereco;
            }
            endereco.Complemento = response.Complemento;
            endereco.Cep = response.Cep;
            endereco.Bairro = response.Bairro;
            endereco.Logradouro = response.Logradouro;
            endereco.Localidade = response.Localidade;
            endereco.Cep = response.Cep;
            endereco.Unidade = response.Unidade;
            endereco.Uf = response.Uf;
            return endereco;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Endereco&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using FirstApi.Infrastructure.Integration.ViaCep;

namespace FirstApi.Domain.ValueObjects
{
    public class Endereco
    {
        public string? Cep { get; set; }
        public string? Logradouro { get; set; }
        public string? Complemento { get; set; }
        public string? Unidade { get; set; }
        public string? Bairro { get; set; }
        public string? Localidade { get; set; }
        public string? Uf { get; set; }

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A estrutura vamos seguir a mesma que a &lt;a href="https://dev.tolink"&gt;parte 1 desse artigo&lt;/a&gt;, ou se preferir pode visitar o &lt;a href="https://github.com/2020nani/dotnet-article/tree/master" rel="noopener noreferrer"&gt;repositorio&lt;/a&gt; contendo o código para usar como referencia.&lt;br&gt;
Na classe de entrada de dados para registrar nosso usuário vamos receber um parâmetro &lt;code&gt;Cep&lt;/code&gt;, através desse parâmetro vamos realizar uma chamada para a api &lt;code&gt;ViaCep&lt;/code&gt; para preencher os dados de endereço do usuário, vamos utilizar a lib &lt;code&gt;Refit&lt;/code&gt; para realizar essa integração.&lt;br&gt;
Conforme vimos no artigo anterior vamos instalar essas duas dependências no projeto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4zv8kuq1b38a4vnn59s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4zv8kuq1b38a4vnn59s.png" alt="Image description" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feito isso, seguindo o padrão da &lt;code&gt;Clean Architecture&lt;/code&gt; na camada de &lt;strong&gt;Infrastructure&lt;/strong&gt; vamos criar uma pasta Integration e a seguinte estrutura dentro dela.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe25blsx64br8g4suiwyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe25blsx64br8g4suiwyf.png" alt="Image description" width="418" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ViaCepResponse&lt;/strong&gt; classe que mapeara o response oriundo da api ViaCep&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace FirstApi.Infrastructure.Integration.ViaCep
{
    public class ViaCepResponse
    { 
        public string? Cep { get; set; }
        public string? Logradouro { get; set; }
        public string? Complemento { get; set; }
        public string? Unidade { get; set; }
        public string? Bairro { get; set; }
        public string? Localidade { get; set; }
        public string? Uf {  get; set; }

}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IViaCepIntegrationRefit&lt;/strong&gt; classe onde configuramos o client de acesso ao endpoint da api ViaCep&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Refit;

namespace FirstApi.Infrastructure.Integration.ViaCep.Refit
{
    public interface IViaCepIntegrationRefit
    {
        [Get("/ws/{cep}/json/")]
        public Task&amp;lt;ApiResponse&amp;lt;ViaCepResponse&amp;gt;&amp;gt; FindEnderecoByCep(string cep);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IViaCepIntegrationService&lt;/strong&gt; contrato do serviço que utilizara o client para realizar a chamada em nossa logica.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace FirstApi.Infrastructure.Integration.ViaCep
{
    public interface IViaCepIntegrationService
    {
        public Task&amp;lt;ViaCepResponse&amp;gt; FindEnderecoByCep(string cep);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ViaCepIntegrationService&lt;/strong&gt; serviço que implementa o serviço para buscar os dados de Endereço do usuário.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
using FirstApi.Infrastructure.CustomException;
using FirstApi.Infrastructure.Integration.ViaCep.Refit;

namespace FirstApi.Infrastructure.Integration.ViaCep
{
    public class ViaCepIntegrationService : IViaCepIntegrationService
    {
        private readonly IViaCepIntegrationRefit _refit;

        public ViaCepIntegrationService(IViaCepIntegrationRefit refit)
        {
            _refit = refit;
        }

        async Task&amp;lt;ViaCepResponse&amp;gt; IViaCepIntegrationService.FindEnderecoByCep(string cep)
        {
            var responseData = await _refit.FindEnderecoByCep(cep);
            if(responseData is null || !responseData.IsSuccessStatusCode)
            {
                throw new AppException("Error on integration with extern api");
            }

            return responseData.Content;
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos agora adicionar o escopo de cada serviço na nossa classe &lt;code&gt;Program.cs&lt;/code&gt;, para podermos realizar a injeção de dependência, e configurar o RefitClient também em nossa classe &lt;code&gt;Program.cs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8zrbp8e4lr2sbbzeloh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8zrbp8e4lr2sbbzeloh.png" alt="Image description" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nossa classe &lt;code&gt;Program.cs&lt;/code&gt; ficara assim.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
using FirstApi.Application.UseCases.CasesEmployer.ConsultEmployer;
using FirstApi.Application.UseCases.CasesEmployer.DeleteEmployer;
using FirstApi.Application.UseCases.CasesEmployer.Register;
using FirstApi.Application.UseCases.CasesEmployer.UpdateEmployer;
using FirstApi.Application.UseCases.CasesUser.ConsultUser;
using FirstApi.Application.UseCases.CasesUser.DeleteUser;
using FirstApi.Application.UseCases.CasesUser.RegisterUser;
using FirstApi.Application.UseCases.CasesUser.UpdateUser;
using FirstApi.Application.UseCases.PasswordHasher;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.Data;
using FirstApi.Infrastructure.Handler;
using FirstApi.Infrastructure.Integration.ViaCep;
using FirstApi.Infrastructure.Integration.ViaCep.Refit;
using FirstApi.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
using Refit;

namespace FirstApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            // Configure logging
            builder.Logging.ClearProviders();
            builder.Logging.AddConsole();
            builder.Logging.AddDebug();
            // Configure data base access
            builder.Services.AddDbContext&amp;lt;SystemDbContext&amp;gt;(
                options =&amp;gt; options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
            // Add repositories to the container.
            builder.Services.AddScoped&amp;lt;IEmployerRepository, EmployerRepository&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUserRepository, UserRepository&amp;gt;();
            // Add services to the container.
            // employer
            builder.Services.AddScoped&amp;lt;IRegisterEmployerService, RegisterEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUpdateEmployerService, UpdateEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IConsultEmployerService, ConsultEmployerService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IDeleteEmployerService, DeleteEmployerService&amp;gt;();
            // user
            builder.Services.AddScoped&amp;lt;IRegisterUserService, RegisterUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IUpdateUserService, UpdateUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IConsultUserService, ConsultUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IDeleteUserService, DeleteUserService&amp;gt;();
            builder.Services.AddScoped&amp;lt;IPasswordHasher, PasswordHasher&amp;gt;();
            // client viacep
            builder.Services.AddScoped&amp;lt;IViaCepIntegrationService, ViaCepIntegrationService&amp;gt;();
            // Add client refit
            builder.Services.AddRefitClient&amp;lt;IViaCepIntegrationRefit&amp;gt;().ConfigureHttpClient(c =&amp;gt;
            {
                c.BaseAddress = new Uri("https://viacep.com.br");
            }
            );

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            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();

            // global error handler
            app.UseMiddleware&amp;lt;GlobalExceptionHandler&amp;gt;();

            app.MapControllers();

            app.Run();
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dentro do nosso &lt;code&gt;UserController&lt;/code&gt; chamamos a api ViaCep para popularmos nosso objeto ViaCepResponse, que e passada como parâmetro para nosso serviço junto com o input de entrada de dados.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb17knvazegudnhp7y2yy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb17knvazegudnhp7y2yy.png" alt="Image description" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dentro do nosso serviço utilizamos o método &lt;code&gt;Convert&lt;/code&gt; implementado dentro da nossa classe de entrada de dados, seguindo o conceito de não termos classes anêmicas em nosso projeto, que ira retornar um objeto &lt;code&gt;User&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcp3dood9so13x01tya4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcp3dood9so13x01tya4.png" alt="Image description" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqmg5fov4ufjmcyxvr87r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqmg5fov4ufjmcyxvr87r.png" alt="Image description" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lembre-se de seguir o passo a passo do &lt;a href="https://dev.tolink"&gt;artigo 1&lt;/a&gt; pois será necessário mapear a entity &lt;code&gt;User&lt;/code&gt; e criar a tabela &lt;code&gt;Users&lt;/code&gt; no nosso database, feito isso tudo já esta pronto para testarmos nossa api.&lt;/p&gt;

&lt;p&gt;Requisição POST&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqrg24yct6dyo1zceran.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqrg24yct6dyo1zceran.png" alt="Image description" width="800" height="371"&gt;&lt;/a&gt;&lt;br&gt;
Retorno da Api com os dados completos do usuário, inclusive endereço&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ru6mkq66s3j1i3duob9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ru6mkq66s3j1i3duob9.png" alt="Image description" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Registro salvo banco de dados.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3d4b7yx7818g50gcd0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3d4b7yx7818g50gcd0y.png" alt="Image description" width="800" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na próxima parte vamos implementar a parte de segurança da nossa api utilizando o JWt Token, ate lá.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/2020nani/api-rest-net-completa-com-jwttoken-integracao-com-api-viacep-utilizando-padrao-de-arquitetura-clean-architecture-parte-3-21o9"&gt;Parte 3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/2020nani" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>cleancode</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Saga Pattern in Microservices</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Tue, 12 Mar 2024 02:15:36 +0000</pubDate>
      <link>https://forem.com/2020nani/saga-pattern-in-microservices-3h6i</link>
      <guid>https://forem.com/2020nani/saga-pattern-in-microservices-3h6i</guid>
      <description>&lt;p&gt;Neste post vamos desmistificar o design do padrão saga, vamos construir um sistema de microsserviços que estará salvando dados completos do usuário onde usaremos o &lt;strong&gt;Orchestration Saga Pattern&lt;/strong&gt;, abaixo vemos o fluxograma do projeto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiukfpwnoc4kvvb3py779.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiukfpwnoc4kvvb3py779.png" alt="Image description" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UserService&lt;/strong&gt; - Essa API (Java) ira receber a requisição via rest http de um cadastro de usuário e produzir uma mensagem em um tópico &lt;strong&gt;Kafka&lt;/strong&gt; para que a mesma seja consumida pela aplicação orquestradora.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Orchestrator&lt;/strong&gt; - Essa API (Java) recebe a mensagem produzida pela API acima através de um Listener de um tópico KAFKA que estará captando as mensagem produzidas por este tópico e realiza a orquestração de todo fluxo da transação para os serviços seguintes atraves da &lt;strong&gt;(Events-Driven Architecture)&lt;/strong&gt;, ou seja via eventos, para garantir o sucesso da transação e o rolback da mesma caso exista alguma falha.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AdressService&lt;/strong&gt; - Essa API (Java - Webflux) recebe a mensagem produzida pela API Orchestrator pelo listener do tópico kafka e através do cep passado na requisição faz uma integração via webclient &lt;strong&gt;(Api de Data que iremos construir utilizando nodejs(Graphql)&lt;/strong&gt; para buscar o endereço desse usuário e adicionar esses dados na transação.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ValidatedService&lt;/strong&gt; - Essa API (Kotlin) recebe a mensagem produzida pela API Orchestrator pelo listener do tópico kafka e valida se o cpf do usuário esta apto para ser cadastrado através da api de Data construida em graphql.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UserRegistration&lt;/strong&gt; - Essa API (Kotlin) recebe a mensagem produzida pela API Orchestrator pelo listener do tópico kafka e persiste os dados completos do usuario no banco de dados.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DataService&lt;/strong&gt; - Essa API (Nodejs/Graphql) ira nos fornecer dados relacionados ao usuário, como endereço a partir do cep e validar se o documento passado na requisição não esta numa lista de documentos bloqueados.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nesse artigo vamos utilizar o &lt;strong&gt;Saga Pattern Orchestration&lt;/strong&gt;, que consiste em ter um microsserviço orquestrador para garantir a execução com sucesso ou rollback dessa transação garantindo sua &lt;strong&gt;atomicidade&lt;/strong&gt;. Há também o &lt;strong&gt;Saga Pattern Choreography&lt;/strong&gt; que pretendo fazer outro post para aprendermos juntos sobre ele.&lt;/p&gt;

&lt;p&gt;Vamos utilizar apis em Java, Kotlin e nodejs(graphql) para deixar essas integrações mais interessante e demonstrar o poder do conceito de microsservices, onde podemos ter aplicações de varias linguagens se integrando uma com as outras, bora la que vai ser bem bacana.&lt;/p&gt;

&lt;p&gt;OBS: Esse fluxo partiu da minha ideia e de longe não seria necessário utilizar o Saga Pattern, porem este artigo tem a finalidade de entendermos melhor o funcionamento e o conceito dessa ferramenta em microsserviços.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/2020nani/saga-pattern-in-microservices-parte-2-122e"&gt;Parte 2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/2020nani" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>java</category>
      <category>kotlin</category>
      <category>node</category>
    </item>
    <item>
      <title>Saga Pattern in Microservices - Parte 2</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Tue, 12 Mar 2024 02:11:35 +0000</pubDate>
      <link>https://forem.com/2020nani/saga-pattern-in-microservices-parte-2-122e</link>
      <guid>https://forem.com/2020nani/saga-pattern-in-microservices-parte-2-122e</guid>
      <description>&lt;p&gt;Ferramentas necessárias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/java/technologies/downloads/" rel="noopener noreferrer"&gt;Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/windows/install/#:~:text=%20Install%20Docker%20Desktop%20on%20Windows%20%F0%9F%94%97%20,to%20authorize%20the%20installer%20and%20proceed...%20More%20" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;IDE de sua preferencia&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para iniciarmos nossa aplicação, será necessário preparar o ambiente local para termos acessos as tecnologias utilizadas, vamos utilizar a magia do &lt;strong&gt;Docker&lt;/strong&gt; e rodar o kafka, mongodb e postgres.&lt;br&gt;
Crie uma pasta com o nome a seu gosto e dentro da mesma crie um arquivo com o nome &lt;strong&gt;docker-compose.yaml&lt;/strong&gt; e dentro deste arquivo copie o seguinte codigo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'

services:

  user-db:
    image: mongo:latest
    container_name: user-db
    restart: always
    networks:
      - orchestrator-saga
    environment:
      - MONGO_INITDB_ROOT_USERNAME=admin
      - MONGO_INITDB_ROOT_PASSWORD=123456
    ports:
      - 27017:27017

  adress-db:
    image: 'postgres:alpine'
    container_name: adress-db
    volumes:
      - adress-volume:/var/lib/postgresql/data
    ports:
      - 5433:5432
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: password
      POSTGRES_DB: adress-db
      POSTGRES_HOST: adress-db
    networks:
      - orchestrator-saga

  userregistration-db:
    image: 'postgres:alpine'
    container_name: userregistration-db
    volumes:
      - userregistration-volume:/var/lib/postgresql/data
    ports:
      - 5434:5432
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: password
      POSTGRES_DB: userregistration-db
      POSTGRES_HOST: userregistration-db
    networks:
      - orchestrator-saga

  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    networks:
      - orchestrator-saga
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    ports:
      - 22181:2181

  kafka:
    image: confluentinc/cp-kafka:latest
    container_name: kafka
    depends_on:
      - zookeeper
    restart: "no"
    ports:
      - "2181:2181"
      - "9092:9092"
    networks:
      - orchestrator-saga
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

  redpanda-console:
    container_name: redpanda
    image: docker.redpanda.com/vectorized/console:latest
    restart: on-failure
    entrypoint: /bin/sh
    command: -c "echo \"$$CONSOLE_CONFIG_FILE\" &amp;gt; /tmp/config.yml; /app/console"
    ports:
      - "8087:8087"
    networks:
      - orchestrator-saga
    environment:
      CONFIG_FILEPATH: /tmp/config.yml
      CONSOLE_CONFIG_FILE: |
        kafka:
          brokers: ["kafka:29092"]
    depends_on:
      - "kafka"

volumes:
  adress-volume:
  userregistration-volume:

networks:
  orchestrator-saga:
    driver: bridge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora dentro da pasta execute o seguinte comando &lt;strong&gt;docker-compose up -d&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dentro da interface visual do docker você devera visualizar os containers rodando para cada tecnologia que utilizaremos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ckf19qnw7k2pjbvco7e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ckf19qnw7k2pjbvco7e.png" alt="Image description" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos utilizar o spring starter para criar nossa primeira estrutura de API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmczsfwfxdt8l0ffb7vsc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmczsfwfxdt8l0ffb7vsc.png" alt="Image description" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos adicionar uma nova lib no nosso arquivo pom.xml para que seja gerado automaticamente a documentação via swagger da nossa api.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springdoc&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;springdoc-openapi-starter-webmvc-ui&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;2.0.4&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feito isso, vamos configurar nosso tópicos kafka e acesso ao mongoDb através do arquivo de properties da aplicação, defina o arquivo de application.properties igual o código abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server.port=3000

spring.kafka.bootstrap-servers=${KAFKA_BROKER:localhost:9092}
spring.kafka.topic.start-saga=start-saga
spring.kafka.topic.notify-ending=notify-ending
spring.kafka.consumer.group-id=user-group
spring.kafka.consumer.auto-offset-reset=latest

spring.data.mongodb.database=admin
spring.data.mongodb.uri=${MONGO_DB_URI:mongodb://admin:123456@localhost:27017}

logging.level.org.apache.kafka=OFF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;para configurar nosso topico Kafka vamos criar uma classe de configuração contendo as seguintes config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package br.com.userservice.infrastructure.kafka;

import lombok.RequiredArgsConstructor;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.config.TopicBuilder;
import org.springframework.kafka.core.*;

import java.util.HashMap;
import java.util.Map;

@EnableKafka
@Configuration
@RequiredArgsConstructor
public class KafkaConfig {

    private static final Integer PARTITION_COUNT = 1;
    private static final Integer REPLICA_COUNT = 1;

    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;

    @Value("${spring.kafka.consumer.group-id}")
    private String groupId;

    @Value("${spring.kafka.consumer.auto-offset-reset}")
    private String autoOffsetReset;

    @Value("${spring.kafka.topic.start-saga}")
    private String startSagaTopic;

    @Value("${spring.kafka.topic.notify-ending}")
    private String notifyEndingTopic;

    @Bean
    public ConsumerFactory&amp;lt;String, String&amp;gt; consumerFactory() {
        return new DefaultKafkaConsumerFactory&amp;lt;&amp;gt;(consumerProps());
    }

    private Map&amp;lt;String, Object&amp;gt; consumerProps() {
        var props = new HashMap&amp;lt;String, Object&amp;gt;();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);
        return props;
    }

    @Bean
    public ProducerFactory&amp;lt;String, String&amp;gt; producerFactory() {
        return new DefaultKafkaProducerFactory&amp;lt;&amp;gt;(producerProps());
    }

    private Map&amp;lt;String, Object&amp;gt; producerProps() {
        var props = new HashMap&amp;lt;String, Object&amp;gt;();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        return props;
    }

    @Bean
    public KafkaTemplate&amp;lt;String, String&amp;gt; kafkaTemplate(ProducerFactory&amp;lt;String, String&amp;gt; producerFactory) {
        return new KafkaTemplate&amp;lt;&amp;gt;(producerFactory);
    }

    private NewTopic buildTopic(String name) {
        return TopicBuilder
                .name(name)
                .partitions(PARTITION_COUNT)
                .replicas(REPLICA_COUNT)
                .build();
    }

    @Bean
    public NewTopic startSagaTopic() {
        return buildTopic(startSagaTopic);
    }

    @Bean
    public NewTopic notifyEndingTopic() {
        return buildTopic(notifyEndingTopic);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após isto criamos uma classe controller que ira receber a requisição via http e chamar uma classe service para enviar uma mensagem com dados para nosso servico &lt;strong&gt;Orchestrator&lt;/strong&gt; que ira start/comandar/finalizar todo resto de envio de mensagens via topico Kafka para finalizar a transação de salvar um usuário.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2jtzo9csaogtisj1r2j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2jtzo9csaogtisj1r2j.png" alt="Image description" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Uma classe Service que ira agregar/gerir a regra de negocio relacionada a transação, nesse caso enviar a mensagem com os dados para o serviço de orquestração.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3suxiuot5rqaiu83007s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3suxiuot5rqaiu83007s.png" alt="Image description" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O codigo dessa aplicação ficara assim.&lt;br&gt;
&lt;a href="https://github.com/2020nani/saga_pattern/tree/master/user-service" rel="noopener noreferrer"&gt;API Saga Pattern - User Service&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/2020nani/saga-pattern-in-microservices-parte-3-3i73"&gt;Parte 3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/2020nani" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>microservices</category>
      <category>kafka</category>
    </item>
    <item>
      <title>Saga Pattern in Microservices - Parte 3</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Tue, 12 Mar 2024 02:10:42 +0000</pubDate>
      <link>https://forem.com/2020nani/saga-pattern-in-microservices-parte-3-3i73</link>
      <guid>https://forem.com/2020nani/saga-pattern-in-microservices-parte-3-3i73</guid>
      <description>&lt;p&gt;Ferramentas necessárias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/java/technologies/downloads/" rel="noopener noreferrer"&gt;Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/windows/install/#:~:text=%20Install%20Docker%20Desktop%20on%20Windows%20%F0%9F%94%97%20,to%20authorize%20the%20installer%20and%20proceed...%20More%20" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;IDE de sua preferencia&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/2020nani/teste-16oa-temp-slug-8201349?preview=92424408771c77939281b5ffdd2520e8e738ea34aa40fabb79b714d7e861f958d9a6a4c22310af4b7b3d96e4501163c596956f0aaedb6365c2ec2f3e"&gt;Configuração do ambiente conforme a parte 2 do artigo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos criar uma aplicação com o nome de &lt;strong&gt;Orchestrator&lt;/strong&gt; no &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;spring starter&lt;/a&gt; com as seguintes dependências necessárias&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc48yhvloazq317p2s21p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc48yhvloazq317p2s21p.png" alt="Image description" width="800" height="406"&gt;&lt;/a&gt;&lt;br&gt;
Essa api realizara todo o controle de eventos da nossa transação de cadastro de usuário através da &lt;strong&gt;(Events-Driven Architecture)&lt;/strong&gt;, nela ira conter a logica de envio de mensagens para seguir com a saga de cadastrar usuário ou para finalizar/realizar rolback conforme os tópicos de cada aplicação.&lt;br&gt;
Conforme a api anterior nessa api teremos o &lt;strong&gt;application.yml&lt;/strong&gt; contendo as configs do tópico kafka e também a classe de configuração do Kafka e tópicos.&lt;br&gt;
A saga e iniciada na api orquestradora através de um listener do kafka que recebe a mensagem enviada pela api &lt;strong&gt;User-Service&lt;/strong&gt;, nessa classe &lt;strong&gt;SagaOrchestratorConsumer&lt;/strong&gt; também estão configurados os listeners que continua a saga e que finalizam a saga com sucesso ou falha.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package br.com.orchestrator.application.consumer;

import br.com.orchestrator.core.service.OrchestrationService;
import br.com.orchestrator.core.utils.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@AllArgsConstructor
public class SagaOrchestratorConsumer {

    private final OrchestrationService service;
    private final JsonUtil jsonUtil;

    @KafkaListener(
        groupId = "${spring.kafka.consumer.group-id}",
        topics = "${spring.kafka.topic.start-saga}"
    )
    public void consumeStartSagaEvent(String payload) {
        log.info("Receiving event {} from start-saga topic", payload);
        var event = jsonUtil.toEvent(payload);
        service.startSaga(event);
    }

    @KafkaListener(
        groupId = "${spring.kafka.consumer.group-id}",
        topics = "${spring.kafka.topic.orchestrator}"
    )
    public void consumeOrchestratorEvent(String payload) {
        log.info("Receiving event {} from orchestrator topic", payload);
        var event = jsonUtil.toEvent(payload);
        service.continueSaga(event);
    }

    @KafkaListener(
        groupId = "${spring.kafka.consumer.group-id}",
        topics = "${spring.kafka.topic.finish-success}"
    )
    public void consumeFinishSagaSuccessEvent(String payload) {
        log.info("Receiving event {} from finish-success topic", payload);
        var event = jsonUtil.toEvent(payload);
        service.finishSagaSuccess(event);
    }

    @KafkaListener(
        groupId = "${spring.kafka.consumer.group-id}",
        topics = "${spring.kafka.topic.finish-fail}"
    )
    public void consumeFinishSagaFailEvent(String payload) {
        log.info("Receiving event {} from finish-fail topic", payload);
        var event = jsonUtil.toEvent(payload);
        service.finishSagaFail(event);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após receber a mensagem chamamos nosso service conforme a regra de negocio recebida via tópico kafka que utiliza a classe &lt;strong&gt;SagaExecutionController&lt;/strong&gt; para seguir com o fluxo da saga via event driven conforme veremos logo mais abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package br.com.orchestrator.core.service;

import br.com.orchestrator.application.dto.Event;
import br.com.orchestrator.application.dto.History;
import br.com.orchestrator.application.producer.SagaOrchestratorProducer;
import br.com.orchestrator.application.saga.SagaExecutionController;
import br.com.orchestrator.core.enums.ETopics;
import br.com.orchestrator.core.utils.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

import static br.com.orchestrator.core.enums.EEventSource.ORCHESTRATOR;
import static br.com.orchestrator.core.enums.ESagaStatus.FAIL;
import static br.com.orchestrator.core.enums.ESagaStatus.SUCCESS;

@Slf4j
@Service
@AllArgsConstructor
public class OrchestrationService {

    private final SagaOrchestratorProducer producer;
    private final JsonUtil jsonUtil;
    private final SagaExecutionController sagaExecutionController;

    public void startSaga(Event event) {
        event.setSource(ORCHESTRATOR);
        event.setStatus(SUCCESS);
        var topic = getTopic(event);
        log.info("SAGA STARTED!");
        addHistory(event, "Saga started!");
        sendToProducerWithTopic(event, topic);
    }

    public void finishSagaSuccess(Event event) {
        event.setSource(ORCHESTRATOR);
        event.setStatus(SUCCESS);
        log.info("SAGA FINISHED SUCCESSFULLY FOR EVENT {}!", event.getId());
        addHistory(event, "Saga finished successfully!");
        notifyFinishedSaga(event);
    }

    public void finishSagaFail(Event event) {
        event.setSource(ORCHESTRATOR);
        event.setStatus(FAIL);
        log.info("SAGA FINISHED WITH ERRORS FOR EVENT {}!", event.getId());
        addHistory(event, "Saga finished with errors!");
        notifyFinishedSaga(event);
    }

    public void continueSaga(Event event) {
        var topic = getTopic(event);
        log.info("SAGA CONTINUING FOR EVENT {}", event.getId());
        sendToProducerWithTopic(event, topic);
    }

    private ETopics getTopic(Event event) {
        return sagaExecutionController.getNextTopic(event);
    }

    private void addHistory(Event event, String message) {
        var history = new History(event.getSource(), event.getStatus(), message, LocalDateTime.now());
        event.addToHistory(history);
    }

    private void sendToProducerWithTopic(Event event, ETopics topic) {
        producer.sendEvent(jsonUtil.toJson(event), topic.getTopic());
    }

    private void notifyFinishedSaga(Event event) {
        producer.sendEvent(jsonUtil.toJson(event), ETopics.NOTIFY_ENDING.getTopic());
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nessa api teremos uma classe &lt;strong&gt;SagaHandler&lt;/strong&gt; que terá um método estático que retornara um vetor de uma matriz, utilizaremos essa matriz para que a api orquestradora entenda qual tópico ira enviar a mensagem para dar seguimento a saga de cadastro de usuário.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package br.com.orchestrator.application.saga;


import static br.com.orchestrator.core.enums.EEventSource.*;
import static br.com.orchestrator.core.enums.ESagaStatus.*;
import static br.com.orchestrator.core.enums.ETopics.*;

public final class SagaHandler {

    private SagaHandler() {

    }

    public static final Object[][] SAGA_HANDLER = {
        { ORCHESTRATOR, SUCCESS, ADRESS_VALIDATION_SUCCESS },
        { ORCHESTRATOR, FAIL, FINISH_FAIL },

        { ADRESS_VALIDATION_SERVICE, ROLLBACK_PENDING, ADRESS_VALIDATION_FAIL },
        { ADRESS_VALIDATION_SERVICE, FAIL, FINISH_FAIL },
        { ADRESS_VALIDATION_SERVICE, SUCCESS, VALIDATED_SUCCESS },

        { VALIDATED_SERVICE, ROLLBACK_PENDING, VALIDATED_FAIL },
        { VALIDATED_SERVICE, FAIL, ADRESS_VALIDATION_FAIL },
        { VALIDATED_SERVICE, SUCCESS, REGISTRATION_SUCCESS },

        { REGISTRATION_SERVICE, ROLLBACK_PENDING, REGISTRATION_FAIL },
        { REGISTRATION_SERVICE, FAIL, REGISTRATION_FAIL },
        { REGISTRATION_SERVICE, SUCCESS, FINISH_SUCCESS }
    };

    public static final int EVENT_SOURCE_INDEX = 0;
    public static final int SAGA_STATUS_INDEX = 1;
    public static final int TOPIC_INDEX = 2;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Criaremos uma classe &lt;strong&gt;SagaExecutionController&lt;/strong&gt; que ira ser utilizada para controlar toda a execução da saga, nesta classe que utilizamos a matriz acima para saber qual tópico kafka será enviada a mensagem para seguir saga.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package br.com.orchestrator.application.saga;

import br.com.orchestrator.application.dto.Event;
import br.com.orchestrator.application.exception.ValidationException;
import br.com.orchestrator.core.enums.ETopics;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Arrays;

import static br.com.orchestrator.application.saga.SagaHandler.*;
import static java.lang.String.format;
import static org.springframework.util.ObjectUtils.isEmpty;

@Slf4j
@Component
@AllArgsConstructor
public class SagaExecutionController {

    private static final String SAGA_LOG_ID = "USER ID: %s | TRANSACTION ID %s | EVENT ID %s";

    public ETopics getNextTopic(Event event) {
        if (isEmpty(event.getSource()) || isEmpty(event.getStatus())) {
            throw new ValidationException("Source and status must be informed.");
        }
        var topic = findTopicBySourceAndStatus(event);
        logCurrentSaga(event, topic);
        return topic;
    }

    private ETopics findTopicBySourceAndStatus(Event event) {
        return (ETopics) (Arrays.stream(SAGA_HANDLER)
            .filter(row -&amp;gt; isEventSourceAndStatusValid(event, row))
            .map(i -&amp;gt; i[TOPIC_INDEX])
            .findFirst()
            .orElseThrow(() -&amp;gt; new ValidationException("Topic not found!")));
    }

    private boolean isEventSourceAndStatusValid(Event event,
                                                Object[] row) {
        var source = row[EVENT_SOURCE_INDEX];
        var status = row[SAGA_STATUS_INDEX];
        return source.equals(event.getSource()) &amp;amp;&amp;amp; status.equals(event.getStatus());
    }

    private void logCurrentSaga(Event event, ETopics topic) {
        var sagaId = createSagaId(event);
        var source = event.getSource();
        switch (event.getStatus()) {
            case SUCCESS -&amp;gt; log.info("### CURRENT SAGA: {} | SUCCESS | NEXT TOPIC {} | {}",
                source, topic, sagaId);
            case ROLLBACK_PENDING -&amp;gt; log.info("### CURRENT SAGA: {} | SENDING TO ROLLBACK CURRENT SERVICE | NEXT TOPIC {} | {}",
                source, topic, sagaId);
            case FAIL -&amp;gt; log.info("### CURRENT SAGA: {} | SENDING TO ROLLBACK PREVIOUS SERVICE | NEXT TOPIC {} | {}",
                source, topic, sagaId);
        }
    }

    private String createSagaId(Event event) {
        return format(SAGA_LOG_ID,
            event.getPayload().getId(), event.getTransactionId(), event.getId());
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O codigo dessa aplicação ficara assim.&lt;br&gt;
&lt;a href="https://github.com/2020nani/saga_pattern/tree/master/orchestrator" rel="noopener noreferrer"&gt;API Saga Pattern - Orchestrator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/2020nani/saga-pattern-in-microservices-parte-4-g82"&gt;Parte 4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/2020nani" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>java</category>
      <category>eventdriven</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Saga Pattern in Microservices - Parte 4</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Tue, 12 Mar 2024 02:09:56 +0000</pubDate>
      <link>https://forem.com/2020nani/saga-pattern-in-microservices-parte-4-g82</link>
      <guid>https://forem.com/2020nani/saga-pattern-in-microservices-parte-4-g82</guid>
      <description>&lt;p&gt;Ferramentas necessárias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/java/technologies/downloads/" rel="noopener noreferrer"&gt;Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/windows/install/#:~:text=%20Install%20Docker%20Desktop%20on%20Windows%20%F0%9F%94%97%20,to%20authorize%20the%20installer%20and%20proceed...%20More%20" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;IDE de sua preferencia&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/2020nani/saga-pattern-in-microservices-parte-2-122e"&gt;Configuração do ambiente conforme a parte 2 do artigo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos criar três aplicações com os nomes de &lt;strong&gt;Adress-Service(Java)&lt;/strong&gt;, &lt;strong&gt;UserRegistration&lt;/strong&gt; e &lt;strong&gt;Validated-Document-Service(Kotlin)&lt;/strong&gt; no &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;spring starter&lt;/a&gt; com as seguintes dependências necessárias&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqo02g8thtvztdr2r81lb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqo02g8thtvztdr2r81lb.png" alt="Image description" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Essas apis receberam o payload via tópico kafka conforme listener do kafka configurado nelas e integrarão com outro service nosso construído em Nodejs/Graph que nos retornara os dados que precisamos, a &lt;strong&gt;AdressService&lt;/strong&gt; ira buscar e adicionar o endereço do usuário no payload enquanto que a &lt;strong&gt;Validated-Document-Service&lt;/strong&gt; ira validar se o documento do usuário não esta guardado dentro de uma lista de documentos bloqueados.&lt;br&gt;
Feito isso a mensagem e enviada para o topico a api &lt;strong&gt;User-Registration&lt;/strong&gt; que com o payload com os dados completo do usuário armazeno-os no banco de dados postgres.&lt;br&gt;
Abaixo deixarei o link para acessar o código das aplicações no github.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/2020nani/saga_pattern/tree/master/adress-service" rel="noopener noreferrer"&gt;API Saga Pattern - AdressService(Java)&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/2020nani/saga_pattern/tree/master/validated-document-service" rel="noopener noreferrer"&gt;API Saga Pattern - ValidatedService(Kotlin)&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/2020nani/saga_pattern/tree/master/user-registration" rel="noopener noreferrer"&gt;API Saga Pattern - UserRegistration(Kotlin)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Essas apis integrarão com outra api nossa construído em Nodejs/Graphql que nos fornecerão os dados de endereço do usuário e a lista de documentos bloqueados.&lt;br&gt;
&lt;a href="https://github.com/2020nani/saga_pattern/tree/master/data-service" rel="noopener noreferrer"&gt;API Saga Pattern - DataService(Nodejs/Graphql)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos testar agora a saga para cadastrar um usuário e validar o comportamento dos microservices se integrando entre si através da orientação a eventos.&lt;br&gt;
Acesse nossa primeira api atraves do swagger por este &lt;a href="http://localhost:3000/swagger-ui/index.html" rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No endpoint /user vamos fazer a requisição inicial passando nome, cpf valido e o cep do usuario.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxowahlrdpajm5o244sc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxowahlrdpajm5o244sc.png" alt="Image description" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Logs do fluxo da nossa saga na api orquestradora&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3u8fsyuqs0l3n0j67gbp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3u8fsyuqs0l3n0j67gbp.png" alt="Image description" width="800" height="364"&gt;&lt;/a&gt;&lt;br&gt;
Vamos pegar o transactionId no payload de retorno da requisição que fizemos acima e consultar via endpoint /user/event&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbxuzgbxkfjn6k323bue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbxuzgbxkfjn6k323bue.png" alt="Image description" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9sh29izikrysycm07so.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9sh29izikrysycm07so.png" alt="Image description" width="800" height="379"&gt;&lt;/a&gt;&lt;br&gt;
Isso nos retornara todo o processo que aconteceu na nossa transação com sucesso.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "id": "65efb539e7a8223c9344944f",
  "userId": "65efb538e7a8223c9344944e",
  "transactionId": "1710208312995_b931560b-58d2-4d8a-8351-3abdcf2e7268",
  "payload": {
    "id": "65efb538e7a8223c9344944e",
    "transactionId": "1710208312995_b931560b-58d2-4d8a-8351-3abdcf2e7268",
    "name": "Teste",
    "document": "91549825020",
    "cep": "18608-456",
    "createdAt": "2024-03-12T01:51:52.996"
  },
  "source": "ORCHESTRATOR",
  "status": "SUCCESS",
  "eventHistory": [
    {
      "source": "ORCHESTRATOR",
      "status": "SUCCESS",
      "message": "Saga started!",
      "createdAt": "2024-03-11T22:51:53.032"
    },
    {
      "source": "ADRESS_VALIDATION_SERVICE",
      "status": "SUCCESS",
      "message": "adress was validated successfully! {}",
      "createdAt": "2024-03-11T22:51:53.188"
    },
    {
      "source": "VALIDATED_SERVICE",
      "status": "SUCCESS",
      "message": "Document was validated successfully! {}",
      "createdAt": "2024-03-11T22:51:53.287"
    },
    {
      "source": "REGISTRATION_SERVICE",
      "status": "SUCCESS",
      "message": "user persisted with successfully! {}",
      "createdAt": "2024-03-11T22:51:54.476"
    },
    {
      "source": "ORCHESTRATOR",
      "status": "SUCCESS",
      "message": "Saga finished successfully!",
      "createdAt": "2024-03-11T22:51:54.741"
    }
  ],
  "createdAt": "2024-03-12T01:51:54.999"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos repetir esse processo porem passando um cpf que contem na lista de documentos bloqueados&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91crggwxr4zqq5salus7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91crggwxr4zqq5salus7.png" alt="Image description" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Veja pelos logs da nossa api orquestradora que ocorreu um erro na api de validated-document e que a partir dai foi enviada, via kafka, uma mensagem de rollback para api validated-service e adress-service para que ambas apaguem o que já haviam feito nessa transação&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwytfil2d6fsuq3j62yi5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwytfil2d6fsuq3j62yi5.png" alt="Image description" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos pegar o transactionId no payload de retorno da requisição que fizemos acima e consultar via endpoint /user/event o retorno dessa saga.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "id": "65efb708e7a8223c93449451",
  "userId": "65efb708e7a8223c93449450",
  "transactionId": "1710208776483_c53613c4-4779-4402-9702-77f9e376c2a5",
  "payload": {
    "id": "65efb708e7a8223c93449450",
    "transactionId": "1710208776483_c53613c4-4779-4402-9702-77f9e376c2a5",
    "name": "Teste",
    "document": "86060970044",
    "cep": "18608-490",
    "createdAt": "2024-03-12T01:59:36.483"
  },
  "source": "ORCHESTRATOR",
  "status": "FAIL",
  "eventHistory": [
    {
      "source": "ORCHESTRATOR",
      "status": "SUCCESS",
      "message": "Saga started!",
      "createdAt": "2024-03-11T22:59:36.507"
    },
    {
      "source": "ADRESS_VALIDATION_SERVICE",
      "status": "SUCCESS",
      "message": "adress was validated successfully! {}",
      "createdAt": "2024-03-11T22:59:36.579"
    },
    {
      "source": "VALIDATED_SERVICE",
      "status": "ROLLBACK_PENDING",
      "message": "Fail to validate adress: Document {} has some pendency in our base",
      "createdAt": "2024-03-11T22:59:36.635"
    },
    {
      "source": "VALIDATED_SERVICE",
      "status": "FAIL",
      "message": "Rollback executed on validated-document validation!",
      "createdAt": "2024-03-11T22:59:36.733"
    },
    {
      "source": "ADRESS_VALIDATION_SERVICE",
      "status": "FAIL",
      "message": "Rollback executed on adress validation!",
      "createdAt": "2024-03-11T22:59:36.876"
    },
    {
      "source": "ORCHESTRATOR",
      "status": "FAIL",
      "message": "Saga finished with errors!",
      "createdAt": "2024-03-11T22:59:36.933"
    }
  ],
  "createdAt": "2024-03-12T01:59:36.955"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vlw pessoal por visualizarem mais esse artigo e deixo aqui minhas redes sociais para quem quiser me adicionar e trocar uma ideia sobre desenvolvimento de softwares para aprendermos juntos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/2020nani" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>graphql</category>
      <category>postgres</category>
      <category>node</category>
    </item>
    <item>
      <title>Desacoplando frontend com module-federation Angular - Parte 2</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Sat, 03 Jun 2023 13:29:40 +0000</pubDate>
      <link>https://forem.com/2020nani/desacoplando-frontend-com-module-federation-angular-parte-2-4le1</link>
      <guid>https://forem.com/2020nani/desacoplando-frontend-com-module-federation-angular-parte-2-4le1</guid>
      <description>&lt;p&gt;Ferramentas necessárias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://angular.io/cli" rel="noopener noreferrer"&gt;Angular cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/download" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/2020nani/desacoplando-frontend-com-module-federation-angular-parte-1-5enm"&gt;Artigo Parte 1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos continuar a implementação da nossa aplicação, abra o terminal na pasta &lt;code&gt;mfe-1&lt;/code&gt; e rode o seguinte comando para instalar a lib do module-federation e configurar o webpack da aplicação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng add @angular-architects/module-federation --project mfe-1 --port 4201 --type remote --skip-confirmation 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logo apos digite code . para abrir o vscode e vermos o codigo de nossa aplicação.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nr4vx5ocngij754ml40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nr4vx5ocngij754ml40.png" alt="Image description" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mude os seguintes arquivos dentro do código.&lt;br&gt;
&lt;code&gt;app.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1&amp;gt;Aqui e o Mfe-1&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { CommonModule } from '@angular/common';
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  declarations: [AppComponent],
  imports: [CommonModule, AppRoutingModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Detalhe que veja que passamos no import desse module o &lt;strong&gt;CommomModule&lt;/strong&gt; pois esse module da aplicação e um module filho da nossa aplicação host.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app-routing.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';

const MFE1_ROUTES: Routes = [
  {
    path: '',
    component: AppComponent,
  },
];

@NgModule({
  imports: [RouterModule.forChild(MFE1_ROUTES)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Outro detalhe aqui tambem, passamos no nosso RouterModule como &lt;strong&gt;RouterModule.forChild&lt;/strong&gt; pois essa rota será filha da rota root da nossa aplicação host.&lt;br&gt;
&lt;code&gt;webpack.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {
  shareAll,
  withModuleFederationPlugin,
} = require("@angular-architects/module-federation/webpack");

module.exports = withModuleFederationPlugin({
  name: "mfe-1",

  exposes: {
    "./Module": "./src/app/app.module.ts",
  },

  shared: {
    ...shareAll({
      singleton: true,
      strictVersion: true,
      requiredVersion: "auto",
 },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feito isso digite o seguinte comando no terminal para rodar a aplicação &lt;code&gt;npm run start&lt;/code&gt; e acesse nosso host e clique no link &lt;code&gt;Acesse Mfe-1&lt;/code&gt; e podemos ver nosso mfe sendo acessado via host.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2ysvc2fto0untorogwm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2ysvc2fto0untorogwm.png" alt="Image description" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Siga os mesmos passos dentro da pasta onde foi criado o projeto mfe-2, porem para instalar a lib do module-federation e configurar o webpack digite o seguinte comando no terminal.&lt;br&gt;
&lt;code&gt;ng add @angular-architects/module-federation --project mfe-2 --port 4202 --type remote --skip-confirmation&lt;/code&gt;&lt;br&gt;
Após isso ajuste os seguintes arquivos para ficar assim.&lt;br&gt;
Mude os seguintes arquivos dentro do código.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1&amp;gt;Aqui e o Mfe-2&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { CommonModule } from '@angular/common';
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  declarations: [AppComponent],
  imports: [CommonModule, AppRoutingModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Detalhe que veja que passamos no import desse module o &lt;strong&gt;CommomModule&lt;/strong&gt; pois esse module da aplicação e um module filho da nossa aplicação host.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app-routing.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';

const MFE2_ROUTES: Routes = [
  {
    path: '',
    component: AppComponent,
  },
];

@NgModule({
  imports: [RouterModule.forChild(MFE2_ROUTES)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Outro detalhe aqui tambem, passamos no nosso RouterModule como &lt;strong&gt;RouterModule.forChild&lt;/strong&gt; pois essa rota será filha da rota root da nossa aplicação host.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;webpack.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {
  shareAll,
  withModuleFederationPlugin,
} = require("@angular-architects/module-federation/webpack");

module.exports = withModuleFederationPlugin({
  name: "mfe-2",

  exposes: {
    "./Module": "./src/app/app.module.ts",
  },

  shared: {
    ...shareAll({
      singleton: true,
      strictVersion: true,
      requiredVersion: "auto",
 },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feito isso digite o seguinte comando no terminal para rodar a aplicação &lt;code&gt;npm run start&lt;/code&gt; e acesse nosso host e clique no link &lt;code&gt;Acesse Mfe-2&lt;/code&gt; e podemos ver nosso mfe sendo acessado via host.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd83yqcm641wdmgu6f9sr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd83yqcm641wdmgu6f9sr.png" alt="Image description" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos agora a cada clique no link acessar remotamente nossas aplicações &lt;code&gt;mfe-1&lt;/code&gt; e &lt;code&gt;mfe-2&lt;/code&gt; o que nos possibilita, por exemplo fazer alterações e subir um deploy na aplicação mfe-1 sem que isso interfira na aplicação mfe-2 pois e um outro codigo totalmente independente.&lt;/p&gt;

&lt;p&gt;E isso ai devs mas um conhecimento passado, deixarei no meu &lt;a href="https://github.com/2020nani/mfe-article" rel="noopener noreferrer"&gt;github&lt;/a&gt; para quem quiser acessar o projeto&lt;br&gt;
Deixo aqui também meu &lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt; e &lt;a href="https://www.instagram.com/her_almeida40/" rel="noopener noreferrer"&gt;instagram&lt;/a&gt; para quem quiser me adicionar, grato a todos que leram este artigo e bora aprender galera.&lt;br&gt;
*Observação: * Como um plus numa terceira parte irei me desafiar a construir um componente angular utilizando module-federation para servir como lib de algum componente para se usar nas aplicações, exemplo um botão padrão para usar em todas as aplicações, e compartilho aqui numa terceira parte.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>modulefederation</category>
      <category>javascript</category>
      <category>microfrontend</category>
    </item>
    <item>
      <title>Desacoplando frontend com module-federation Angular - Parte 1</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Sat, 03 Jun 2023 13:28:03 +0000</pubDate>
      <link>https://forem.com/2020nani/desacoplando-frontend-com-module-federation-angular-parte-1-5enm</link>
      <guid>https://forem.com/2020nani/desacoplando-frontend-com-module-federation-angular-parte-1-5enm</guid>
      <description>&lt;p&gt;Ferramentas necessárias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://angular.io/cli" rel="noopener noreferrer"&gt;Angular cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/download" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O conceito Micro frontends e permitir aos desenvolvedores a flexibilidade de solicitar remotamente um módulo na rede e inicializar esse módulo em sua aplicação, podendo assim separar responsabilidades de uma aplicação frontend em varias aplicações. &lt;br&gt;
Os microfrontends podem ajudar os desenvolvedores a focar nos requisitos funcionais e nas necessidades de negócios.&lt;br&gt;
Os microfrontends podem aumentar o reuso de código na empresa e simplificar o processo de desenvolvimento, deixando a aplicação com pouco acoplamento pois possibilita a cada aplicação dentro do ambiente ter sua própria versão, seu próprio framework, seu próprio deploy e no final para o usuário isso se reflete a 1 aplicação.&lt;br&gt;
Construir uma plataforma para microfrontends é muito semelhante ao de uma plataforma para microservices.&lt;/p&gt;

&lt;p&gt;Nesse artigo vamos construir um microfrontend utilizando o framework &lt;code&gt;Angular&lt;/code&gt; onde através da aplicação main iremos acessar outras duas aplicações remotas utilizando a ferramenta &lt;code&gt;module-federation&lt;/code&gt;, dito isso bora codar.&lt;/p&gt;

&lt;p&gt;Crie uma pasta &lt;code&gt;artigoMFE&lt;/code&gt; e atraves do terminal de sua maquina entre dentro desta pasta vamos criar 3 aplicações angular através do comando &lt;code&gt;ng new mfe-host&lt;/code&gt;, após &lt;code&gt;ng new mfe-1&lt;/code&gt; e após &lt;code&gt;ng new mfe-2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdy24rf9m5sp9kqjp4n0t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdy24rf9m5sp9kqjp4n0t.png" alt="Image description" width="546" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ewjk12ynq2e3kc0t8jx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ewjk12ynq2e3kc0t8jx.png" alt="Image description" width="534" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fziw5odv2j4bwy31wkiyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fziw5odv2j4bwy31wkiyh.png" alt="Image description" width="558" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Temos agora 3 projetos angular criado dentro da pasta &lt;code&gt;artigoMFE&lt;/code&gt;, digite o seguinte comando &lt;code&gt;cd mfe-host&lt;/code&gt; no terminal na raiz da pasta &lt;code&gt;artigoMFE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasi0dvgq0aus8qsyknk8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasi0dvgq0aus8qsyknk8.png" alt="Image description" width="597" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dentro da pasta &lt;code&gt;mfe-host&lt;/code&gt; digite o seguinte comando para abrir a aplicação dentro do VSCODE &lt;code&gt;code .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feuc9mff16hd9e4nkelzb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feuc9mff16hd9e4nkelzb.png" alt="Image description" width="758" height="61"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ainda na pasta &lt;code&gt;mfe-host&lt;/code&gt; digite o seguinte comando&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng add @angular-architects/module-federation --project mfe-host --port 4200 --type host --skip-confirmation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse comando ira instalar a biblioteca do module-federation em nossa aplicação e configurar o arquivo webpack da nossa aplicação.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmrp3j5fwvw2d0iyu74j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmrp3j5fwvw2d0iyu74j.png" alt="Image description" width="537" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5n7hfevkpu974ycp7kn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5n7hfevkpu974ycp7kn.png" alt="Image description" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feito isso crie um arquivo app-routing.module.ts na sua aplicação de maneira que a estrutura fique o seguinte.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74j5zoiveenc4ov6s8be.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74j5zoiveenc4ov6s8be.png" alt="Image description" width="571" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E altere os seguintes arquivos para ficar igual os arquivos abaixo.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app-routing.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { loadRemoteModule } from '@angular-architects/module-federation';

const APP_ROUTES: Routes = [
  {
    path: 'mfe-1',
    loadChildren: () =&amp;gt;
      loadRemoteModule({
        type: 'module',
        remoteEntry: 'http://localhost:4201/remoteEntry.js',
        exposedModule: './Module',
      }).then((m) =&amp;gt; m.AppModule),
  },
  {
    path: 'mfe-2',
    loadChildren: () =&amp;gt;
      loadRemoteModule({
        type: 'module',
        remoteEntry: 'http://localhost:4202/remoteEntry.js',
        exposedModule: './Module',
      }).then((m) =&amp;gt; m.AppModule),
  },
];

@NgModule({
  imports: [RouterModule.forRoot(APP_ROUTES)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module.';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AppRoutingModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;a routerLink="/mfe-1"&amp;gt;Acesse MFE-1&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;a routerLink="/mfe-2"&amp;gt;Acesse MFE-2&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
  &amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feito isso digite o seguinte comando no terminal &lt;code&gt;npm run start&lt;/code&gt; para rodar a aplicação e clique no link abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmf4mzqtddbaybk29z6yh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmf4mzqtddbaybk29z6yh.png" alt="Image description" width="158" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos ver na ferramenta da google na aba de &lt;code&gt;Redes&lt;/code&gt; que nosso host tentou se comunicar a nossa aplicação remota &lt;code&gt;mfe-1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy532tffcdnbcnn5pkc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy532tffcdnbcnn5pkc8.png" alt="Image description" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No próximo &lt;a href="https://dev.to/2020nani/desacoplando-frontend-com-module-federation-angular-parte-2-4le1"&gt;artigo&lt;/a&gt; vamos implementar as aplicações remotas &lt;code&gt;mfe-1&lt;/code&gt; e &lt;code&gt;mfe-2&lt;/code&gt; para concluir nosso artigo, ate o próximo artigo&lt;/p&gt;

&lt;p&gt;Deixo aqui meu &lt;a href="https://www.linkedin.com/in/nanialmeida/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt; e &lt;a href="https://www.instagram.com/her_almeida40/" rel="noopener noreferrer"&gt;instagram&lt;/a&gt; para quem quiser me adicionar&lt;/p&gt;

</description>
      <category>modulefederation</category>
      <category>javascript</category>
      <category>microfrontend</category>
      <category>angular</category>
    </item>
    <item>
      <title>Armazenando imagens protocolo SFTP com java - Springboot</title>
      <dc:creator>Hernani Almeida</dc:creator>
      <pubDate>Sat, 02 Jul 2022 02:22:16 +0000</pubDate>
      <link>https://forem.com/2020nani/armazenando-imagens-protocolo-sftp-com-java-springboot-37ia</link>
      <guid>https://forem.com/2020nani/armazenando-imagens-protocolo-sftp-com-java-springboot-37ia</guid>
      <description>&lt;p&gt;Ferramentas necessárias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/java/technologies/downloads/" rel="noopener noreferrer"&gt;Java SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/windows/install/#:~:text=%20Install%20Docker%20Desktop%20on%20Windows%20%F0%9F%94%97%20,to%20authorize%20the%20installer%20and%20proceed...%20More%20" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postman.com/downloads/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://filezilla-project.org/download.php?type=client" rel="noopener noreferrer"&gt;Filezila Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ide de sua preferencia&lt;/li&gt;
&lt;li&gt;Criar uma aplicação Springboot pelo &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;Spring Initializr&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neste post vamos abordar o armazenamento de imagens utilizando o protocolo &lt;code&gt;SFTP&lt;/code&gt;, vamos subir um servidor SFTP utilizando a chave SSH &lt;strong&gt;Importante mecanismo de autenticação que asseguram a proteção de dados e informações&lt;/strong&gt; dentro de um container docker e enviar imagens para este servidor através de uma api rest java.&lt;br&gt;
Crie uma aplicação Springboot, como nosso objetivo e apenas demonstrar o envio de imagens via SFTP não vamos nos preocupar com padrões arquiteturais.&lt;br&gt;
Crie uma pasta na raiz do projeto com o nome de sua escolha e dentro desta pasta um arquivo &lt;code&gt;dockerfile&lt;/code&gt; e cole dentro dele os seguintes comandos.&lt;br&gt;
&lt;code&gt;dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ubuntu:latest

RUN mkdir -p /var/run/sshd

RUN apt update &amp;amp;&amp;amp; \
    apt install -y openjdk-8-jdk &amp;amp;&amp;amp; \
    apt install -y openssh-server

RUN useradd -rm -d /home/remote_user -s /bin/bash remote_user &amp;amp;&amp;amp; \
    echo remote_user:password1234 | chpasswd

RUN mkdir /home/remote_user/.ssh &amp;amp;&amp;amp; \
    chmod 700 /home/remote_user/.ssh

COPY id_rsa.pub /home/remote_user/.ssh/authorized_keys

RUN chown remote_user:remote_user -R /home/remote_user/.ssh &amp;amp;&amp;amp; \
    chmod 600 /home/remote_user/.ssh/authorized_keys

CMD [ "/usr/sbin/sshd" , "-D"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E um arquivo com o nome docker-compose.yml com a seguinte configuração no mesmo&lt;br&gt;
&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.7'
services:
  app:
    container_name: sftp
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - '58898:22'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feito isso, via cmd, entre na pasta onde esta localizado os arquivos dockerfile e docker-compose.yml e rode o seguinte comando para criar uma chave de segurança &lt;code&gt;SSH&lt;/code&gt;&lt;br&gt;
&lt;code&gt;ssh-keygen -t rsa -m PEM -f id_rsa&lt;/code&gt; apos rodar o comando sera pedido para você escrever uma frase e depois confirmar esta frase para gerar o ssh.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fulow5ytlgmqjqsnw0mcl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fulow5ytlgmqjqsnw0mcl.png" alt="ssh" width="800" height="403"&gt;&lt;/a&gt;&lt;br&gt;
Ainda dentro desta pasta via cmd rode o comando &lt;code&gt;docker compose up -d&lt;/code&gt; e será criado nosso servidor dentro de um container docker.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu4hei51t6rosuh7k8eh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu4hei51t6rosuh7k8eh.png" alt="Image description" width="800" height="114"&gt;&lt;/a&gt;&lt;br&gt;
Já podemos acessar nosso servidor através do &lt;code&gt;Filezila CLient&lt;/code&gt;&lt;br&gt;
*Obs: Password tem que ser o mesmo especificado no dockerfile, neste caso password1234&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz91fdcol2md6jap6649b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz91fdcol2md6jap6649b.png" alt="Image description" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos utilizar a lib &lt;code&gt;JSch&lt;/code&gt; para armazenar a imagem recebida no servidor &lt;code&gt;SFTP&lt;/code&gt;, para isto adicione a seguinte dependência em seu projeto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- https://mavenlibs.com/maven/dependency/org.lucee/jsch --&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.lucee&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;jsch&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;0.1.55&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crie uma classe de configuração do JSch para acessar nosso servidor junto com os métodos necessarios.&lt;br&gt;
&lt;code&gt;SftpConfig&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.sftp;

import com.jcraft.jsch.*;

import java.io.InputStream;


public class SftpConfig {

    /* criar canal para acessar servidor sftp */
    public static ChannelSftp setupJsch() throws JSchException {

        Session jschSession = null;

        Channel sftp = null;

        JSch jsch = new JSch();
        JSch.setConfig("StrictHostKeyChecking", "no");
        //jsch.setKnownHosts("/home/remote_user/.ssh/known_hosts");
        jschSession = jsch.getSession("remote_user", "localhost", 58898);

        // authenticate using private key
        // jsch.addIdentity(&amp;lt;destino da chave ssh&amp;gt;);

        // authenticate using password
        jschSession.setPassword("password1234");

        // 10 seconds session timeout
        jschSession.connect(100000);

        sftp = jschSession.openChannel("sftp");

        return  (ChannelSftp) sftp;

    }

    /* armazenar imagem servidor SFTP */
    public static void salvarImagemSftp(InputStream storedImage, String originalFilename) throws SftpException, JSchException {

        Session jschSession = null;

        Channel sftp = null;
        // transfer file from local to remote server
        ChannelSftp channelSftp = setupJsch();
        channelSftp.connect(50000);

        channelSftp.put(storedImage,channelSftp.getHome() + "/" + originalFilename );

        channelSftp.exit();

        System.out.println("Done");

    }

    /* buscar imagem servidor sftp e transferir para pasta local */
    public static void buscarImagemSftp(String originalFilename) throws SftpException, JSchException {

        Session jschSession = null;

        Channel sftp = null;
        // transfer file from local to remote server
        ChannelSftp channelSftp = setupJsch();
        channelSftp.connect(50000);

        channelSftp.get(channelSftp.getHome() + "/" + originalFilename, "C:\\Users\\Dell\\Desktop\\testeImagem" );

        channelSftp.exit();

        System.out.println("Done");

    }

    /* deletar imagem servidor sftp */
    public static void deletarImagemSftp(String nomeImagem) throws SftpException, JSchException {

        Session jschSession = null;

        Channel sftp = null;
        // transfer file from local to remote server
        ChannelSftp channelSftp = setupJsch();
        channelSftp.connect(50000);

        channelSftp.rm(channelSftp.getHome() + "/" + nomeImagem);

        channelSftp.exit();

        System.out.println("Done");

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos criar uma classe &lt;code&gt;controller&lt;/code&gt; para testarmos o envio de imagens para nosso servidor &lt;code&gt;SFTP&lt;/code&gt; e uma classe &lt;code&gt;model&lt;/code&gt; para mapear a imagem que vamos receber pelo input via controller.&lt;br&gt;
&lt;code&gt;Imagem&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.springframework.web.multipart.MultipartFile;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Imagem {

    private MultipartFile file;
}
```


`ImagemController`


```
package com.example.sftp;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.io.InputStream;

@RestController
public class ImagemController {

    @PostMapping("/imagem")
    public void armazenarImagemApenasSftp(Imagem imagem) throws IOException {
        InputStream image = imagem.getFile().getInputStream();

        try{
            SftpConfig.salvarImagemSftp(image, imagem.getFile().getOriginalFilename() );
        }catch (Exception e){
            System.out.println(e.getMessage());
            throw new IOException("Erro ao salvar imagem no cdn");
        }
    }

    @GetMapping("/imagem")
    public void buscarImagemSftp() throws IOException {

        try{
            SftpConfig.buscarImagemSftp("tcc.png");
        }catch (Exception e){
            System.out.println(e.getMessage());
            throw new IOException("Erro ao salvar imagem no cdn");
        }
    }

    @DeleteMapping("/imagem")
    public void deletarImagemSftp() throws IOException {

        try{
            SftpConfig.deletarImagemSftp("tcc.png");
        }catch (Exception e){
            System.out.println(e.getMessage());
            throw new IOException("Erro ao salvar imagem no cdn");
        }
    }
}
```



Vamos testar nossa aplicação via postman.

**requisicao para salvar imagem**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fn561t1ghl208t8ud61t.png)

**imagem salva em nosso servidor**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpyej4732rthm2qqoz9q.png)

**requisicao para buscar imagem servidor SFTP e tranferir pasta local**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ku0cmt2ouqe6d5uwezh0.png)


**imagem salva na pasta passada como path**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akeaxhfmdgxbdv8afnma.png)

**requisicao para deletar imagem servidor SFTP**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pu3gdxicmwyr8t0igmcr.png)

**imagem deletada do servidor SFTP**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jnc1ofj88ixxyhegacn2.png)

E isso ai devs mas um conhecimento passado, deixarei no meu [github](https://github.com/2020nani/sftp-springboot) para quem quiser acessar o projeto
Deixo aqui também meu [linkedin](https://www.linkedin.com/in/nanialmeida/) para quem quiser me adicionar, grato a todos que leram este artigo e bora aprender galera.







&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>sftp</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
