<?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>DEV Community: Jean Jacques Barros</title>
    <description>The latest articles on DEV Community by Jean Jacques Barros (@jjeanjacques10).</description>
    <link>https://dev.to/jjeanjacques10</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%2F233682%2Fb2205e36-5775-4df9-9fa1-685122aaf5fc.jpg</url>
      <title>DEV Community: Jean Jacques Barros</title>
      <link>https://dev.to/jjeanjacques10</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jjeanjacques10"/>
    <language>en</language>
    <item>
      <title>Descomplicando a Configuração de Producers e Consumers com Kafka</title>
      <dc:creator>Jean Jacques Barros</dc:creator>
      <pubDate>Wed, 08 May 2024 11:00:00 +0000</pubDate>
      <link>https://dev.to/jjeanjacques10/descomplicando-a-configuracao-de-producers-e-consumers-com-kafka-nj8</link>
      <guid>https://dev.to/jjeanjacques10/descomplicando-a-configuracao-de-producers-e-consumers-com-kafka-nj8</guid>
      <description>&lt;p&gt;Entendendo como configurar Producers e Consumers com Kafka de forma simples e descomplicada visando o que faz sentido para os seus projetos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdck3gtbmwpz9vbmkxevd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdck3gtbmwpz9vbmkxevd.png" alt="Exemplo da arquitetura utilizada no projeto modelo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Em algum momento pode ser que apareça uma tarefa em seu trabalho onde precise criar uma integração com o Kafka, quando você começa a desenvolver sua aplicação, criar sua lógica de negócio e então se de para com o arquivo cheio de parâmetros surgindo a pergunta: "Quais configurações devo fazer aqui que fazem sentido para o meu cenário?".&lt;/p&gt;

&lt;p&gt;É comum esse tiopo de questionamento, kafka como uma ferramenta robusta nos oferece diversos recursos e configurações, e é importante entender como cada uma delas pode ser utilizada para atender as necessidades do seu projeto. Vamos entender um pouco mais sobre o Kafka e trazer exemplos práticos de como você pode preencher esses parâmetros da melhor forma sem precisar "chutar" o que pode ou não ser o melhor para o seu cenário.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é o Kafka
&lt;/h2&gt;

&lt;p&gt;Kafka é uma plataforma de streaming distribuída desenvolvida pelo LinkedIn em 2011 e posteriormente, foi doada para a &lt;a href="https://www.apache.org/" rel="noopener noreferrer"&gt;Apache Software Foundation&lt;/a&gt;, que hoje cuida da ferramenta.  Nasceu da necessidade de lidar com fluxos de dados em tempo real de forma escalável, durável e tolerante a falhas, tornando-se uma peça fundamental em arquiteturas modernas de processamento de dados com a capacidade de processar trilhões de mensagens por dia. Em exemplos reais, Kafka pode ser utilizado para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Streaming de Dados em tempo real&lt;/li&gt;
&lt;li&gt;Monitoramento e Alertas&lt;/li&gt;
&lt;li&gt;Processamento de Big Data&lt;/li&gt;
&lt;li&gt;Integração entre Microsserviços&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tópicos
&lt;/h3&gt;

&lt;p&gt;Tópicos são utilizados para organizar o fluxo de dados no Kafka, onde os registros são armazenados na sequência em que foram gerados, vamos entrar em detalhes mais a frente de como isso funciona. Por exemplo, uma aplicação de processamento de pagamentos pode ter tópicos como "transacoes-cartao", "transacoes-boletos" e "transacoes-pix", cada um representando um tipo diferente de transação financeira.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tópicos vs Filas&lt;/strong&gt;: Ao discutir tópicos no Kafka, muitas vezes surge a dúvida sobre a diferença entre eles e as filas. Em uma fila, as mensagens são consumidas sequencialmente por um único consumidor, garantindo o processamento FIFO (First In, First Out). Por outro lado, em um tópico, segue-se o modelo publish/subscribe, onde cada mensagem publicada é consumida por todos os consumidores registrados no tópico. Além disso, são utilizadas técnicas de particionamento para garantir a escalabilidade na leitura das mensagens pelos grupos de consumidores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ffp74he0182o9zgppgrue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ffp74he0182o9zgppgrue.png" alt="Exemplificação da diferença de tópico e filas"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Brokers
&lt;/h3&gt;

&lt;p&gt;Os brokers no Kafka são os servidores encarregados de armazenar e gerenciar os dados dos tópicos. Cada broker é parte de um cluster Kafka e mantém uma cópia dos dados dos tópicos aos quais está atribuído. Eles são escaláveis e podem ser adicionados ou removidos do cluster conforme necessário para aumentar a capacidade ou a disponibilidade do sistema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Partições (&lt;em&gt;Partitions&lt;/em&gt;)
&lt;/h3&gt;

&lt;p&gt;Partições são segmentações que permitem dividir o tópico em frações menores; novas mensagens são adicionadas a uma &lt;em&gt;partition&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Cada partição possui &lt;em&gt;offsets&lt;/em&gt;, que são a identificação da mensagem dentro de uma partição específica, o que ajuda a saber a ordem em que foram recebidas e que devem ser processadas. Serem separadas por &lt;em&gt;offsets&lt;/em&gt; facilita na hora de processar as mensagens, pois é possível configurar para que sejam lidas a partir de um offset específico, como em casos de reprocessamento, regras de negócio que precisem "voltar no tempo" para processar mensagens antigas ou onde queremos apenas ler mensagens de um determinado ponto.&lt;/p&gt;

&lt;p&gt;Utilizar várias partições garante desempenho com grandes cargas de trabalho, aproveitando a replicação e a distribuição de carga entre os brokers. Por exemplo, se um tópico tiver 3 partições e 3 consumidores, cada consumidor lerá de uma partição diferente, garantindo que as mensagens sejam processadas de forma paralela.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fjalwl27aifwj51jcfk5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fjalwl27aifwj51jcfk5t.png" alt="Exemplo de representação para partições"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Avro
&lt;/h2&gt;

&lt;p&gt;O Kafka transfere bytes de um local para outro, Avro é uma solução para garantir o contrato nessa comunicação. Ele é uma ferramenta de serialização/deserialização de dados que define o formato das mensagens que serão enviadas e recebidas. Além disso, ele permite definir esquemas para os dados transferidos, o que auxilia na garantia de que os dados sejam interpretados corretamente pelos consumidores, mesmo quando os esquemas evoluem ao longo do tempo.&lt;/p&gt;

&lt;p&gt;Aqui está um exemplo de um esquema Avro que define um objeto chamado &lt;strong&gt;TransactionItem&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 avro
{
  "type": "record",
  "name": "TransactionItem",
  "namespace": "com.payment",
  "fields": [
    {
      "name": "value",
      "type": "string"
    },
    {
      "name": "origin",
      "type": {
        "type": "record",
        "name": "Origin",
        "fields": [
          {
            "name": "account",
            "type": "string"
          }
        ]
      }
    },
    {
      "name": "createdAt",
      "type": {
        "type": "string"
      }
    }
  ]
}


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Importante lembrar que o Avro não é obrigatório, mas é uma boa prática utilizá-lo para garantir a compatibilidade entre as mensagens enviadas e recebidas.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Além dos exemplos mencionados anteriormente, existem muitas outras aplicações para essa ferramenta. Neste artigo, vamos nos concentrar na comunicação entre &lt;strong&gt;microsserviços&lt;/strong&gt;. Nosso exemplo será um &lt;strong&gt;sistema de pagamentos&lt;/strong&gt; que processa transações PIX e notifica os clientes quando tudo ocorrer com sucesso!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Todos os exemplos apresentados neste artigo podem ser encontrados no seguinte repositório no GitHub: &lt;a href="https://github.com/jjeanjacques10/payment-async-kafka" rel="noopener noreferrer"&gt;jjeanjacques10/payment-async-kafka: This is a payment system that utilizes Kafka technology for asynchronous integration&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Producer
&lt;/h2&gt;

&lt;p&gt;O producer Kafka é responsável por gerar as mensagens e publicá-las nos tópicos. É possível ter diversos producers publicando mensagens em um mesmo tópico, e também é possível ter diversos producers publicando mensagens em tópicos diferentes. Para evitar de perder mensagens, lembre-se de configurar o "acks", que define o número de replicas que devem confirmar o recebimento da mensagem, e também o "retries", que define o número de tentativas que o producer deve realizar para enviar a mensagem.&lt;/p&gt;

&lt;p&gt;Aqui está um exemplo de configuração para um produtor em Spring, onde as principais configurações estão relacionadas à forma como queremos enviar as mensagens.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 yml
spring:
  application.name: pix-processor
  kafka:
    producer:
      bootstrap-servers: localhost:9092
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: io.confluent.kafka.serializers.KafkaAvroSerializer
      compression-type: snappy # Configura o tipo de compressão que deve ser aplicado às mensagens
      acks: all # Configura o acks para "all" para garantir que todas as réplicas confirmem o recebimento da mensagem
      retries: 3 # Configura o número de tentativas que o producer deve realizar para enviar a mensagem
    template:
      default-topic: payment-topic


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

&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuração&lt;/th&gt;
&lt;th&gt;Descrição&lt;/th&gt;
&lt;th&gt;Exemplo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bootstrap-servers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define os endpoints utilizados para se conectar com o cluster Kafka.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bootstrap-servers=kafka1:9092,kafka2:9092&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key-serializer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Classe responsável pela serialização das chaves das mensagens produzidas.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;key-serializer=org.apache.kafka.common.serialization.StringSerializer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;value-serializer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Classe responsável pela serialização dos valores das mensagens produzidas, define o formato que os consumidores devem seguir.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;value-serializer=io.confluent.kafka.serializers.KafkaAvroSerializer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;properties.schema.registry.url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL do registro de esquemas utilizado para registrar e recuperar esquemas.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;schema.registry.url=&amp;lt;http://localhost:8082&amp;gt;&lt;/code&gt; (exemplo para configuração local)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;auto.register.schemas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Indica se os esquemas que ainda não existem devem ser registrados automaticamente no &lt;em&gt;schema registry&lt;/em&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;auto.register.schemas=false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;retries&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Número de tentativas que o produtor deve realizar para enviar a mensagem.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;retries=3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  acks
&lt;/h3&gt;

&lt;p&gt;O "acks" é uma propriedade que define o número de réplicas que devem confirmar o recebimento da mensagem. Existem três valores possíveis para essa propriedade:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0: O produtor não aguarda nenhuma confirmação.&lt;/li&gt;
&lt;li&gt;1: O produtor aguarda a confirmação do líder da partição.&lt;/li&gt;
&lt;li&gt;all: O produtor aguarda a confirmação de todas as réplicas da partição.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  value-serializer
&lt;/h3&gt;

&lt;p&gt;Um dos pontos principais a serem configurados no produtor é o "value-serializer", que define como os valores das mensagens serão serializados. No exemplo acima, o "value-serializer" está configurado como "io.confluent.kafka.serializers.KafkaAvroSerializer", que é um serializador Avro.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;io.confluent.kafka.serializers.KafkaAvroDeserializer&lt;/li&gt;
&lt;li&gt;org.apache.kafka.common.serialization.StringSerializer&lt;/li&gt;
&lt;li&gt;org.apache.kafka.common.serialization.ByteArraySerializer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  compression-type
&lt;/h3&gt;

&lt;p&gt;O "compression-type" é uma propriedade que define o tipo de compressão que deve ser aplicado às mensagens. Existem alguns tipos de compressão disponíveis, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;none: Sem compressão.&lt;/li&gt;
&lt;li&gt;gzip: Compressão GZIP.&lt;/li&gt;
&lt;li&gt;snappy: Compressão Snappy.&lt;/li&gt;
&lt;li&gt;lz4: Compressão LZ4.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Consumer
&lt;/h2&gt;

&lt;p&gt;Os consumidores são aqueles que se conectam aos nossos tópicos para ler as mensagens publicadas pelos produtores. É possível ter diversos consumers conectados a um tópico. Para evitar a leitura de mensagens repetidas, lembre-se de configurar o "group-id", definindo que o novo consumidor faz parte de um grupo único e que realiza o mesmo processo e também realiza o "acknowledging" que remove a mensagem da fila para o grupo definido. É utilizado o &lt;em&gt;offset&lt;/em&gt; para controlar a leitura das mensagens, garantindo que as mensagens sejam lidas apenas uma vez por aquele grupo consumidor (group-id).&lt;/p&gt;

&lt;p&gt;Aqui está um exemplo de configuração para um consumidor em Spring, onde as principais configurações estão relacionadas à forma como queremos receber as mensagens e como elas devem ser tratadas.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 yml
spring:
  application.name: notification
  kafka:
    consumer:
      bootstrap-servers: localhost:9092
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: io.confluent.kafka.serializers.KafkaAvroDeserializer
      group-id: notification-payment-group
      properties:
        auto.offset.reset: earliest # ou 'latest'
    listener:
      ack-mode: MANUAL_IMMEDIATE
    template:
      default-topic: payment-topic
    properties:
      spring.deserializer.key.delegate.class: org.apache.kafka.common.serialization.StringDeserializer
      spring.deserializer.value.delegate.class: io.confluent.kafka.serializers.KafkaAvroDeserializer
      spring.deserializer.value.fail-on-unknown-type: false
      spring.deserializer.value.type: io.confluent.kafka.serializers.subject.RecordNameStrategy
      schema.registry.url: http://localhost:8082
      specific.avro.reader: true # Deserialize to the generated Avro class rather than a GenericRecord type
      auto.register.schemas: false # Whether schemas that do not yet exist should be registered


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

&lt;/div&gt;

&lt;p&gt;Existem tantas configurações que seria difícil descrevê-las todas neste artigo, então vou focar em algumas das propriedades que costumo adicionar em minhas configurações, busque entender o que faz mais sentido para o seu cenário.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuração&lt;/th&gt;
&lt;th&gt;Descrição&lt;/th&gt;
&lt;th&gt;Exemplo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key-deserializer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Classe responsável pela desserialização das chaves das mensagens consumidas.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;key-deserializer=org.apache.kafka.common.serialization.StringDeserializer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;value-deserializer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Classe responsável pela desserialização dos valores das mensagens consumidas.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;value-deserializer=io.confluent.kafka.serializers.KafkaAvroDeserializer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;group-id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Identificador do grupo de consumidores ao qual este consumidor pertence.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;group-id=pix-consumer-group&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;properties.spring.deserializer.key.delegate.class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Classe delegada responsável pela desserialização das chaves das mensagens, utilizada para configurações específicas do Spring.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;spring.deserializer.key.delegate.class=org.apache.kafka.common.serialization.StringDeserializer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;properties.spring.deserializer.value.delegate.class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Classe delegada responsável pela desserialização dos valores das mensagens, utilizada para configurações específicas do Spring.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;spring.deserializer.value.delegate.class=io.confluent.kafka.serializers.KafkaAvroDeserializer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;properties.spring.deserializer.value.fail-on-unknown-type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Indica se deve falhar ao desserializar um tipo desconhecido. Pode auxiliar em cenários onde o esquema é desconhecido.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;spring.deserializer.value.fail-on-unknown-type=false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;properties.spring.deserializer.value.type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Estratégia para obter o nome do registro do esquema para um determinado valor, usado em conjunto com o Schema Registry.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;spring.deserializer.value.type=io.confluent.kafka.serializers.subject.RecordNameStrategy&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;specific.avro.reader&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Indica se a desserialização deve ser feita para a classe Avro gerada específica ou para o tipo GenericRecord.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;specific.avro.reader=true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ack-mode
&lt;/h3&gt;

&lt;p&gt;O "ack-mode" é uma propriedade que define como o consumidor deve confirmar o recebimento da mensagem. Existem três modos de confirmação:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RECORD: Confirmação de registro por registro.&lt;/li&gt;
&lt;li&gt;BATCH: Confirmação de registros em lote.&lt;/li&gt;
&lt;li&gt;MANUAL: Confirmação manual.&lt;/li&gt;
&lt;li&gt;MANUAL_IMMEDIATE: Confirmação manual imediata.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No exemplo abaixo, o "ack-mode" está configurado para MANUAL_IMMEDIATE, ou seja, a confirmação é feita manualmente e imediatamente após o processamento da mensagem.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 java
 try {
    log.info("Consume Kafka message - topic: {}, offset: {}, partition: {}", topic, offset, partition)
    notificationService.process(transactionItem.toTransaction())
} catch  ex: Exception) {
    log.error("Error processing message: {}", ex.message, ex)
} finally {
    ack.acknowledge() // Confirmação manual sendo realizada
}


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  consumer.properties.auto.offset.reset
&lt;/h3&gt;

&lt;p&gt;No Kafka, as configurações "earliest" e "latest" são usadas para determinar onde o consumidor deve começar a consumir mensagens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;earliest&lt;/code&gt;: Quando o deslocamento da partição não está presente (por exemplo, quando o grupo de consumidores está consumindo a partição pela primeira vez), o consumidor começará a consumir a partir do início da partição.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;latest&lt;/code&gt;: Quando o deslocamento da partição não está presente, o consumidor começará a consumir a partir do final da partição, ou seja, ele não consumirá nenhuma mensagem que já esteja na partição no momento em que começou a consumir.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Boas práticas
&lt;/h2&gt;

&lt;p&gt;É uma boa prática adiciona o log do &lt;em&gt;offset&lt;/em&gt; e também a partition que está lendo, isso pode auxiliar na análise futura do problema de ambos os lados, tanto no consumer quanto no producer.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 kotlin
@KafkaListener(topics = ["payment-topic"], containerFactory = "kafkaListenerContainerFactory")
fun consumePayment(
    @Payload transaction: TransactionItem,
    @Header(KafkaHeaders.OFFSET) offset: Long,
    @Header(KafkaHeaders.RECEIVED_PARTITION) partition: Int?,
    @Header(KafkaHeaders.RECEIVED_TOPIC) topic: String?,
    ack: Acknowledgment
) {
    try {
        log.info("Consume Kafka message - topic: {}, offset: {}, partition: {}", topic, offset, partition)
    } catch (
   ex: Exception) {
        log.error("Error processing message: {}", ex.message, ex)
    } finally {
        ack.acknowledge()
    }
}


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fu0fz0p4z1vfaov4jtf97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fu0fz0p4z1vfaov4jtf97.png" alt="Exemplo de itens sendo apresentados nos logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Outro ponto importante é a segurança, em ambientes produtivos é essencial proteger o cluster Kafka. Isso pode incluir autenticação, autorização, SSL/TLS, entre outros. Aqui está um exemplo de configuração para habilitar SSL/TLS:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 yml
spring.kafka.properties.security.protocol: SSL
spring.kafka.properties.ssl.truststore.location: /path/to/truststore
spring.kafka.properties.ssl.truststore.password: truststorePassword
spring.kafka.properties.ssl.keystore.location: /path/to/keystore
spring.kafka.properties.ssl.keystore.password: keystorePassword


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

&lt;/div&gt;

&lt;p&gt;Essas configurações devem ser ajustadas de acordo com as necessidades específicas do ambiente e da política de segurança da organização.&lt;/p&gt;




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

&lt;p&gt;Desde que comecei a aprender sobre mensageria e todas as possíveis aplicações percebi como é um tema vasto. Até focando apenas no Kafka já vemos como não é algo simples, sendo ele uma ferramenta robusta que pode ser utilizada para diversos cenários, desde streaming de dados até integração entre microsserviços. Neste artigo, vimos algumas das principais configurações que pode ser feitas em nossas aplicações, para ir mais além vá mais a fundo na documentação e realize testes dentro de casa.&lt;/p&gt;

&lt;p&gt;A forma que mais aprendi sobre Kafka foi passando por problemas durante o desenvolvimento, e o que mais me auxiliou a resolver esses problemas foi entender melhor como a ferramenta funciona, por isso a introdução de conceitos básicos é essencial para quem está começando.&lt;/p&gt;

&lt;p&gt;Espero que este artigo tenha sido último em sua jornada e que seja um ponto de partida para você começar a explorar cada vez mais o Kafka em seus projetos.&lt;/p&gt;

&lt;p&gt;Fique a vontade para compartilhar tópicos que ache interessantes nos comentários!&lt;/p&gt;




&lt;p&gt;Gostaria de agradecer ao &lt;a href="https://www.linkedin.com/in/gustavo-santos-madeira-611144151/" rel="noopener noreferrer"&gt;Gustavo Santos Madeira&lt;/a&gt; por ter me apresentado estes conceitos sobre o Kafka me incentivado a ir mais a fundo nos estudos.&lt;/p&gt;

&lt;p&gt;Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:&lt;/p&gt;

&lt;p&gt;Linkedin: &lt;a href="https://www.linkedin.com/in/jjean-jacques10/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/jjean-jacques10/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Até a próxima!&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kafka.apache.org/" rel="noopener noreferrer"&gt;https://kafka.apache.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-kafka/api/org/springframework/kafka/listener/package-summary.html" rel="noopener noreferrer"&gt;org.springframework.kafka.listener (Spring for Apache Kafka 3.1.4 API)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/serdes-avro.html" rel="noopener noreferrer"&gt;Avro Schema Serializer and Deserializer for Schema Registry | Confluent Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.lydtechconsulting.com/blog-kafka-schema-registry-demo-part2.html" rel="noopener noreferrer"&gt;Kafka Schema Registry &amp;amp; Avro: Spring Boot Demo (2 of 2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://atitudereflexiva.wordpress.com/2019/03/12/implementacao-de-uma-fila-e-de-um-topico-jms/" rel="noopener noreferrer"&gt;Implementação de uma Fila e de um Tópico JMS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kafka</category>
      <category>java</category>
      <category>eventdriven</category>
      <category>springboot</category>
    </item>
    <item>
      <title>A habilidade mais importante para um desenvolvedor é a comunicação</title>
      <dc:creator>Jean Jacques Barros</dc:creator>
      <pubDate>Tue, 08 Aug 2023 01:21:08 +0000</pubDate>
      <link>https://dev.to/jjeanjacques10/a-habilidade-mais-importante-para-um-desenvolvedor-e-a-comunicacao-7g4</link>
      <guid>https://dev.to/jjeanjacques10/a-habilidade-mais-importante-para-um-desenvolvedor-e-a-comunicacao-7g4</guid>
      <description>&lt;h3&gt;
  
  
  Nem sempre escrever código vai resolver os maiores desafios na carreira de um desenvolvedor
&lt;/h3&gt;

&lt;p&gt;Podemos dizer que 90% dos problemas de uma empresa são causados pela falta de comunicação. Isso em nível de colegas de equipe e até entre áreas inteiras. Então, como podemos melhorar a forma como nos comunicamos e evitar dores de cabeça com problemas simples que acabam crescendo com o tempo? Neste artigo, proponho-me a trazer algumas dicas de como se expressar melhor, transmitir ideias de forma escrita e verbal.&lt;/p&gt;

&lt;p&gt;Muito além das habilidades técnicas, um bom desenvolvedor precisa entender como compartilhar suas soluções e problemas com outras pessoas.&lt;/p&gt;

&lt;h2&gt;
  
  
  O poder de um bom texto…
&lt;/h2&gt;

&lt;p&gt;Você já deve ter participado de uma reunião que poderia ter sido substituída por um e-mail… Frequentemente, ela ocorreu apenas porque acreditavam que seria “impossível” transmitir a ideia por escrito. Posso afirmar que a maioria delas realmente deveria ter sido um texto objetivo. No entanto, para alcançar esse patamar, é necessário compreender previamente como redigir textos de qualidade.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cuidado com erros de digitação&lt;/li&gt;
&lt;li&gt;Conheça seu público algo&lt;/li&gt;
&lt;li&gt;Faça as perguntas certas&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Cuidado com erros de digitação
&lt;/h2&gt;

&lt;p&gt;O primeiro é o mais simples de todos: Evite equívocos de digitação ou de português, você não precisa redigir a redação do ENEM, nem ser formal constantemente. O dilema ocorre quando tais equívocos podem prejudicar a compreensão do leitor, portanto revise sempre que possível antes de clicar em “ENVIAR”. Alguns segundos adicionais não comprometerão sua oportunidade de compartilhar algo, mas substituir “significado” por “sinificado” pode gerar confusão para o leitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conheça seu público
&lt;/h2&gt;

&lt;p&gt;O segundo aspecto está intimamente relacionado à elaboração de palestras; um palestrante competente sempre pesquisa o público-alvo de seu conteúdo, a fim de aprimorar a forma como é apresentado. No texto, devemos ter a mesma preocupação; não é adequado chegar a uma reunião com profissionais de negócios discorrendo sobre conceitos altamente técnicos. Vejamos um exemplo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--USzIYtXw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zqmcgmnljux7663v9zu5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--USzIYtXw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zqmcgmnljux7663v9zu5.png" alt="Abordagem muito técnica trazendo informações desnecessárias para o contexto - Olá, pessoal! Atualizei a lib do DynamoDB, e na tabela info_meios_pagamento não estamos mais utilizando GlobalSecondaryIndexs para consulta." width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A melhor forma de expressar isso seria abstraindo essas informações e focando no que é importante para esse público:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YWsrP4sJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64lhea2q270ugu6ye337.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YWsrP4sJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64lhea2q270ugu6ye337.png" alt="Exemplo de uma interação certa com pessoas sem conhecimento técnico mais aprofundado - Olá, pessoal! Realizei a atualização no aplicativo e com isso melhoramos a pesquisa de meios de pagamento, acredito que nossos clientes já consigam sentir a diferença." width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Faça as perguntas certas
&lt;/h2&gt;

&lt;p&gt;O último ponto é importante independente da senioridade, ele se baseia no “saber perguntar”, um dos sites mais famosos para desenvolvedores é o &lt;a href="https://stackoverflow.com/"&gt;stackoverflow&lt;/a&gt;, nele é crucial descrever de maneira precisa o problema a fim de buscar auxílio de programadores de todo o mundo. Essa prática também ocorre dentro das empresas, quando surge uma dúvida, é necessário enviar uma mensagem no Teams ou no Slack para alguém com maior experiência ou algum profissional de negócios.&lt;/p&gt;

&lt;p&gt;Muitas vezes partimos do pressuposta que as outras pessoas tem o mesmo contexto que nós, que sabem todo o caminho que passamos para chegar até aquele ponto, mas nem sempre (grande parte das vezes) não é este o caso. Quando entrei na área, esse foi um dos melhores conselhos que recebi: “Mostre como você chegou a essa conclusão”. Segue mais um exemplo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Xxmo94w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vocdv2bsrf4xqsv2bjil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Xxmo94w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vocdv2bsrf4xqsv2bjil.png" alt="Exemplo de uma conversa com mais contexto e fácil de ser compreendida por colegas de equipe&amp;lt;br&amp;gt;
Bruce Wayne: Bom dia, tudo bem? Ontem realizei alguns testes na aplicação, mas não consegui conectar ela ao banco de dados (RDS), estou fazendo a tarefa JIRA-X1999 (Link tarefa).&amp;lt;br&amp;gt;
 Consegui configurar as variáveis de ambiente&amp;lt;br&amp;gt;
 &amp;lt;print das variáveis&amp;gt;&amp;lt;br&amp;gt;
 Atualizei as dependências também&amp;lt;br&amp;gt;
 &amp;lt;print do endpoint atualizado&amp;gt;&amp;lt;br&amp;gt;
 Tony: Você já tentou trocar as credenciais da AWS?&amp;lt;br&amp;gt;
  Bruce Wayne: Deu certo, obrigado!" width="800" height="869"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Costumo incluir também imagens, pois isso evita que a pessoa precise navegar por vários links para me auxiliar em algo simples. Caso seu colega possua alguma deficiência visual, adicione o texto, se possível, ou descreva o que anteriormente seria uma imagem. Quanto mais detalhes forem acrescentados, mais valor você está dando ao tempo de seus colegas de equipe.&lt;/p&gt;
&lt;h2&gt;
  
  
  Don’t ask to ask, just ask
&lt;/h2&gt;

&lt;p&gt;Um site que encontrei por acaso e enfatiza bastante esse aspecto é o &lt;a href="https://dontasktoask.com/"&gt;Don’t ask to ask, just ask&lt;/a&gt;. Ele concentra-se nas interações que ocorrem principalmente em fóruns da internet, onde alguém chega com a seguinte pergunta:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cDn8SGSx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0370yfnbqf22q7on2h7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cDn8SGSx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0370yfnbqf22q7on2h7.png" alt="Exemplo de um péssimo pedido de ajuda em fórum, sem detalhamento - Tony: Algum expert em java aqui?" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como mencionado no tópico sobre formular as perguntas adequadas, é necessário sempre trazer o contexto do problema, preferencialmente descrevendo as medidas já tomadas para tentar solucioná-lo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NuOFJC_B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0q74t30zsorkimurffcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NuOFJC_B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0q74t30zsorkimurffcb.png" alt="Exemplo de uma boa abordagem em um fórum, pergunta direta e com detalhes - tony: Como eu implemento uma função em Java para filtrar todos os meios de pagamento com saldo maior que 1.000?" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ao adotar esse método, você obterá uma resposta mais rápida, pois a pessoa não precisará enviar uma nova mensagem perguntando sobre qual tipo de ajuda você precisa. Além de ajudar quem for pesquisar sobre este assunto futuramente, muitas vezes será você mesmo.&lt;/p&gt;

&lt;p&gt;Aqui tem alguns &lt;a href="https://stackoverflow.com/help/how-to-ask"&gt;exemplos do próprio stackoverflow&lt;/a&gt; de como escrever bons questionamentos (tradução livre feita por mim):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bad: C# Problema de matemática
Good: Por que usar float em vez de int me dá resultados diferentes quando todas as minhas entradas são números inteiros?

Bad: [php] dúvida sobre sessões
Good: Como posso redirecionar usuários para páginas diferentes com base em dados de sessão em PHP?

Bad: problemas com if-else no Android
Good: Por que str == “valor” retorna falso quando str é definido como “valor”?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Iniciando uma conversa
&lt;/h2&gt;

&lt;p&gt;Outra questão bastante comum é como iniciar uma conversa. Provavelmente, você já recebeu uma mensagem no chat com um simples “Oi, tudo bem?” que permaneceu ali até que você respondesse com um “Oi” de volta. No entanto, essa troca pode levar tempo, enquanto novas prioridades surgem e você não tem ideia da urgência daquela saudação sem contexto. Não devemos deixar as pessoas em “espera” como em uma ligação telefônica. Seja direto, fornecendo todas as informações necessárias de forma objetiva junto à saudação.&lt;/p&gt;

&lt;p&gt;Não faça isso:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aVtUhfT9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gij31stg0k8yple5qzlx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aVtUhfT9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gij31stg0k8yple5qzlx.png" alt="Exemplo de uma abordagem lenta - Bruce: Oi Alfred: Olá, tudo bem? Bruce: Como rodo o script na minha máquina? Alfred: sh open_batcaverna.sh" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prefira uma abordagem direta:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vqUOsexm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1csofc6g6xyz1tt5rjrl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vqUOsexm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1csofc6g6xyz1tt5rjrl.png" alt="Exemplo de uma abordagem direta - Bruce: Oi, como faço para rodar o script na minha máquina? Alfred: Olá! sh open_batcaverna.sh" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TechWriter
&lt;/h2&gt;

&lt;p&gt;Em nossa área temos diversas vertentes, uma delas é a de TechWriters, que são profissionais especializados em escrever e comunicar informações técnicas de maneira clara e compreensível. Eles criam documentação, manuais de usuário e guias de instruções, ajudando a traduzir conceitos complexos em linguagem acessível. Embora os desenvolvedores não precisem ser TechWriters, é importante que aprendam com esses profissionais a compartilhar informações, garantindo o sucesso e a usabilidade de produtos e de software.&lt;/p&gt;

&lt;p&gt;Se você tiver a oportunidade de trabalhar com estes profissionais peça algumas dicas, com certeza eles lhe ensinarão algo novo, assim como você ensinará a eles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inteligência artificial
&lt;/h2&gt;

&lt;p&gt;Uma ótima ferramenta para lhe ajudar a escrever textos melhores é a Inteligência artificial (IA). Essa tecnologia tem se mostrado cada vez mais promissora na hora de revisar textos. Através de algoritmos avançados, as ferramentas de IA podem identificar erros gramaticais, sugerir correções e até mesmo oferecer alternativas de palavras e frases para melhorar a clareza e a fluidez do texto.&lt;/p&gt;

&lt;p&gt;Algumas das opções mais utilizadas atualmente incluem o &lt;a href="https://chat.openai.com/"&gt;ChatGPT&lt;/a&gt;, &lt;a href="https://clarice.ai/"&gt;Clarice.ai&lt;/a&gt;, &lt;a href="https://www.copy.ai/"&gt;copy.ai&lt;/a&gt; e várias outras.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Se você pretende utilizar essas ferramentas em ambientes corporativos, certifique-se de não violar nenhuma política interna relacionada ao compartilhamento de informações.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UsM0uKpo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7mf12xdnl0nq4cizko6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UsM0uKpo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7mf12xdnl0nq4cizko6.png" alt="Exemplo de como melhorar um texto no ChatGPT" width="554" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Timidez, uma barreira para se expressar
&lt;/h2&gt;

&lt;p&gt;Aqui está outra vantagem de se expressar por texto. Sempre fui uma pessoa muito tímida e busquei por muito tempo não dar minha opinião em conversas no trabalho. Por isso, comecei a escrever. Na escrita, você se expõe menos e tem a chance de revisar mais de uma vez o que será enviado. Apenas tome cuidado para não ficar lapidando um texto até chegar na versão “perfeita”, porque ela não existe. É mais importante você ter uma versão direta com as informações mais importantes do que cobrir todos os cenários.&lt;/p&gt;

&lt;p&gt;Além disso, a comunicação escrita também proporciona uma forma de registro e referência para futuras consultas. Ao expressar ideias por meio de textos, você cria um histórico que pode ser utilizado como base para tomadas de decisão, análises retrospectivas e aprendizados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Algumas dicas extras
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Leia bastante, quem escreve bem é aquele que lê muito&lt;/li&gt;
&lt;li&gt;Pegue feedback com seus colegas de trabalho&lt;/li&gt;
&lt;li&gt;Não tenha medo de expor suas ideias&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Escrevi esse artigo para compartilhar com vocês ações que me auxiliaram muito em minha carreira, ainda estou aprendendo a me expressar melhor a transmitir minha ideias para as pessoas, isso tudo é um processo que só vai melhorar com muita prática, assim como programar!&lt;/p&gt;

&lt;p&gt;Pratique realizando anotações sobre a tarefa que está fazendo, se possível documente seu projeto, vai ajudar você e será uma forma fácil de compartilhar com novas pessoas que entrarem em sua equipe (sem passar horas em reuniões de overview).&lt;/p&gt;

&lt;p&gt;Espero que tenha gostado e sempre que ver alguém fazendo uma pergunta sem muito contexto compartilhe esse artigo com essa pessoa! Vamos melhorar a forma que nos comunicamos e criar um mundo com menos desentendimentos!&lt;/p&gt;




&lt;p&gt;Caso tenha alguma crítica, dúvida ou sugestão, fique à vontade para comentar abaixo ou nos envie uma mensagem:&lt;/p&gt;

&lt;p&gt;Jean Jacques, Backend Software Engineer — &lt;a href="https://www.linkedin.com/in/jjean-jacques10/"&gt;https://www.linkedin.com/in/jjean-jacques10/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Até a próxima!&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dontasktoask.com/"&gt;https://dontasktoask.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nohello.net/en/"&gt;https://nohello.net/en/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://catb.org/%7Eesr/faqs/smart-questions.html"&gt;http://catb.org/~esr/faqs/smart-questions.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/help/how-to-ask"&gt;https://stackoverflow.com/help/how-to-ask&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>documentation</category>
      <category>techwriter</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Como buscar posts no TabNews - Google Hacking</title>
      <dc:creator>Jean Jacques Barros</dc:creator>
      <pubDate>Sun, 04 Dec 2022 22:32:51 +0000</pubDate>
      <link>https://dev.to/jjeanjacques10/como-buscar-posts-no-tabnews-google-hacking-1m95</link>
      <guid>https://dev.to/jjeanjacques10/como-buscar-posts-no-tabnews-google-hacking-1m95</guid>
      <description>&lt;p&gt;Certeza que para quem utiliza a plataforma do TabNews a feature mais desejada é a barra de buscas. Enquanto não temos ela implementada podemos utilizar alguns recursos do Google para encontrar o que desejamos!&lt;/p&gt;

&lt;h2&gt;
  
  
  Google Hacking
&lt;/h2&gt;

&lt;p&gt;Google Hacking é uma coleção de comandos que podem ser utilizados para encontrar o que você deseja. É um termo usado para descrever a arte de explorar técnicas de pesquisa avançada, operadores e algoritmos para localizar brechas de segurança em aplicativos, redes e sistemas da web, mas pode ser utilizado para encontrar qualquer coisa na internet.&lt;/p&gt;

&lt;p&gt;Utilizamos alguns termos e characteres especiais para realizar essas buscas, seguem os principais:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;intext:&lt;/strong&gt; Útil para localizar páginas que contêm certos caracteres ou strings dentro de seu texto. Exemplo: intext:“TabNews”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;filetype:&lt;/strong&gt; Este operador é usado para procurar por arquivos específicos. Exemplo: pdf, txt e etc...&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;site:&lt;/strong&gt; Este operador é usado para procurar em sites específicos. Exemplo: site:tabnews.com.br&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;inurl:&lt;/strong&gt; Este operador é usado para procurar por palavras específicas dentro de uma URL. Exemplo: inurl:“tabnews”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;allintext:&lt;/strong&gt; Este operador é usado para procurar por palavras específicas dentro de um documento. Exemplo: allintext:“TabNews”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;allinurl:&lt;/strong&gt; Pode ser usado para obter resultados cuja URL contém todos os caracteres especificados. Exemplo: allinurl:“tabnews”&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Realizando buscas no TabNews
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1# Exemplo de busca:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;JavaScript site:tabnews.com.br&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Resultado:&lt;/p&gt;

&lt;p&gt;Como resultado da busca no Google temos diversos Links da TabNews com o assunto JavaScript.&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%2Ful0kfd1r8ld7km7vr6bw.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%2Ful0kfd1r8ld7km7vr6bw.png" alt="Resultada da busca no Google com Links da TabNews com o assunto JavaScript" width="800" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2# Exemplo de busca:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Java intext:Spring Boot site:tabnews.com.br&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Resultado:&lt;/p&gt;

&lt;p&gt;Quando adicionamos o termo&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;intext:Spring Boot&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 estamos dizendo para o Google que queremos encontrar o termo Spring Boot dentro do texto do site. Como resultado temos os posts do TabNews que contém o termo "Spring Boot".&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%2F7opcnrt898y9gnlp988v.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%2F7opcnrt898y9gnlp988v.png" alt="Resultada da busca no Google com Links da TabNews com o assunto Java e também com Spring Boot em destaque" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Viu como é simples? E essa técnica funciona para qualquer site!&lt;/p&gt;




&lt;p&gt;Se gostou do conteúdo, compartilhe com seus amigos e deixe seu comentário!&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/jjeanjacques10/" rel="noopener noreferrer"&gt;https://github.com/jjeanjacques10/&lt;/a&gt;&lt;br&gt;
Medium: &lt;a href="https://medium.com/@jjeanjacques10" rel="noopener noreferrer"&gt;https://medium.com/@jjeanjacques10&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Até a próxima!&lt;/p&gt;

</description>
      <category>react</category>
      <category>node</category>
      <category>postgres</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Hospedando uma aplicação Flask no Heroku</title>
      <dc:creator>Jean Jacques Barros</dc:creator>
      <pubDate>Tue, 26 Jul 2022 19:43:00 +0000</pubDate>
      <link>https://dev.to/jjeanjacques10/hospedando-uma-aplicacao-flask-no-heroku-3cm4</link>
      <guid>https://dev.to/jjeanjacques10/hospedando-uma-aplicacao-flask-no-heroku-3cm4</guid>
      <description>&lt;p&gt;Sempre que preciso criar um projetinho para realizar algum teste ou uma prova de conceito minha stack costuma ser essa:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Linguagem: &lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Framework: &lt;a href="https://flask.palletsprojects.com/en/2.1.x/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Plataforma de Hospedagem: &lt;a href="https://www.heroku.com/" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Essas três ferramentas facilitam muito o dia a dia e abstraem aquela complexidade de ter que subir uma máquina em provedores como AWS ou Azure, e chega a ser mais simples do que abrir um túnel para acesso externo (outra coisa que já fiz muito e quem sabe um dia mostro como fazer aqui ✌🏼)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Antes de tudo, segue o repositório de exemplo que criei: &lt;a href="https://github.com/jjeanjacques10/example_heroku_flask" rel="noopener noreferrer"&gt;https://github.com/jjeanjacques10/example_heroku_flask&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;E agora vamos por a mão na massa!&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando nosso App
&lt;/h2&gt;

&lt;p&gt;Vamos iniciar criando uma aplicação bem simples para utilizarmos como base:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;h1&amp;gt;Hello World!&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configurando o Projeto
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8fnt6vmry4m1eej7cuc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8fnt6vmry4m1eej7cuc5.png" alt="Localização do Requirements"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;É preciso especificar quais dependências serão necessárias para executar o projeto, pois serão baixadas dentro da plataforma do Heroku. Para isso adicionamos o arquivo &lt;a href="https://github.com/jjeanjacques10/example_heroku_flask/blob/master/requirements.txt" rel="noopener noreferrer"&gt;&lt;code&gt;requirements.txt&lt;/code&gt;&lt;/a&gt; na raiz do repositório.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flask
Flask-Cors
requests # Exemplo de lib externa que pode usar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Procfile
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpu2fgrcusvbpaf62kseq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpu2fgrcusvbpaf62kseq.png" alt="Localização do Procfile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Um ponto mega importante é adicionar o arquivo &lt;a href="https://github.com/jjeanjacques10/example_heroku_flask/blob/master/Procfile" rel="noopener noreferrer"&gt;&lt;code&gt;Procfile&lt;/code&gt;&lt;/a&gt; o qual é imprescindível para o Heroku saber qual comando executar quando for subir a aplicação. No nosso caso o comando é o seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;web: python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Que basicamente é o mesmo que costumamos rodar local para testar. As vezes pode ser necessário adicionar uma configuração ou outra, mas via de regra apenas chamar o python e o arquivo principal vai funcionar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Porta
&lt;/h3&gt;

&lt;p&gt;Um detalhe que pode passar despercebido é a necessidade de pegar o valor da porta que o Flask irá utilizar nas variáveis de ambiente. A plataforma irá gerar uma aleatório e mapear para a 80, por isso você consegue acessar o link sem passar o ":5000". Segue exemplo de como fazer isso de forma fácil e ainda sim definir um padrão para rodar local (port 5000):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PORT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Realizando o Deploy
&lt;/h2&gt;

&lt;p&gt;Agora que nossa aplicação está configurada criamos nosso projeto na cloud do Heroku e conectamos com o GitHub, você também pode utilizar a opção CLI, a única diferença é que ao conectar com a plataforma da Microsoft temos a opção de habilitar o deploy automático.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7csglq076hwgoa1ix46o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7csglq076hwgoa1ix46o.png" alt="Conexão GitHub e Heroku"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após fazer a conexão será iniciado o processo da esteira de deploy, baixando as dependências e preparando o ambiente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-----&amp;gt; Building on the Heroku-20 stack
-----&amp;gt; Determining which buildpack to use for this app
-----&amp;gt; Python app detected
-----&amp;gt; No Python version was specified. Using the buildpack default: python-3.10.5
       To use a different version, see: https://devcenter.heroku.com/articles/python-runtimes
-----&amp;gt; Installing python-3.10.5
-----&amp;gt; Installing pip 22.1.2, setuptools 60.10.0 and wheel 0.37.1
-----&amp;gt; Installing SQLite3
-----&amp;gt; Installing requirements with pip
       Collecting chardet
         Downloading chardet-5.0.0-py3-none-any.whl (193 kB)
       Collecting click
         Downloading click-8.1.3-py3-none-any.whl (96 kB)
       Collecting cryptography
         Downloading cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl (4.1 MB)
       Collecting distlib
         Downloading distlib-0.3.5-py2.py3-none-any.whl (466 kB)
       Collecting et-xmlfile
         Downloading et_xmlfile-1.1.0-py3-none-any.whl (4.7 kB)
       Collecting filelock
-----&amp;gt; Discovering process types
       Procfile declares types -&amp;gt; web
-----&amp;gt; Compressing...
       Done: 28.7M
-----&amp;gt; Launching...
       Released v3
       https://example-heroku-flask.herokuapp.com/ deployed to Heroku
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto, agora temos uma aplicação, de forma simples, hospedada em uma cloud e de fácil acesso para qualquer pessoa que deseje consumir esse App.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Link para testar: &lt;a href="https://example-heroku-flask.herokuapp.com/" rel="noopener noreferrer"&gt;https://example-heroku-flask.herokuapp.com/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:&lt;/p&gt;

&lt;p&gt;Linkedin: &lt;a href="https://www.linkedin.com/in/jjean-jacques10/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/jjean-jacques10/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Até a próxima!&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>python</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Utilizando AWS Fargate Spot para economizar até 70% em aplicações ECS</title>
      <dc:creator>Jean Jacques Barros</dc:creator>
      <pubDate>Tue, 26 Jul 2022 16:45:19 +0000</pubDate>
      <link>https://dev.to/jjeanjacques10/utilizando-aws-fargate-spot-para-economizar-ate-70-em-aplicacoes-ecs-426l</link>
      <guid>https://dev.to/jjeanjacques10/utilizando-aws-fargate-spot-para-economizar-ate-70-em-aplicacoes-ecs-426l</guid>
      <description>&lt;p&gt;Um forma simples de configurar instâncias Fargate Spot para containers ECS, sendo pelo console ou CloudFormation&lt;/p&gt;

&lt;p&gt;Escrevi este artigo após identificar uma necessidade na empresa em que trabalho, acredito que possa ser útil para quem utiliza o Elastic Container Service (ECS) no dia a dia e deseja diminuir custos, principalmente em ambiente de desenvolvimento e homologação, assim como foi o meu caso!&lt;/p&gt;

&lt;p&gt;Antes de tudo, o ECS é um orquestrador de containers que permite provisionar aplicações de uma forma simples e rápida, possibilitando escalabilidade e um rápido deploy. Com ele é possível utilizar instâncias EC2, onde a infraestrutura fica por nossa conta, ou por meio do AWS Fargate onde não há necessidade por parte do cliente de gerenciar máquinas (Essa segunda opção é a que vamos utilizar).&lt;/p&gt;

&lt;p&gt;Em 2019 foi lançado o Fargate Spot que segue o mesmo conceito das instâncias Spot para Amazon EC2, ele busca oferecer uma forma de econômica para os usuários da cloud fornecendo capacidade não utilizada pela AWS. Então quando solicitadas tarefas Spot será desta capacidade ociosa que elas sairão. Clientes que adquirem instâncias dedicadas tem preferência para tais recursos, por conta disso essa capacidade pode ser retirada a qualquer momento para que estes clientes, que optaram pela opção mais cara, possam utilizar. Você receberá uma notificação dois minutos antes da AWS precisar destes recursos de volta. Contudo, o principal motivo para utilizar Instâncias Spot é cortar custos, o preço (por hora de CPU e GB) de uma Task Spot é variável, com um desconto de 50% a 70% sobre o preço de uma tarefa sob demanda. Então, não se preocupe, definindo a estratégia certa seu sistema não ficará indisponível. Contudo, antes de utilizar em ambiente produtivo pense bem na estratégia que irá implantar, para assim evitar problemas de indisponibilidade ocasionais (📝#dica do Jean ).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Como sempre busco em meus artigos, para exemplificar de forma prática vamos subir uma aplicação Spring Boot com a infra sendo provisionada por um CloudFormation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Então, com os conceitos básicos explicados, vamos começar criando os recursos necessários em nosso Console AWS, e caso queira pular para o template CloudFormation acesse &lt;a href="https://github.com/jjeanjacques10/springboot-ecs-fargate/blob/main/template.yml" rel="noopener noreferrer"&gt;aqui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Existem dois tipos de configuração, uma sendo no Cluster, a qual será replicada para todos os serviços dentro dele e a feita para um serviço específico. Caso seu Cluster possua mais de um Service e você apenas queira implementar essa estratégia em um único, neste cenário não é necessário configurar o Cluster, apenas o Service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando um Cluster
&lt;/h2&gt;

&lt;p&gt;E é aqui que faremos a configuração para aceitar a estratégia de Fargate Spot. Na aba do serviço Elastic Container Service selecione a opção “Networking only”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fxi7ylrzwgvcqvmze4mxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fxi7ylrzwgvcqvmze4mxw.png" alt="Criação do cluster"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após provisionar o Cluster podemos verificar que por padrão nosso Capacity Providers já está configurado para aceitar ambos: &lt;strong&gt;FARGATE ** e **FARGATE_SPOT&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ffzh63gvlj4gwdvabrxeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ffzh63gvlj4gwdvabrxeq.png" alt="Tela de configuração do Cluster Digimon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para definir quais estratégias queremos utilizar devemos clicar no botão de “Update” no canto superior direito.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fc0pu8rjjg07ivswjugcg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fc0pu8rjjg07ivswjugcg.png" alt="Configuração do capacity provider strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nesta tela de configuração selecionamos a estratégia que nosso cluster irá utilizar, nesse caso selecionei ambos os provedores de capacidade. Defini o peso (weight) de 1 para &lt;strong&gt;FARGATE&lt;/strong&gt; e 5 para &lt;strong&gt;FARGATE_SPOT&lt;/strong&gt;. Com esses valores definidos para cada seis tarefas, cinco são iniciadas no &lt;strong&gt;FARGATE_SPOT&lt;/strong&gt; e uma no FARGATE. Claro que você deverá escolher a estratégia que melhor se encaixa com sua necessidade, o que dependerá também de qual ambiente está realizando essas modificações. Por exemplo, o ambiente de desenvolvimento tem menos necessidade de instâncias apenas FARGATE e quanto mais Spots menor a conta no final do mês.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudFormation: Cluster
&lt;/h3&gt;

&lt;p&gt;Para realizar configuração do Fargate Spot por meio do Cloudformation devemos adicionar ao nosso Cluster os atributos de CapacityProvider, neles definimos o peso que cada estratégia deve ter como fizemos pelo Console AWS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;EcsCluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ECS::Cluster'&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ClusterName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${EcsClusterName}'&lt;/span&gt;
    &lt;span class="na"&gt;CapacityProviders&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;FARGATE&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;FARGATE_SPOT&lt;/span&gt;
    &lt;span class="na"&gt;DefaultCapacityProviderStrategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CapacityProvider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FARGATE&lt;/span&gt;
        &lt;span class="na"&gt;Weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CapacityProvider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FARGATE_SPOT&lt;/span&gt;
        &lt;span class="na"&gt;Weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Criando uma Task Definition
&lt;/h2&gt;

&lt;p&gt;No lado esquerdo clique em “Task Definitions”, vamos criar uma nova task do tipo “FARGATE”, no caso é a primeira opção:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F729towd996rnnga516l7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F729towd996rnnga516l7.png" alt="Opção de Fargate no AWS Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Faça as configurações básicas de Role, network e sistema operacional e configuração do container que será utilizado. Para simular criei uma imagem no Amazon Elastic Container Registry (ECR) com a aplicação SpringBoot dentro dela, pode utiliza-la ou fazer com uma imagem própria.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://gallery.ecr.aws/o2n5f4o6/digimonapi" rel="noopener noreferrer"&gt;ECR Public Gallery — digimonapi&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nesta etapa não precisamos configurar nada relacionado a instância &lt;strong&gt;Fargate Spot&lt;/strong&gt;, apenas nos certificarmos que a opção selecionada foi “Fargate”.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudFormation: Task Definition
&lt;/h3&gt;

&lt;p&gt;No Cloudformation um ponto de atenção é adicionar o atributo “&lt;strong&gt;RequiresCompatibilities&lt;/strong&gt;” sendo igual a FARGATE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;TaskDefinition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ECS::TaskDefinition'&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ContainerDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${ContainerName}'&lt;/span&gt;
        &lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ImageDocker&lt;/span&gt;
        &lt;span class="na"&gt;PortMappings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ContainerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ListenerContainerPort&lt;/span&gt;
            &lt;span class="na"&gt;HostPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ListenerContainerPort&lt;/span&gt;
        &lt;span class="na"&gt;Cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ContainerCpu&lt;/span&gt;
        &lt;span class="na"&gt;Memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ContainerMemory&lt;/span&gt;
        &lt;span class="na"&gt;MemoryReservation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ContainerMemoryReservation&lt;/span&gt;
        &lt;span class="na"&gt;Essential&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;Family&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;family-${FeatureName}-${MicroServiceName}'&lt;/span&gt;
    &lt;span class="na"&gt;NetworkMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awsvpc&lt;/span&gt;
    &lt;span class="na"&gt;Memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ContainerMemoryReservation&lt;/span&gt;
    &lt;span class="na"&gt;Cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ContainerCpu&lt;/span&gt;
    &lt;span class="na"&gt;ExecutionRoleArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;TaskExecutionRole.Arn&lt;/span&gt;
    &lt;span class="na"&gt;TaskRoleArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TaskExecutionRole&lt;/span&gt;
    &lt;span class="na"&gt;RequiresCompatibilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;FARGATE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Criando um Service
&lt;/h2&gt;

&lt;p&gt;Agora vamos criar um serviço para gerenciar melhor as Tasks que ficarão rodando. Vá até seu Cluster entre na aba de “Services” e clique em “Create”. Podemos fazer a mesma configuração de estratégia que fizemos para o Cluster ao nível de Service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fia1u174rbvmgailaz2tb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fia1u174rbvmgailaz2tb.png" alt="Como é apresentado no console AWS a mudança do Service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudFormation: Service
&lt;/h3&gt;

&lt;p&gt;A configuração aqui também é bem semelhante, iremos passar o “&lt;em&gt;CapacityProvider&lt;/em&gt;” criando uma estratégia personalizada para este serviço.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::ECS::Service"&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ServiceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;service-${FeatureName}-${MicroServiceName}&lt;/span&gt;
    &lt;span class="na"&gt;Cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;EcsClusterName&lt;/span&gt;
    &lt;span class="na"&gt;DeploymentConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MinimumHealthyPercent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
      &lt;span class="na"&gt;MaximumPercent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
    &lt;span class="na"&gt;DesiredCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
    &lt;span class="na"&gt;HealthCheckGracePeriodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40&lt;/span&gt;
    &lt;span class="na"&gt;CapacityProviderStrategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CapacityProvider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FARGATE_SPOT&lt;/span&gt;
        &lt;span class="na"&gt;Weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CapacityProvider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FARGATE&lt;/span&gt;
        &lt;span class="na"&gt;Weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;PlatformVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.4.0'&lt;/span&gt;
    &lt;span class="na"&gt;LoadBalancers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ContainerName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${ContainerName}&lt;/span&gt;
        &lt;span class="na"&gt;ContainerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ListenerContainerPort&lt;/span&gt;
        &lt;span class="na"&gt;TargetGroupArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TargetGroup&lt;/span&gt;
    &lt;span class="na"&gt;SchedulingStrategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;REPLICA&lt;/span&gt;
    &lt;span class="na"&gt;TaskDefinition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TaskDefinition&lt;/span&gt;
    &lt;span class="na"&gt;NetworkConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AwsvpcConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AssignPublicIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLED&lt;/span&gt;
        &lt;span class="na"&gt;SecurityGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;SecurityGroup&lt;/span&gt;
        &lt;span class="na"&gt;Subnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;PrivateSubnets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rodando as aplicações
&lt;/h2&gt;

&lt;p&gt;Com tudo configurado vamos ativar nosso service para provisionar novas instâncias e observar o comportamento delas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftrtl19bhq737dj124yv6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftrtl19bhq737dj124yv6.png" alt="Tarefas do service rodando"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Acessando as tasks para ter uma visão mais detalhada podemos verificar quais delas foram provisionadas como FARGATE_SPOT. A primeira está como FARGATE e a partir da segunda task já temos o desconto de 50% à 70% por tarefa rodando.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fotzvd3yjdprcuy2qn8bn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fotzvd3yjdprcuy2qn8bn.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Os exemplos descritos neste artigo estão melhor detalhados no seguinte repositório (qualquer dúvida abra uma Issue ou comente):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Repositório GitHub&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/jjeanjacques10/springboot-ecs-fargate" rel="noopener noreferrer"&gt;https://github.com/jjeanjacques10/springboot-ecs-fargate&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Seguindo esse passo a passo podemos provisionar tarefas em nossos ECS’s com desconto na Cloud AWS, nos testes que realizei consegui ver a diferença de custos em média de 72%. Claro que em alguns casos essa abordagem não é a melhor, principalmente porquê estes recursos podem ser solicitados pela AWS quando necessário. Então analise o que faz mais sentido para o seu cenário atual e suas necessidades.&lt;/p&gt;

&lt;p&gt;Agora é buscar aplicar estes conceitos em suas aplicações, sugiro como novamente que façam testes em ambiente não produtivo, que na minha opinião é o ideal, não queremos ter instâncias não dedicadas sendo utilizadas diretamente pelo cliente final.&lt;/p&gt;

&lt;p&gt;Gostaria de agradecer a &lt;a href="https://www.linkedin.com/in/kamiladekassiaperes" rel="noopener noreferrer"&gt;Kamila Peres&lt;/a&gt; por ter me apresentado esse conceito e me incentivado a ir mais a fundo nos estudos.&lt;/p&gt;




&lt;p&gt;Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:&lt;/p&gt;

&lt;p&gt;Linkedin: &lt;a href="https://www.linkedin.com/in/jjean-jacques10/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/jjean-jacques10/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Até a próxima!&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AWS Fargate Spot Now Generally Available | AWS News Blog (amazon.com) &lt;a href="https://aws.amazon.com/blogs/aws/aws-fargate-spot-now-generally-available/" rel="noopener noreferrer"&gt;https://aws.amazon.com/blogs/aws/aws-fargate-spot-now-generally-available/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS Fargate Spot vs. Fargate price comparison &lt;a href="https://tomgregory.com/aws-fargate-spot-vs-fargate-price-comparison/" rel="noopener noreferrer"&gt;https://tomgregory.com/aws-fargate-spot-vs-fargate-price-comparison/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fully Managed Container Solution — Amazon Elastic Container Service (Amazon ECS) — Amazon Web Services&lt;a href="https://aws.amazon.com/ecs/" rel="noopener noreferrer"&gt;https://aws.amazon.com/ecs/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provision Infrastructure As Code — AWS CloudFormation — Amazon Web Services &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;https://aws.amazon.com/cloudformation/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The trouble with Fargate Spot — The Scale Factory &lt;a href="https://www.scalefactory.com/blog/2020/07/27/the-trouble-with-fargate-spot/" rel="noopener noreferrer"&gt;https://www.scalefactory.com/blog/2020/07/27/the-trouble-with-fargate-spot/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>java</category>
      <category>finops</category>
    </item>
    <item>
      <title>Tornando seu código mais SOLID!</title>
      <dc:creator>Jean Jacques Barros</dc:creator>
      <pubDate>Sat, 09 Jul 2022 03:32:20 +0000</pubDate>
      <link>https://dev.to/jjeanjacques10/tornando-seu-codigo-mais-solid-540j</link>
      <guid>https://dev.to/jjeanjacques10/tornando-seu-codigo-mais-solid-540j</guid>
      <description>&lt;p&gt;Respondendo a primeira e mais importante pergunta, o que é SOLID? O SOLID são os 5 princípios de design de código voltados para orientação a objetos que auxiliam os desenvolvedores a escreverem um código para “serem humanos” não apenas para máquinas. O que significa que tornam o sistema criado mais fácil de se manter e também mais adaptável, facilitando alterações de escopo que podem surgir durante a evolução do projeto.&lt;/p&gt;

&lt;p&gt;Estes princípios se mantiveram importantes desde que foram definidos por Robert C. Martin (Uncle Bob) e a necessidade de implementa-los começa a surgir quando se sente um “cheirinho estranho” no código, ou melhor dizendo, um code smells, pode ser definido como qualquer ponto no código que não pareça muito bem e possa indicar algum problema mais profundo. Neste artigo vou lhe mostrar como identificar alguns destes pontos de atenção.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Os exemplos utilizados neste artigo foram escritos em Java, mas podem ser replicados em qualquer linguagem! ☕&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Princípios do SOLID
&lt;/h2&gt;

&lt;p&gt;Vamos começar dizendo o que cada letra significa:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S —&lt;/strong&gt; Single-responsibility Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O —&lt;/strong&gt; Open-closed Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L —&lt;/strong&gt; Liskov Substitution Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I —&lt;/strong&gt; Interface Segregation Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D —&lt;/strong&gt; Dependency Inversion Principle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos começar pelo primeiro princípio e também, em minha opinião, o mais importante de todos eles, o princípio de:&lt;/p&gt;

&lt;h2&gt;
  
  
  Single-Responsibility Principle
&lt;/h2&gt;

&lt;p&gt;No princípio da responsabilidade única, uma classe não deve ter mais do que um objetivo ou finalidade. Com cada parte do seu código responsável por um escopo bem definido, quando isso é feito da maneira certa é possível encontrar de forma fácil o que deseja modificar e também identificar qual o melhor local para implementar uma nova funcionalidade.&lt;/p&gt;

&lt;p&gt;Outra vantagem é que o SRP evita de você ter que guardar todo o programa em sua cabeça, sendo que pequenos “módulos” do código são mais fáceis de lembrarmos no dia a dia. Em projetos grandes é comum criar confusão na hora de lembrar onde fica uma certa função ou qual método deve ser chamado para realizar uma ação.&lt;/p&gt;

&lt;p&gt;Exemplo de uma classe que tem mais responsabilidades do que o necessário:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokedexService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;calculateTotalSum&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;getAllPokemon&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addPokemon&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;deletePokemon&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;printReport&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;showCatch&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Pokemon&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Pokemon&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este é um exemplo de uma God Class (Classe Deus), em POO damos esse nome quando uma classe “sabe demais”, ou seja, que implementa vários métodos e não tem um objetivo bem definido. Com o tempo a tendência é ela aumentar cada vez mais de tamanho. Um dos caminhos para evitar essa bola de neve que irá se formar é utilizar o princípio da responsabilidade única criando classes que são responsáveis por uma única tarefa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokedexService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;calculateTotalSum&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAllPokemon&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Pokemon&lt;/span&gt; &lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addPokemon&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;deletePokemon&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;    
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Service&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;printReport&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;showCatch&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*&amp;lt;code&amp;gt;*/&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Repository&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PokemonRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;Pokemon&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;Pokemon&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neste novo exemplo fiz a separação das responsabilidades, agora temos o ReportService que cuida dos métodos de relatório e o PokedexService que é responsável pelo CRUD de Pokemon. O acesso ao banco de dados é feito por meio de um Repository. Agora ficou mais fácil para crescer a aplicação, com cada item com responsabiliades bem divididas.&lt;/p&gt;

&lt;p&gt;Principais problemas quando não implementado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dificuldade em escrever testes, principalmente de unidade;&lt;/li&gt;
&lt;li&gt;Falta de coesão no código;&lt;/li&gt;
&lt;li&gt;Alto acoplamento, a dependência entre as partes de sua aplicação irá aumentar.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open-closed Principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Entidades de software (classes, módulos, funções e etc.) devem estar abertas para extensão, porém fechadas para modificação”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Este princípio determina que uma classe deve ser “fechada para alteração e aberta para extensão”. Okay, mas o que isso significa? 😕&lt;/p&gt;

&lt;p&gt;Isso quer dizer que sempre que uma regra de negócio nova é adicionada não será necessário alterar um código já existente e sim adicionar uma nova implementação dele. Isso ficará muito mais claro depois de alguns exemplos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrainingService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;PokemonDTO&lt;/span&gt; &lt;span class="nf"&gt;trainPokemon&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonDTO&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TrainingDTO&lt;/span&gt; &lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLastWorkout&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hoursBetweenDates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChronoUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;HOURS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLastWorkout&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hoursBetweenDates&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidTraining&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your pokemon is tired, wait 8 hours"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAttack&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidTraining&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pokemon attack cannot be greater than 1000"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;newAttack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;round&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAttack&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAttack&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIntension&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;newDefense&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;round&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDefense&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDefense&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIntension&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
        &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAttack&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newAttack&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDefense&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newDefense&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setLastWorkout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui temos diversas validações sendo realizadas ao tentar treinar um Pokémon, sempre que uma nova validação precisar ser adicionada precisaremos modificar esse código adicionando uma condicional. Como solução podemos fazer a seguinte modificação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrainingServiceImpl&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;TrainingService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TrainingValidation&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validations&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TrainingServiceImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TrainingValidation&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validations&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validations&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;PokemonDTO&lt;/span&gt; &lt;span class="nf"&gt;trainPokemon&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonDTO&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TrainingDTO&lt;/span&gt; &lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;validations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;setNewAttributes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setNewAttributes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonDTO&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TrainingDTO&lt;/span&gt; &lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora nosso Service recebe uma lista de validações (linha 5) que implementa a interface TrainingValidation. Desta forma sempre que uma nova validação precisar ser adicionada ao nosso treinamento basta criar uma nova classe que implemente está interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TrainingValidation&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;valid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonDTO&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TrainingDTO&lt;/span&gt; &lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidatePokemonPowerLimit&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;TrainingValidation&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;valid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonDTO&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TrainingDTO&lt;/span&gt; &lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAttack&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidTraining&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pokemon attack cannot be greater than 1000"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidateDateLastWorkout&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;TrainingValidation&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;valid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonDTO&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TrainingDTO&lt;/span&gt; &lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLastWorkout&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hoursBetweenDates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChronoUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;HOURS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLastWorkout&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;training&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDate&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hoursBetweenDates&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidTraining&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your pokemon is tired, wait 8 hours"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obs: O OCP pode lembrar bastante o Design Pattern &lt;a href="https://refactoring.guru/design-patterns/strategy"&gt;Strategy&lt;/a&gt;, é um tópico interessante para se aprofundar.&lt;/p&gt;

&lt;p&gt;Principais problemas quando não implementado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Métodos com várias condicionais&lt;/li&gt;
&lt;li&gt;Modificações constantes em entidades de software que já existem&lt;/li&gt;
&lt;li&gt;Aumento no número de bugs ocasionais ao alterar regras de negócio&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Liskov Substitution Principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Se S é um subtipo de T, então os objetos do tipo T, em um programa, podem ser substituídos pelos objetos de tipo S sem que seja necessário alterar as propriedades deste programa” — &lt;a href="https://pt.wikipedia.org/wiki/Princ%C3%ADpio_da_substitui%C3%A7%C3%A3o_de_Liskov"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vamos combinar que essa definição pode deixar nossa mente mais confusa do que explicar algo 😅, mas vamos por partes… Em outras palavras: Um novo objeto criado a partir de uma classe que possuí herança não pode quebrar o comportamento da classe ancestral.&lt;/p&gt;

&lt;p&gt;Como exemplo estou utilizando a classe Item que é estendida para as classes filho PokeBall, MasterBall e UltraBall. Mas quando vemos a implementação podemos perceber que MasterBall não implementa o método “buy” porquê não é possível comprar esse tipo de item, apenas adquiri-lo em um lugar específico durante a sua jornada.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;buyItem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;sellItem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MasterBall&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;buyItem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidBusinessTransaction&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"It's impossible buy this item"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;sellItem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getLocation&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pokeball&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;buyItem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;sellItem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por conta disso temos esse efeito inesperado quando tentamos vender um item MasterBall.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IC9p79Bq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i7lw3brvfqdwbup7ppk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IC9p79Bq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i7lw3brvfqdwbup7ppk7.png" alt="Exemplificação de como a herança feriu o LSP" width="736" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para seguirmos o princípio LSP a nossa MasterBall não deve herdar da classe “Item” e sim da nova Classe “ItemRare” que tem os métodos que se encaixam melhor no escopo desejado.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ItemRare&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getLocation&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;sellItem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MasterBall&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ItemRare&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;buyItem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;sellItem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getLocation&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*code*/&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com essa nova implementação as classes filho podem substituir facilmente a classe ancestral.&lt;/p&gt;

&lt;p&gt;Principais problemas quando não implementado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Métodos que lançam exceções inesperadas&lt;/li&gt;
&lt;li&gt;Valores de tipos diferentes da classe base&lt;/li&gt;
&lt;li&gt;Implementar um método que não faz nada&lt;/li&gt;
&lt;li&gt;Muita lógica condicional espalhada pela aplicação&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Interface Segregation Principle
&lt;/h2&gt;

&lt;p&gt;Uma classe não deve implementar métodos que não irá utilizar. Por conta disso uma segmentação maior acaba sendo melhor para a organização do código. Ao desenvolver um software devemos preferir ter mais interfaces específicas, em vez de uma única interface grande e de uso geral, o que tem relação com o princípio de responsabilidade única (SRP), de acordo com essa ideia vejamos a seguinte implementação. Essa é a interface de Payment onde temos os métodos relacionados a um processo de transação dentro da aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initiatePayments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;intiateLoanSettlement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;initiateRePayment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos ver a implementação desta interfacenas Classes WalletService e LoanService.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Service Wallet implementando a interface de Pagamento&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WalletService&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initiatePayments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* code */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* code */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* code */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initiateLoanSettlement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnsupportedOperation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is not a loan payment"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;initiateRePayment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnsupportedOperation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is not a loan payment"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O service de Wallet precisa implementar todos os métodos da interface, o que acaba causando comportamentos inesperados quando métodos relacionados a empréstimo como o initiateLoanSettlement são chamados, nesse exemplo a classe Wallet não tem como iniciar um empréstimo assim como a classe de LoanService não tem como iniciar um pagamento.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Service Loan implementando a interface e Pagamento&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoanService&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initiatePayments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnsupportedOperation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is not a wallet payment"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* code */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* code */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initiateLoanSettlement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* code */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokeball&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;initiateRePayment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* code */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como solução para este problema segue um exemplo de implementação de interfaces mais específicas que atendem melhor a necessidade de nossa aplicação:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EfVXXWaR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j32elek9naqmr9b58b00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EfVXXWaR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j32elek9naqmr9b58b00.png" alt="Diagrama de Classe — Exemplo ISP" width="818" height="852"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neste novo desenho cada Service implementa de uma interface com os métodos que pertence ao seu domínio. Como dá para perceber o número de arquivos aumentou, mas isso não é um problema porquê o foco é manter as classes implementando apenas os métodos necessários.&lt;/p&gt;

&lt;p&gt;Resumindo este princípio, caso uma interface comece a ganhar muitas responsabilidades ela deve ser dividida em interfaces menores, as quais serão implementadas pelos clientes (entidades de software). Lembrando que os clientes não devem implementar métodos que não utilizam.&lt;/p&gt;

&lt;p&gt;Principais problemas quando não implementado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comportamentos inesperados quando chamar uma função&lt;/li&gt;
&lt;li&gt;Utilização dos conceitos de herança de forma errada&lt;/li&gt;
&lt;li&gt;Ferir o primeiro princípio da responsabilidade única&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Dependency Inversion Principle
&lt;/h2&gt;

&lt;p&gt;O princípio de inversão de dependência é sobre como classes devem depender de abstrações, não de implementações específicas dessas abstrações. Quando fazemos isso evitamos que detalhes ditem como devemos implementar uma solução. Se você realiza a chamada de uma classe dentro de outra está classe irá depender da que foi chamada. Isso resulta em um grande acoplamento e dificulta na alteração de módulos externos e também na escrita de testes. Busque então incorporar essa noção em seu código caso queira torna-lo mais flexível, ágil e reutilizável.&lt;/p&gt;

&lt;p&gt;Neste exemplo a classe PokedexService está instanciando o modelMapper e também a Conexão do banco de dados dentro de seu construtor, da forma que está implementada temos uma dependência destes dois atributos. No caso temos acesso até as informações de URL, USER e PASSWORD referentes a conexão ao banco de dados, o que sai totalmente do escopo original da Pokedex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokedexService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ModelMapper&lt;/span&gt; &lt;span class="n"&gt;modelMapper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;Connection&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PokedexService&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;modelMapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ModelMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DriverManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConnection&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PASSWORD&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para resolver esses problemas transformamos a conexão em uma interface de Repository, que é a abstração que comentei a cima, onde ela pode ser trocada facilmente por outros clientes de banco de dados. Além disso ambos Repository e ModelMapper são trazidos para classe de Service por meio da passagem da informação pelo construtor (Dependency Injection) sendo possível uma maior flexibilidade.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokedexServiceImpl&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PokedexService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;PokemonRepository&lt;/span&gt; &lt;span class="n"&gt;pokemonRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ModelMapper&lt;/span&gt; &lt;span class="n"&gt;modelMapper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PokedexServiceImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonRepository&lt;/span&gt; &lt;span class="n"&gt;pokemonRepository&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                              &lt;span class="nc"&gt;ModelMapper&lt;/span&gt; &lt;span class="n"&gt;modelMapper&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pokemonRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pokemonRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelMapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modelMapper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos utilizar este conceito também para estruturar melhor nossa aplicação, como exemplo no projeto criei uma interface por serviço, desta forma tenho uma visão melhor de quais são os métodos realmente necessários e também tenho uma facilidade maior para trocar uma implementação caso necessário.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WAJPE2-C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ogvuziytrpx6x7t8tjdq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WAJPE2-C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ogvuziytrpx6x7t8tjdq.png" alt="Estrutura de arquivos no projeto de exemplo" width="599" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Principais problemas quando não implementado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alto acoplamento entre classes&lt;/li&gt;
&lt;li&gt;Aumento da complexidade para trocar um sistema externo (adapter)&lt;/li&gt;
&lt;li&gt;Aumento da complexidade na escrita de testes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recomendações&lt;/p&gt;

&lt;p&gt;Conheci os princípios do &lt;strong&gt;SOLID&lt;/strong&gt; por meio do livro &lt;a href="https://www.amazon.com.br/C%C3%B3digo-limpo-Robert-C-Martin/dp/8576082675/ref=asc_df_8576082675/?tag=googleshopp00-20&amp;amp;linkCode=df0&amp;amp;hvadid=379792215563&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=12106636064503990563&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=1001773&amp;amp;hvtargid=pla-398225630878&amp;amp;psc=1"&gt;Clean Code&lt;/a&gt;. É uma leitura que recomendo para aqueles que buscam melhorar a forma que escrevem código. Poderia recomendar também diversos vídeos e artigos como este, contudo, o mais importante é implementar o que viu aqui em seus projetos, só assim estes conceitos ficaram mais enraizados em sua mente.&lt;/p&gt;

&lt;p&gt;Para ajudar você a por a mão na massa segue um link que pode ser bem útil:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Repositório GitHub&lt;/strong&gt;&lt;br&gt;
Aqui está o link para o repositório no GitHub onde coloquei os exemplos de código escritos em Java: &lt;a href="https://github.com/jjeanjacques10/solid"&gt;https://github.com/jjeanjacques10/solid&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Quando implementamos estes conceitos em nossos projetos temos um código melhor organizado, mais robusto e de fácil manutenção. Que é basicamente o sonho de todo desenvolvedor. O &lt;strong&gt;SOLID&lt;/strong&gt; sozinho não vai fazer milagres, mas ajuda aqueles que trabalham com programação orientada a objetos a terem uma visão mais voltada para o reaproveitamento e boas práticas.&lt;/p&gt;

&lt;p&gt;No começo pode ser um pouco complicado entender todos estes princípios, mas o que me ajuda a compreender cada dia mais o &lt;strong&gt;SOLID&lt;/strong&gt; é buscar implementa-los todos os dias nos códigos que desenvolvo. Se quer aprender então coloque a mão na massa!&lt;/p&gt;




&lt;p&gt;Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem. Até a próxima!&lt;/p&gt;

&lt;p&gt;LinkedIn: &lt;a href="https://www.linkedin.com/in/jjean-jacques10/"&gt;https://www.linkedin.com/in/jjean-jacques10/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod"&gt;http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/java-interface-segregation"&gt;https://www.baeldung.com/java-interface-segregation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/proandroiddev/exploring-s-o-l-i-d-principle-in-android-a90947f57cf0"&gt;https://medium.com/proandroiddev/exploring-s-o-l-i-d-principle-in-android-a90947f57cf0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>solid</category>
      <category>java</category>
      <category>programming</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Spring Boot Microservices — Monorepo Heroku</title>
      <dc:creator>Jean Jacques Barros</dc:creator>
      <pubDate>Fri, 08 Jul 2022 22:47:44 +0000</pubDate>
      <link>https://dev.to/jjeanjacques10/spring-boot-microservices-monorepo-heroku-3pff</link>
      <guid>https://dev.to/jjeanjacques10/spring-boot-microservices-monorepo-heroku-3pff</guid>
      <description>&lt;p&gt;Como hospedar Microservices desenvolvidos com Spring Boot no Heroku utilizando a estratégia de Monorepo&lt;/p&gt;

&lt;p&gt;Diversas empresas utilizam &lt;strong&gt;Monorepo&lt;/strong&gt;, que consiste na estratégia de adicionar diversos projetos em apenas um repositório, gerando assim uma única base de código. Essa abordagem facilita no reaproveitamento de código e também na padronização entre projetos. Contudo, este tipo de abordagem trás consigo alguns desafios quando queremos utilizar um sistema de Continuous Deploy (CD) como o Heroku.&lt;/p&gt;

&lt;p&gt;Neste artigo vou apresentar o caminho que utilizei para realizar o deploy de diversos microservices desenvolvidos com &lt;strong&gt;Spring Boot&lt;/strong&gt; que se encontram em apenas um repositório no &lt;strong&gt;GitHub&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Para isso vamos precisar alterar os arquivos de configuração de nossos projetos e também configurar o processo dentro da plataforma de hospedagem, no caso o Heroku, então vamos por a mão na massa!&lt;/p&gt;

&lt;h2&gt;
  
  
  Configurando o repositório
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.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%2F5xjai422vz8xlkjuunj9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5xjai422vz8xlkjuunj9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Precisaremos adicionar 2 arquivos na raiz de cada projeto, no caso, como vamos trabalhar com Java o primeiro arquivo deverá ser o “system.properties”, nele informamos qual a versão que desejamos trabalhar, neste caso estou utilizando o Java 17.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java.runtime.version&lt;span class="o"&gt;=&lt;/span&gt;17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora iremos criar o arquivo “Procfile”, que devemos prestar muita atenção na hora de escrever. Nele adicionamos o comando que irá ser executado quando o Heroku for realizar o deploy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: java $JAVA_OPTS -cp target/classes:target/dependency/* -Dserver.port=$PORT -jar target/user-service-0.0.1-SNAPSHOT.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neste caso estou dizendo para ele executar o arquivo jar (user-service-0.0.1-SNAPSHOT.jar), para cada projeto você deve informar o arquivo de saída esperado. Como parâmetro neste comando passamos a porta que a aplicação deverá rodar “-Dserver.port=$PORT”, o valor da variável de ambiente $PORT será fornecido pelo Heroku. É possível adicionar mais parâmetros de acordo com a necessidade do seu projeto, então fique atento e realize alguns testes locais tentando rodar o comando em sua máquina antes de subir para o repositório e testa-lo na esteira.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configurando o Heroku
&lt;/h2&gt;

&lt;p&gt;Acessando o site do Heroku: &lt;a href="https://dashboard.heroku.com/" rel="noopener noreferrer"&gt;https://dashboard.heroku.com/&lt;/a&gt; devemos clicar em “new” para criar um novo App, selecione um nome único para o aplicativo, ele será o Host de sua aplicação. Após ter criado o App vá até a opção de “settings” (configurações) e localize a sessão de “Buildpacks”, nele que adicionaremos a opção de trabalhar com monorepos utilizando o pacote &lt;a href="https://elements.heroku.com/buildpacks/lstoll/heroku-buildpack-monorepo" rel="noopener noreferrer"&gt;heroku-buildpack-monorepo&lt;/a&gt; criado por &lt;a href="https://github.com/lstoll" rel="noopener noreferrer"&gt;Lincoln Stoll&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Primeiro clique em adicionar um buildpack novo, coloque o link do repositório: &lt;a href="https://github.com/lstoll/heroku-buildpack-monorepo" rel="noopener noreferrer"&gt;https://github.com/lstoll/heroku-buildpack-monorepo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpvplk65ukmjjby62pc2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpvplk65ukmjjby62pc2f.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Não se esqueça de adicionar o buildpack para o Java também, como no exemplo acima. Os dois devem estar selecionados para que possamos prosseguir:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7ppr9ays4t90xp7rg2y1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7ppr9ays4t90xp7rg2y1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para especificar em qual pasta estará o projeto correspondente ao App devemos alterar as variáveis de ambiente adicionando o:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;APP_BASE=/nome-pasta-projeto&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Neste exemplo passamos /user-service para o meu serviço que controla os usuários do Twitter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fikb43nddomy9i8fol8dn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fikb43nddomy9i8fol8dn.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Não se esqueça de adicionar as demais variáveis de ambiente que o seu microservice precisa para ser executado. Ambiente configurado, vamos conectar o nosso repositório GitHub. Na aba de “Deploy” selecione o método GitHub e escolha a conta desejada, pode ser a sua pessoal ou da organização que trabalha.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fzu97gmd26l3ijuttlfy0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fzu97gmd26l3ijuttlfy0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após realizar liberação temos a opção de escolher o projeto que queremos trabalhar, pesquise pelo nome dele e conecte. A seguir escolha a branch que deseja utilizar, no meu caso selecionei a main, também deixei ativa a opção para realizar o deploy quando algum novo commit for feito.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7swjd4klc38wjeifu6xk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7swjd4klc38wjeifu6xk.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pronto! Agora com tudo configurado basta testar seus Apps. Segue o exemplo dos diversos serviços rodando na aplicação do Twitter que usei como base!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ffkx9ffhp9cyltfjsaeic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ffkx9ffhp9cyltfjsaeic.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repositório projeto de exemplo:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/jjeanjacques10/twitter-microservices" rel="noopener noreferrer"&gt;https://github.com/jjeanjacques10/twitter-microservices&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Seguindo este passo a passo é possível de forma fácil e rápida disponibilizar diversos serviços na plataforma do Heroku sem criar um repositório para cada um deles. O exemplo que dei foi utilizando Spring Boot, mas é possível fazer esta mesma configuração em projetos NodeJS, Python ou demais linguagens.&lt;/p&gt;




&lt;p&gt;Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:&lt;/p&gt;

&lt;p&gt;Linkedin: &lt;a href="https://www.linkedin.com/in/jjean-jacques10/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/jjean-jacques10/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Até a próxima!&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://elements.heroku.com/buildpacks/lstoll/heroku-buildpack-monorepo" rel="noopener noreferrer"&gt;https://elements.heroku.com/buildpacks/lstoll/heroku-buildpack-monorepo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.heroku.com/managing_your_microservices_on_heroku_with_netflix_s_eureka" rel="noopener noreferrer"&gt;https://blog.heroku.com/managing_your_microservices_on_heroku_with_netflix_s_eureka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jjeanjacques10/twitter-microservices" rel="noopener noreferrer"&gt;https://github.com/jjeanjacques10/twitter-microservices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>heroku</category>
      <category>springboot</category>
      <category>git</category>
    </item>
    <item>
      <title>Análise de sentimentos | Twitter com Azure Cognitive Services</title>
      <dc:creator>Jean Jacques Barros</dc:creator>
      <pubDate>Wed, 15 Apr 2020 01:21:16 +0000</pubDate>
      <link>https://dev.to/msplatam/analise-de-sentimentos-twitter-com-azure-cognitive-services-375b</link>
      <guid>https://dev.to/msplatam/analise-de-sentimentos-twitter-com-azure-cognitive-services-375b</guid>
      <description>&lt;p&gt;Com Python + Azure temos uma forma fácil de usar inteligência artificial em nossas aplicações&lt;/p&gt;

&lt;p&gt;O objetivo desse artigo não é apresentar uma aplicação complexa, tenho como foco destrinchar o uso de API’s com inteligência artificial integrando com uma das maiores redes sociais do mundo.&lt;/p&gt;

&lt;p&gt;O Twitter se tornou bem mais que apenas uma rede social, políticos o utilizam para se comunicarem diretamente com seu público, ameaçar outros governantes entre outras coisas possíveis no meio de muitos MEME’s e gifs engraçados. Para nós da área de tecnologia é um cenário repleto de dados que podemos analisar e foi isso que fiz, utilizado o Azure e a API do Twitter fiz uma análise de sentimentos de alguns Tweets de personalidades importantes atualmente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Twitter
&lt;/h2&gt;

&lt;p&gt;O Twitter oferece acesso as suas APIs por meio do site deles, elas são os segmentadas em&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standard API&lt;/li&gt;
&lt;li&gt;Premium API&lt;/li&gt;
&lt;li&gt;Enterprise API&lt;/li&gt;
&lt;li&gt;Ads API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Irei utilizar a Padrão (Standard API) que não é preciso pagar e também é a mais fácil de se ter acesso, como única exigência é preciso ter uma conta de desenvolvedor, ação bem simples de ser concluída, assim que fizer a inscrição basta esperar o e-mail de confirmação.&lt;/p&gt;

&lt;p&gt;Já logado você vai entrar no site e criar o seu APP, assim que finalizado, vai ter acesso a sua API key e a API secret key (CUIDADO, apenas você de ter acesso a elas).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F700%2F1%2AW9Fn7xlNo9B2u6OiNmQHFA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F700%2F1%2AW9Fn7xlNo9B2u6OiNmQHFA.png" alt="API Twitter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com essas duas chaves você agora poderá integrar ao seu código&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é Azure Cognitive services?
&lt;/h2&gt;

&lt;p&gt;A Microsoft oferece um meio de desenvolvedores que não tem conhecimentos avançados em inteligência artificial a implementarem em suas soluções tecnologias “Cognitivas”, ou seja, que emulam os serem humanos com funcionalidades como ver, ouvir, falar, pesquisar, entender e auxiliar na tomada de decisão.&lt;/p&gt;

&lt;p&gt;Vamos utilizar a Análise de texto para identificar o sentimento em uma frase, um dos pontos fortes é que essa análise funciona tanto em português quanto em outros idiomas. É possível testar o recurso no próprio site deles:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjtddc44wwx3w4xjhsb0q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjtddc44wwx3w4xjhsb0q.png" alt="Teste da API de analise de sentimento no site do Azure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assim que fizer o cadastro no Azure e criar um novo cognitive service vai receber o seu subscription_key e o endpoint no qual poderá consumir o serviço.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F700%2F1%2Aa-6fnoNKL010NxmDvGE59Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F700%2F1%2Aa-6fnoNKL010NxmDvGE59Q.png" alt="Tela do serviço criado no Azure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com as keys do Twitter(4 primeiros campos) e da API do Azure(2 últimos campos) vamos criar o env.json, nele teremos tudo que nosso projeto precisa para funcionar, ele vai ter esse formato:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F700%2F1%2ANkFlTuLRJmoGk1PH5co8wg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F700%2F1%2ANkFlTuLRJmoGk1PH5co8wg.png" alt="env.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora que temos tudo que precisamos,&lt;/p&gt;

&lt;h2&gt;
  
  
  Vamos por a mão na massa!
&lt;/h2&gt;

&lt;p&gt;Para criar uma aplicação simples primeiro vamos coletar os tweets da rede social, o módulo que vamos precisar importar primeiro é o tweepy, ele que vai nos ajudar a coletar as publicações de uma determinada conta, como exemplo estou coletando da minha pessoal @jeanjacques1999, defini para coletar até 500 tweets, você pode alterar essas variáveis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# -*- coding: utf-8 -*-
import tweepy, json, os

def get_tweets():
    try:
        #Acessando o arquivo com as keys
        with open('env.json', 'r') as f:
            content = json.loads(f.read())
        f.close()

        chave_consumidor = content['chave_consumidor']
        segredo_consumidor = content['segredo_consumidor']
        token_acesso = content['token_acesso']
        token_acesso_segredo = content['token_acesso_segredo']

        #Fazendo a autenticão
        autenticacao = tweepy.OAuthHandler(chave_consumidor, segredo_consumidor)
        autenticacao.set_access_token(token_acesso, token_acesso_segredo)

        twitter = tweepy.API(autenticacao)

        #Fazendo as pesquisa (Você pode alterar o language)
        result = twitter.search(q='jeanjacques1999', count=1000, tweet_mode="extended")

        data = []
        for tweet in result:
            data.append({'id':str(tweet._json['id']), 'language':'pt', 'text':tweet._json['full_text']})

        return data
    except Exception as e:
        erro = "Encountered exception. {}".format(e)
        return erro 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Função get_tweets() vai se conectar ao Twitter e retornar um JSON com o seguinte formato:&lt;/p&gt;

&lt;p&gt;ID, language e text, a linguagem pode ser alterada no código para uma das seguintes suportadas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F500%2F1%2A8g8S2oPp907HWeTBbeqU-g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F500%2F1%2A8g8S2oPp907HWeTBbeqU-g.png" alt="JSON que salva as informações"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Português (pt)&lt;/li&gt;
&lt;li&gt;Inglês (en)&lt;/li&gt;
&lt;li&gt;Espanhol (es)&lt;/li&gt;
&lt;li&gt;Francês (fr)&lt;/li&gt;
&lt;li&gt;Russo (ru)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O campo text é o corpo que será analisado pela nossa inteligência artificial, assim que retornamos todos os tweets vamos passa-los para outro método, que está em nosso main(), nossa função principal ficou assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from get_tweets import get_tweets
from cognitiveService import sentiment
import os, json

def main():
    #Coletandos os tweets
    tweet = get_tweets()
    insert_data(tweet)

    if(tweet):
        #Passando em nossa IA para identificar sentimento
        tweet_score = sentiment()
        insert_data(tweet_score)
    else:
        print(tweet)

def insert_data(data):
    #Apagando o arquivo caso ele exista
    dir = os.listdir()
    for file in dir:
        if file == "twitters.json":
            os.remove(file)

    #Inserindo os dados entro do arquivo twitters.json
    with open('twitters.json', 'a', encoding='utf-8') as f:
        json.dump(data, f)
    f.close()

if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No main() chamamos a função get_tweets() que mostrei anteriormente e então inserimos dentro do arquivo twitters.json, a análise de sentimento será feita na função sentiment() que vou mostrar a seguir. Para que ela funciona antes instale o pacote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;pip install azure.cognitiveservices.language.textanalytics&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# -*- coding: utf-8 -*-
import os, json
from azure.cognitiveservices.language.textanalytics import TextAnalyticsClient
from msrest.authentication import CognitiveServicesCredentials

#Acessando o arquivo com as keys
with open('env.json', 'r') as f:
    content = json.loads(f.read())
f.close()

subscription_key = content['subscription_key']
endpoint = content['endpoint']

def authenticateClient():
    credentials = CognitiveServicesCredentials(subscription_key)
    text_analytics_client = TextAnalyticsClient(
        endpoint=endpoint, credentials=credentials)
    return text_analytics_client

def sentiment():
    #Criando um client já autenticado
    client = authenticateClient()
    try:
        #Lendo os tweets que foram salvos anteriormente
        with open('twitters.json', 'r') as f:
            tweets = json.loads(f.read())

        #Fazendo a análise dos sentimentos
        response = client.sentiment(documents=tweets)
        sum_total = 0

        for index, document in enumerate(response.documents):
            tweets[index]["score"] = document.score
            sum_total += document.score

        #Calculo e mostro a média de Scores
        average = (sum_total/len(response.documents)) * 100
        print("Média dos Scores: {0:.2f}%".format(average))

        #Retornando os tweets com o score
        return tweets
    except Exception as err:
        print("Encountered exception. {}".format(err))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chegamos na parte em que a mágica acontece, faço a leitura do arquivo que foi salvo anteriormente e passo para o meu client, o client autenticado é a nossa API do cognitive service que estamos consumindo diretamente do Azure. Quando o processo for finalizados vamos ter no mesmo JSON o seguinte resultado:&lt;/p&gt;

&lt;p&gt;O Score foi adicionado ao nosso objeto e ele é a porcentagem do sentimento, quanto maior mais positivo/feliz o texto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F500%2F1%2AAW4QTKarC3QcogFVzi2vsA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F500%2F1%2AAW4QTKarC3QcogFVzi2vsA.png" alt="JSON que salva as informações finais"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Também optei por mostrar na tela uma média a qual diz se o twitter analisado é mais positivo ou negativo. O meu resultado ficou assim: “Média dos Scores: 46.20%”, poderia ter sido melhor claro 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  Analisando outros perfis
&lt;/h2&gt;

&lt;p&gt;Vamos rodar o algoritmo em alguns perfis de pessoas influentes no Twitter&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trump (@realDonaldTrump)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Média dos Scores: 53.41%&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Globo News (@GloboNews)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Média dos Scores: 36.32%&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bolsonaro (@jairbolsonaro)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Média dos Scores: 52.24%&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tabata Amaral (@tabataamaralsp)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Média dos Scores: 43.10%&lt;/p&gt;

&lt;h1&gt;
  
  
  A Solução
&lt;/h1&gt;

&lt;p&gt;Ao final de tudo vamos ter as seguintes informações&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Porcentual de felicidade em um tweet&lt;/li&gt;
&lt;li&gt;Média de felicidade de um perfil na rede social&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Não tive como objetivo criar o melhor e mais completo programa para análise de sentimento aqui, só quis lhe mostrar como é possível iniciar algo que pode ter várias aplicações de uma forma bem rápida e com pouca enrolação.&lt;/p&gt;

&lt;p&gt;Agora você pode baixar o código no GitHub e brincar a vontade. Veja quem é uma pessoa mais positiva na rede social, você ou seu amigo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repositório Github
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Análise de sentimentos com Python e Azure - &lt;a href="https://github.com/jjeanjacques10/analise-de-sentimento-python-azure" rel="noopener noreferrer"&gt;https://github.com/jjeanjacques10/analise-de-sentimento-python-azure&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:&lt;/p&gt;

&lt;p&gt;Linkedin: &lt;a href="https://www.linkedin.com/in/jjean-jacques10/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/jjean-jacques10/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Até a próxima!&lt;/p&gt;

&lt;h1&gt;
  
  
  Referências
&lt;/h1&gt;

&lt;p&gt;Azure Cognitive Services Acesso em: 22 janeiro. 2020.&lt;/p&gt;

&lt;p&gt;API do Twitter &lt;a href="https://developer.twitter.com/en/dashboard" rel="noopener noreferrer"&gt;https://developer.twitter.com/en/dashboard&lt;/a&gt; Acesso em: 22 janeiro. 2020.&lt;/p&gt;

</description>
      <category>twitter</category>
      <category>python</category>
      <category>azure</category>
      <category>api</category>
    </item>
  </channel>
</rss>
