<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Igor Oliveira</title>
    <description>The latest articles on Forem by Igor Oliveira (@oliverigor27).</description>
    <link>https://forem.com/oliverigor27</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%2F582588%2F73d61050-38fd-4692-8a7a-d8781d5ee9c4.jpeg</url>
      <title>Forem: Igor Oliveira</title>
      <link>https://forem.com/oliverigor27</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/oliverigor27"/>
    <language>en</language>
    <item>
      <title>Uma Abordagem Funcional para Domain-Driven Design</title>
      <dc:creator>Igor Oliveira</dc:creator>
      <pubDate>Fri, 28 Nov 2025 00:43:14 +0000</pubDate>
      <link>https://forem.com/oliverigor27/uma-abordagem-funcional-para-domain-driven-design-51ii</link>
      <guid>https://forem.com/oliverigor27/uma-abordagem-funcional-para-domain-driven-design-51ii</guid>
      <description>&lt;p&gt;Há algumas semanas, eu defini a meta de aprender TypeScript e desenvolver um produto do zero utilizando tudo o que a linguagem de programação tem a oferecer. Sendo alguém com experiência trabalhando com orientação a objetos, migrar para um ambiente multiparadigma me pareceu bastante interessante. Um dos desafios foi trazer os conceitos aplicados em aplicações C# e .NET para o mundo do Node.js, e um desses conceitos foi o Domain-Driven Design.&lt;/p&gt;

&lt;p&gt;Explicando de forma breve, &lt;strong&gt;&lt;em&gt;Domain-Driven Design&lt;/em&gt;&lt;/strong&gt;, ou DDD, é uma abordagem de desenvolvimento de software que foca na complexidade do domínio de negócio, trazendo-o para o centro do desenvolvimento e buscando aproximar os especialistas desse domínio do processo de criação dos sistemas. Ele te fornece algumas ferramentas para lidar melhor com essa complexidade. &lt;/p&gt;

&lt;p&gt;Dentro dessa abordagem, você encontra o &lt;strong&gt;Design Estratégico&lt;/strong&gt;: ele lida com a visão macro, com a estrutura do domínio, com os limites do negócio e com o vocabulário dos especialistas. É aqui que se trabalha com Mapas de Contexto, Contextos Delimitados, subdomínios, Domínio Básico, Domínio de Suporte, Domínio Genérico e Linguagem Ubíqua. Já o &lt;strong&gt;Design Tático&lt;/strong&gt; entra na parte “concreta” da modelagem dentro de cada Bounded Context. É aqui que lidamos com Entidades, Objetos de Valor, Eventos de Domínio, entre outros.&lt;/p&gt;

&lt;p&gt;Os padrões estratégicos se adaptam bem a qualquer linguagem, por isso, o foco do texto será exclusivamente no Design Tático.&lt;/p&gt;

&lt;p&gt;O TypeScript fornece suporte a classes — não tão ricas quanto no C#, mas bastante flexíveis de trabalhar. E, enquanto desenvolvia um projeto eu pensei: utilizando em uma linguagem multiparadigma, é possível trabalhar com DDD incorporando elementos da programação funcional? E a resposta é sim!&lt;/p&gt;

&lt;p&gt;Um dos meu livros de referência &lt;a href="https://a.co/d/dodW5XG" rel="noopener noreferrer"&gt;Implementando Domain-Driven Design&lt;/a&gt;, o foco do DDD Tático fica em linguagens orientadas a objetos, o que me fez buscar outras fontes. Dentre vários artigos lidos (referências ao final da página), cheguei a um projeto simples, mas bastante didático, adaptando conceitos e tentando criar algo a partir disso para quem, assim como eu, também tem essa dúvida.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entidades e Regras de Negócio
&lt;/h2&gt;

&lt;p&gt;No DDD orientado a objetos, as entidades são normalmente classes possuindo estado e comportamento. Regras e invariantes tendem a ficar dentro das classes de domínio. Esse estilo usa construtores, métodos de instância e encapsulamento por visibilidade (private). Em uma abordagem funcional, a ideia é separar os dados (tipos imutáveis) de comportamentos (funções puras). Em vez de métodos que mutam, temos funções que recebem dados e retornam novos dados ou outcomes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type BaseEntity&amp;lt;T&amp;gt; = T &amp;amp; { Id: number, IsActive: boolean };

export type User = BaseEntity&amp;lt;{
    Username: string,
    Email: string, 
    Password: string,
    PhoneNumber: string,
    Address: Adderss
}&amp;gt;;

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

&lt;/div&gt;



&lt;p&gt;Quando falamos de funções puras, um conceito importante são as Invariants.  Em matemática, um invariante é uma propriedade de um objeto matemático que permanece inalterada após a aplicação de operações ou transformações de determinado tipo sobre esse objeto. No nosso caso, esse conceito se aplica a funções pequenas (predicates) que respondem perguntas atômicas do domínio, ex.: isPasswordStrong, isAddressInCoverage&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const IsValidEmail = (email: string) : boolean =&amp;gt;  emailPattern.test(email);
export const IsValidPassWord = (password: string) : boolean =&amp;gt; passwordPattern.test(password);
export const IsUserActive = (isActive: boolean) : boolean =&amp;gt; isActive === true;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Outro conceito importante são os Derivers. Eles formam o núcleo funcional do sistema, concentrando as regras de negócio puras. São compostos por invariantes e têm como resultado um Outcome (uma discriminated union) ou um novo delta/novo. Eles não realizam I/O, não criam entidades finais e não fazem persistência.&lt;/p&gt;

&lt;p&gt;O Deriver faz apenas duas coisas (dependendo do caso):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Decidir: responde à pergunta “pode/como essa operação acontecer?” retornando um Outcome (ex.: INVALID_EMAIL, WEAK_PASSWORD, SUCCEEDED).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Derivar estado: calcular um novo estado/delta a partir dos dados (ex.: calcular passwordHash, normalizar email, calcular novo carrinho) — Transform Deriver.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const DeriveUser = async (dto: UserDtoType) : CreateUserOutcome =&amp;gt; {
    if(IsValidEmail(dto.Email))
        return { outcome: "INVALID_EMAIL" }
    if(IsValidPassWord(dto.Password))
        return { outcome: 'INVALID_PASSWORD' }
    return { 
        outcome: 'SUCCEEDED',
        payload: {
            normalizedEmail: dto.Email,
            hashedPassword: await HashPassword(dto.Password),
            username: dto.Username
        }
     }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando falo em “derivar um novo estado” não quero dizer “aplicar esse novo estado ao banco/sistema”. O Deriver retorna um valor (novo objeto em memória) que representa como o estado deveria ficar. Persistir esse novo estado é responsabilidade do Handler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outcome&lt;/strong&gt;: union discriminado que descreve todas as possibilidades de resultado. É o contrato pelo qual o Deriver comunica decisões ao Handler. Exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export type UserOutcome =
  | { outcome: "INVALID_EMAIL" }
  | { outcome: "INVALID_PASSWORD" }
  | { outcome: "INVALID_PHONE" }
  | { outcome: "INVALID_USERNAME" }
  | { outcome: "SUCCEEDED" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Delta/Novo estado&lt;/strong&gt;: objeto contendo os valores derivados (ex.: passwordHash, normalizedEmail, objetos VO prontos). Pode ser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O objeto completo que representa a entidade pronta para criar, ou&lt;/li&gt;
&lt;li&gt;Apenas as mudanças (delta) para aplicar ao estado atual.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ambas formas são puras.&lt;/p&gt;

&lt;p&gt;A partir disso, a Factory, uma função pura, recebe os dados validados/derivados (delta) e monta a entidade/VO final de forma imutável.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Orquestrador entre Domínio e Infraestrutura
&lt;/h2&gt;

&lt;p&gt;Após entendermos que o Deriver decide as regras de negócio puras, precisamos de uma camada que orquestre o caso de uso. É aqui que entra o Handler (às vezes também chamado de Use Case ou Shell).&lt;/p&gt;

&lt;p&gt;O Handler não contém regras de negócio profundas: ele coordena o fluxo de uma operação. Ele recebe um DTO já validado, chama o Deriver para obter um Outcome e, dependendo desse resultado, decide o próximo passo.&lt;/p&gt;

&lt;p&gt;Se o Deriver retornar uma falha, o Handler devolve essa falha diretamente para o controller. Se retornar um “SUCCEEDED” com um delta/novo estado, então o Handler chama a Factory para montar a entidade final e delega ao repositório o trabalho de persistência.&lt;/p&gt;

&lt;p&gt;Todo esse conhecimento adquirido se traduziu neste &lt;a href="https://github.com/oliverigor27/FP-DomainDrivenDesign/tree/master" rel="noopener noreferrer"&gt;projeto simples&lt;/a&gt;, onde busquei seguir um modelo de arquitetura Package by Feature, fazendo algumas adaptações para o cenário no qual estou mais acostumado. Para entender mais da arquitetura, deixo o ótimo artigo do Waldemar Neto que serve como guia e que eu utilizei como base para desenvolver meu projeto. &lt;/p&gt;

&lt;p&gt;Aos poucos vou me aprofundando em TypeScript e programação funcional, mas esse primeiro passo foi bem divertido e já me mostra ótimas possibilidades para o futuro.&lt;/p&gt;

&lt;p&gt;Referências:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.techleads.club/c/blog/quando-usar-arquiteturas-emergentes-package-by-feature-vertical-slice-e-modularizacao" rel="noopener noreferrer"&gt;Arquiteturas Emergentes Que Você Precisa Conhecer: Package by Feature, Vertical Slice e Modular&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.thoughtworks.com/en-br/insights/blog/architecture/domain-driven-design-in-functional-programming" rel="noopener noreferrer"&gt;Domain-driven design in functional programming&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=pHjTsP9S8r4" rel="noopener noreferrer"&gt;Domain-Driven Design (DDD) com Programação Funcional?!?!&lt;/a&gt;&lt;br&gt;
&lt;a href="https://antman-does-software.com/functional-domain-driven-design-simplified" rel="noopener noreferrer"&gt;Functional Domain Driven Design: Simplified&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.thoughtworks.com/insights/blog/microservices/ddd-implemented-fp" rel="noopener noreferrer"&gt;Domain Driven Design implemented by functional programming&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>ddd</category>
      <category>programming</category>
    </item>
    <item>
      <title>WSL2 e Docker sem Docker Desktop: configurando NAT e acesso localhost manualmente</title>
      <dc:creator>Igor Oliveira</dc:creator>
      <pubDate>Wed, 05 Nov 2025 01:16:32 +0000</pubDate>
      <link>https://forem.com/oliverigor27/wsl2-e-docker-sem-docker-desktop-configurando-nat-e-acesso-localhost-manualmente-20f</link>
      <guid>https://forem.com/oliverigor27/wsl2-e-docker-sem-docker-desktop-configurando-nat-e-acesso-localhost-manualmente-20f</guid>
      <description>&lt;p&gt;Há um tempo eu venho tentando contruir um SaaS como forma de praticar meus estudos. Durante várias ideias e tentativas, eu me deparei com um problema relacionado ao docker: por algum motivo, o Docker Desktop não queria funcionar com Windows.&lt;/p&gt;

&lt;p&gt;O que foi estranho e pareceu ser um problema simples de resolver, acabou se tornando uma dor de cabeça tremenda e me levou a buscar novas alternativas. O uso do WSL2 se mostrou como caminho, não só para resolver um problema, mas como um tópico de estudo para além da programação.&lt;/p&gt;

&lt;p&gt;O WSL2 não é uma simples compatibilidade de comandos UNIX dentro do Windows. Ele roda um kernel Linux completo em uma máquina virtual leve baseada em Hyper-V. Quando o Docker Desktop é configurado para usar o WSL2 como backend, todos os containers são criados dentro desse kernel Linux, e não diretamente no Windows.&lt;/p&gt;

&lt;p&gt;A comunicação entre o Windows e o ambiente Linux do WSL2 é feita via uma interface virtual criada pelo Windows: vEthernet (WSL). Ela é, na verdade, um Switch Virtual criado automaticamente pela plataforma de virtualização do Windows (chamada Virtual Machine Platform, um componente do Hyper-V) quando o WSL2 é iniciado pela primeira vez. Essa interface atua como única ponte de rede entre dois ambientes isolados:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O sistema operacional Windows (Host).&lt;/li&gt;
&lt;li&gt;O kernel Linux/VM do WSL2 (Guest).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O principal mecanismo que ela gerencia é o NAT (Network Address Translation). O Linux (WSL2 VM) recebe um IP privado (por exemplo, 172.27.x.x ou 192.168.x.x) que muda a cada reinicialização do Windows. Esse IP funciona como Gateway Padrão e Servidor DNS dentro do ambiente Linux, permitindo que o tráfego de saída seja traduzido para a rede do host. No lado do Windows, a interface vEthernet (WSL) recebe o primeiro endereço desse bloco privado (por exemplo, 172.27.x.1) e atua como o roteador/NAT responsável por gerenciar todo o tráfego entre o Windows e a VM Linux.&lt;/p&gt;

&lt;p&gt;Teoricamente, o Docker Desktop deveria configurar o proxy automático de portas por meio da interface vEthernet (WSL), permitindo que minha aplicação no Windows acessasse o container através do endereço localhost:1433. No entanto, como optei por rodar o Docker Engine nativo diretamente na minha distribuição WSL2, eu não contava com o serviço de encaminhamento automático de portas que o Docker Desktop injeta no host Windows.&lt;/p&gt;

&lt;p&gt;Assim, minha aplicação .NET, executando no Windows, tentava se conectar a localhost:1433. Como não havia o serviço de port forwarding do Docker Desktop, o Windows não sabia que o tráfego dessa porta deveria ser redirecionado para o endereço IP dinâmico da VM WSL2 (geralmente algo como 172.x.x.x). Por isso, a conexão falhava.&lt;/p&gt;

&lt;p&gt;Uma alternativa para contornar esse problema foi criar uma rede personalizada no Docker e expor a porta do container do SQL Server para o host Windows. Essa configuração permitiu que o tráfego de localhost:1433 fosse corretamente roteado até o container, mesmo sem o proxy de portas do Docker Desktop.&lt;/p&gt;

&lt;p&gt;O primeiro passo foi criar uma rede bridge isolada com suporte a DNS interno:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker network create mynetwork
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essa rede cria uma bridge virtual (por exemplo, br-2c7e9a5d7b45) com uma sub-rede dedicada (172.18.0.0/16) e um gateway interno (172.18.0.1).&lt;br&gt;
Containers conectados a ela podem se comunicar diretamente por nome, sem precisar de IP fixo.&lt;/p&gt;

&lt;p&gt;Em seguida, subi o container do SQL Server conectado a essa rede e expus a porta 1433:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=YourStrong!Passw0rd" \
  --name sqlserver \
  --network mynetwork \
  -p 1433:1433 \
  -d mcr.microsoft.com/mssql/server:2022-latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O parâmetro -p 1433:1433 faz o mapeamento de porta entre o host (Windows) e o container, permitindo que a aplicação fora do WSL2 acesse o serviço pelo localhost:1433.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;--network mynetwork&lt;/strong&gt; garante que, se eu decidir rodar outros containers (por exemplo, uma API .NET) dentro do WSL2, eles poderão se conectar via DNS interno — usando apenas sqlserver como host.&lt;/p&gt;

&lt;p&gt;Para confirmar a conectividade, testei o acesso diretamente do Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test-NetConnection localhost -Port 1433
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E também validei de dentro da aplicação .NET, configurando a connection string da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Server=localhost,1433;Database=MyAppDb;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após isso, a aplicação passou a se conectar normalmente ao SQL Server rodando dentro do container, mesmo com o Docker Engine isolado dentro da VM WSL2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mas, por que essa solução funciona?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ao expor a porta (-p 1433:1433), o Docker criou uma regra de DNAT (Destination NAT) na tabela iptables do Linux interno do WSL2. Essa regra instrui o kernel a redirecionar todo o tráfego recebido na porta 1433 do host (interface eth0) para o IP interno do container SQL Server.&lt;/p&gt;

&lt;p&gt;Quando o Windows acessa localhost:1433, o pacote é enviado à interface vEthernet (WSL) → traduzido pelo NAT → entregue à bridge br-xxxxx → e finalmente roteado ao container. Com isso, o comportamento esperado do Docker Desktop foi restaurado manualmente, mas de forma controlada e previsível.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>linux</category>
    </item>
    <item>
      <title>Desvendando o Three-Way Handshake - Parte 1: A Engrenagem Invisível da Comunicação TCP</title>
      <dc:creator>Igor Oliveira</dc:creator>
      <pubDate>Thu, 02 May 2024 23:33:49 +0000</pubDate>
      <link>https://forem.com/oliverigor27/desvendando-o-three-way-handshake-parte-1-a-engrenagem-invisivel-da-comunicacao-tcp-49gi</link>
      <guid>https://forem.com/oliverigor27/desvendando-o-three-way-handshake-parte-1-a-engrenagem-invisivel-da-comunicacao-tcp-49gi</guid>
      <description>&lt;p&gt;A comunicação desempenha um papel crucial no funcionamento da Internet, com destaque para o uso do TCP (Transmission Control Protocol). Localizado na camada de transporte, o TCP é essencial para a transmissão de dados pela rede, assegurando a comunicação e a transferência de informações entre sistemas de forma confiável e segura.&lt;/p&gt;

&lt;p&gt;Na estrutura Cliente-Servidor, o TCP é fundamental para facilitar a comunicação e a troca de informações. Mas como exatamente essa conexão é estabelecida? Embora saibamos que o three-way handshake é o mecanismo utilizado para essa comunicação, vamos explorar mais detalhes com base nos sistemas operacionais Unix.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Criação e Configuração do Socket:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Durante o processo de conexão, o servidor web delega a responsabilidade das conexões TCP para o kernel do sistema operacional, garantindo eficiência, confiabilidade e segurança na comunicação em rede.&lt;/p&gt;

&lt;p&gt;Quando um processo de aplicação solicita a criação de um socket de comunicação, o sistema operacional aloca recursos para esse novo socket, como memória, e cria estruturas de dados necessárias para representar o socket no nível do kernel. Em seguida, um “File Descriptor” é atribuído a esse socket.&lt;/p&gt;

&lt;p&gt;Esse File Descriptor é disponibilizado para a aplicação, que configura o socket para receber novas conexões, geralmente utilizando primeiro a chamada bind(), indicando ao sistema operacional o endereço e a porta para escutar novas conexões.&lt;/p&gt;

&lt;p&gt;Em seguida, a aplicação coloca o socket no estado de 'Listen' utilizando a chamada de sistema listen(), informando ao sistema operacional que o socket está pronto para aceitar novas conexões de entrada.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Estabelecendo conexão:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Quando o cliente deseja se conectar a um servidor, ele envia um segmento SYN (synchronize) para o servidor de destino. O kernel do sistema operacional, ao receber o segmento SYN, verifica se há um processo esperando conexão pela porta especificada no segmento. Se houver, o segmento SYN é aceito, e o kernel aloca recursos para a nova conexão, colocando as informações na SYN queue, aguardando confirmação.&lt;/p&gt;

&lt;p&gt;Após esse processo, o kernel envia um SYN/ACK para o cliente, reconhecendo o segmento recebido e enviando outro segmento SYN para que o cliente também o reconheça. Em seguida, o cliente envia um ACK, confirmando o recebimento.&lt;/p&gt;

&lt;p&gt;O recebimento desse ACK pelo kernel do servidor conclui o three-way handshake. O kernel move as informações para a Accept Queue, onde as conexões estabelecidas, mas não aceitas pela aplicação, são mantidas.&lt;/p&gt;

&lt;p&gt;Quando a conexão está no backlog, a aplicação pode utilizar a chamada de sistema accept() para aceitar a conexão específica e iniciar a troca de informações com o cliente, utilizando o file descriptor associado para interagir com o cliente por meio das chamadas read() e write().&lt;/p&gt;

&lt;p&gt;Em suma, o estabelecimento adequado das conexões TCP é crucial para a comunicação eficiente entre clientes e servidores na Internet. Através do meticuloso processo de three-way handshake e do cuidadoso gerenciamento pelo kernel do sistema operacional, as informações podem ser trocadas de forma confiável e segura. Por trás da aparente simplicidade de clicar em um link ou enviar uma mensagem, há uma intrincada dança entre o hardware, o software e os protocolos de rede. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>learning</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Entendendo HTTP - Parte 1</title>
      <dc:creator>Igor Oliveira</dc:creator>
      <pubDate>Fri, 12 Jan 2024 21:37:26 +0000</pubDate>
      <link>https://forem.com/oliverigor27/entendo-http-parte-1-2bdh</link>
      <guid>https://forem.com/oliverigor27/entendo-http-parte-1-2bdh</guid>
      <description>&lt;p&gt;Quando abordamos o desenvolvimento de aplicações para o backend, é crucial ter um entendimento sólido sobre o HTTP ao iniciar um projeto. Como o protocolo de transferência de dados mais popular na internet, o &lt;strong&gt;Hypertext Transfer Protocol&lt;/strong&gt; desempenha um papel integral em nossa experiência online há mais de duas décadas, sendo a base para as conexões entre cliente e servidor.&lt;/p&gt;

&lt;p&gt;O HTTP opera na camada sete do modelo OSI e utiliza o &lt;a href="https://pt.wikipedia.org/wiki/Protocolo_de_Controle_de_Transmiss%C3%A3o#Estabelecimento_da_conex%C3%A3o" rel="noopener noreferrer"&gt;TCP para estabelecer a conexão&lt;/a&gt; entre cliente e servidor. Na virada do século, à medida que a internet ganhava popularidade, a experiência do usuário era notavelmente diferente da que conhecemos hoje. A versão 1.0 do HTTP foi introduzida em 1996 e, naquela época, era o protocolo dominante para a transferência de informações entre cliente e servidor. No entanto, começou a manifestar algumas limitações.&lt;/p&gt;

&lt;p&gt;O HTTP/1.0 permitia apenas o processamento de uma única solicitação por conexão. Isso significava que, para cada requisição do cliente ao servidor - como imagens, scripts ou folhas de estilo - uma nova conexão precisava ser aberta. Esse processo resultava em um desempenho lento da página, aumentando a latência e tornando o carregamento do site moroso.&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%2Fdc29id2ox22k3e6rfnz3.jpg" 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%2Fdc29id2ox22k3e6rfnz3.jpg" alt="Image description" width="382" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Diante dos desafios impostos pelo HTTP/1.0, surgiu a necessidade de um protocolo mais eficiente. Em 1999, foi oficialmente introduzido o HTTP/1.1, destinado a superar as limitações de seu antecessor. A mudança mais impactante foi a introdução da persistência de conexão, conhecida como "Keep Alive", que permitia várias solicitações e respostas serem transmitidas pela mesma conexão. Isso eliminou a necessidade de abrir uma nova conexão para cada recurso, reduzindo drasticamente a latência e aprimorando o desempenho no carregamento das páginas web.&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%2Fzujvnbi3msn8ar1nn8yc.jpg" 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%2Fzujvnbi3msn8ar1nn8yc.jpg" alt="Image description" width="432" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Além disso, a versão 1.1 também introduziu a pipeline de requisições, o que permitiu o envio de várias solicitações antes de receber as respostas, otimizando ainda mais a comunicação entre clientes e servidores.&lt;/p&gt;

&lt;p&gt;Com a chegada da nova versão do protocolo, a internet passou por uma revolução. As páginas web tornaram-se mais rápidas, e a experiência do usuário melhorou significativamente. A eficiência na troca de dados contribuiu para o crescimento exponencial de serviços online, impulsionando o comércio eletrônico, redes sociais e muito mais.&lt;/p&gt;

&lt;p&gt;Em resumo, a transição do HTTP/1.0 para o HTTP/1.1 marcou um ponto de virada crucial na evolução da internet. As inovações introduzidas não apenas resolveram os problemas de latência e ineficiência, mas também pavimentaram o caminho para o desenvolvimento contínuo da World Wide Web.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Entendendo Herança e Composição</title>
      <dc:creator>Igor Oliveira</dc:creator>
      <pubDate>Mon, 27 Feb 2023 20:15:33 +0000</pubDate>
      <link>https://forem.com/oliverigor27/entendendo-heranca-e-composicao-3ehc</link>
      <guid>https://forem.com/oliverigor27/entendendo-heranca-e-composicao-3ehc</guid>
      <description>&lt;p&gt;A programação orientada a objetos é uma parte importante e muitas vezes necessária na formação de um programador. Um dos conceitos fundamentais dessa abordagem é a herança, que nos permite reutilizar código em nossos projetos, dentre outras coisas. No entanto, além da herança, existe outro conceito básico que pode ajudar no desenvolvimento de aplicações, a composição. Mas qual a diferença entre herança e composição? A diferença está na forma como uma classe interage com outras classes dentro do código.&lt;/p&gt;

&lt;p&gt;A herança é um mecanismo em POO onde uma classe herda propriedades e métodos e outra classe permitindo a criação de hierarquia de classes, onde uma classe filho (subclasse) é derivada de uma classe mais geral, ou classe pai (Superclasse):&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%2Fhgj83i9ehbnjqwb3l7pd.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%2Fhgj83i9ehbnjqwb3l7pd.png" alt="Exemplo de herança" width="800" height="865"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No exemplo acima, a classe 'Gato' herda todos os atributos e métodos da classe 'Animal' além de possuir seu próprio atributo e método chamado "raça".&lt;/p&gt;

&lt;p&gt;Pro outro lado, a composição é um mecanismo em POO que cria uma relação do tipo 'tem-um' com outra classe:&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%2F4b9it6mz546q7a8aaezo.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%2F4b9it6mz546q7a8aaezo.png" alt="Image Exemplo de composicao" width="800" height="1529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No exemplo acima, temos uma classe chamada 'Motor' e logo em seguida uma classe chamada 'Carro'. O que é importante notar aqui é que a classe 'Carro' não estende a classe 'Motor', mas sim cria uma instância da mesma para ser um de seus membros. Assim, a classe 'Carro' terá seus próprios métodos e também poderá acessar os métodos da classe 'Motor' por meio dessa instância criada.&lt;/p&gt;

&lt;p&gt;Entre os exemplos do uso de herança, podemos citar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reutilização de código através de uma classe já existente.&lt;/li&gt;
&lt;li&gt;A criação de uma classe genérica que pode ser usada como base para outras classes.&lt;/li&gt;
&lt;li&gt;Definir uma hierarquia de classes dentro do código.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Já os exemplos de uso da composição, podemos citar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A criação de objetos complexos através de objetos mais simples.&lt;/li&gt;
&lt;li&gt;A possibilidade de combinar funcionalidades de classes diferentes em um único objeto.&lt;/li&gt;
&lt;li&gt;Permitir a reutilização de código sem a necessidade de criar hierarquias de classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A principal diferença entre herança e composição é que a herança é uma relação do tipo "é-um", enquanto a composição é uma relação do tipo "tem-um". Em outras palavras, na herança, uma classe é um tipo de outra classe (ex: Gato é um tipo de Animal), enquanto na composição, uma classe possui objetos de outras classes (ex: Carro tem um Motor).&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Estrutura de Dados parte 2: Algoritmos de busca.</title>
      <dc:creator>Igor Oliveira</dc:creator>
      <pubDate>Fri, 17 Feb 2023 19:54:15 +0000</pubDate>
      <link>https://forem.com/oliverigor27/estrutura-de-dados-parte-2-algoritimos-de-busca-3kg1</link>
      <guid>https://forem.com/oliverigor27/estrutura-de-dados-parte-2-algoritimos-de-busca-3kg1</guid>
      <description>&lt;p&gt;Um dos conceitos mais importantes dentro da ciência da computação atualmente se refere à busca de dados dentro de uma estrutura. Com o número de informações disponíveis na rede, a busca eficiente por um elemento se tornou parte importante do trabalho de um desenvolvedor de software. Diante disso, surgem dois principais métodos de busca: o algorítimo de busca sequencial e o algoritmo de busca binária.&lt;/p&gt;

&lt;h2&gt;
  
  
  Busca Sequencial
&lt;/h2&gt;

&lt;p&gt;Como o próprio nome já sugere, o algorítimo de busca sequencial — podendo ser chamado também de busca linear — é uma estrutura de dados que percorre cada elemento de um vetor até encontrar o valor desejado. O algorítimo sequencial possui complexidade O(n), ou seja: apesar de funcionar para vetores com poucos elementos, o mesmo pode se tornar custoso na medida em que o número de elementos dentro do vetor cresce, tendo seu pior caso o enésimo elemento de um determinado array: A = {0... N-1}.&lt;/p&gt;

&lt;p&gt;Veja o exemplo de um algoritmo de busca linear abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bfryfy4kdk2ahjvtt53.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%2F1bfryfy4kdk2ahjvtt53.png" alt="Imagem mostrando o código para executar o algoritmo de busca linear" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;É importante notar no código acima que, apesar do vetor estar desorganizado, isso não altera o retorno da função. Isso ocorre porque o algoritmo de busca sequencial independe do vetor estar ordenado para funcionar!&lt;/p&gt;

&lt;h2&gt;
  
  
  Busca Binária
&lt;/h2&gt;

&lt;p&gt;Se a busca sequencial pode se tornar custosa a medida em que o número de elementos dentro de um vetor cresce, o algoritmo de busca binária surge como uma maneira mais eficiente de encontrar um elemento dentro de um array. &lt;/p&gt;

&lt;p&gt;Tendo seu tempo de execução &lt;strong&gt;O(log n)&lt;/strong&gt;, o algoritmo funciona verificando o item que está no meio do vetor, e caso não seja o valor correto, podemos eliminar uma das metades do array. A partir disso, ele divide repetidamente a metade que possivelmente contem o item, até que a lista seja reduzida somente em um elemento.&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%2Fjvagq6sv2tk91skhn0ek.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%2Fjvagq6sv2tk91skhn0ek.png" alt="Imagem mostrando código para executar o algoritmo de busca binária" width="800" height="654"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dividindo o número de elementos presentes no vetor a cada execução, o algoritmo binário diminui o número de interações necessárias para encontrar o elemento desejado, tornando-o assim mais eficiente que o algoritmo linear.&lt;/p&gt;

&lt;p&gt;Abaixo uma comparação gráfica entre os dois:&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%2F0ds698z3ats17bhs21pz.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%2F0ds698z3ats17bhs21pz.png" alt="Gráfico demonstrando a eficiência de dos algoritmos" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Por levar em consideração os maiores e menores números de um vetor para fazer a divisão, o algorítimo de busca binária obrigatoriamente precisa de um array ordenado para funcionar. Por conta disso, pode ser necessária a utilização de um algoritmo de ordenação antes de utilizá-lo. &lt;/p&gt;

</description>
      <category>welcome</category>
    </item>
    <item>
      <title>Estrutura de dados parte 1: Vetores e Matrizes.</title>
      <dc:creator>Igor Oliveira</dc:creator>
      <pubDate>Tue, 06 Sep 2022 20:41:39 +0000</pubDate>
      <link>https://forem.com/oliverigor27/estrutura-de-dados-parte-1-vetores-e-matrizes-2i0f</link>
      <guid>https://forem.com/oliverigor27/estrutura-de-dados-parte-1-vetores-e-matrizes-2i0f</guid>
      <description>&lt;p&gt;Essa serie de textos faz parte dos meus estudos da faculdade referentes à matéria de pesquisa, orientação e técnicas de armazenamento de dados. Uma boa maneira de por em prática o que se foi aprendido é compartilhar esse conhecimento com mais pessoas. &lt;/p&gt;

&lt;p&gt;Esse primeiro texto abordará as estruturas de dados simples como vetores e matrizes.&lt;/p&gt;

&lt;p&gt;Quando falamos de computação, principalmente sobre armazenamento, basicamente estamos falando de dois tipos: o armazenamento interno e o armazenamento externo. De maneira bem simples: Armazenamento externo pode se caracterizar como o tipo de armazenamento que diz respeito aos dados que são guardados em aplicações ou dispositivos fora da aplicação, como um banco de dados ou HD. Já o armazenamento interno diz respeito aos dados que são armazenados dentro da própria aplicação, podendo ser acessados através de instruções diretas do programa e de acordo com a lógica necessária. O armazenamento interno tem como estrutura elementar uma variável. &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%2F8uvjj25d3q9i9nzdiysi.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%2F8uvjj25d3q9i9nzdiysi.png" alt="Imagem mostrando uma variável" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A variável é um espaço em memória responsável por alocar uma informação dentro da aplicação, seja ela um inteiro, uma string ou um booleano, sendo possível recuperá-la através de seu nome identificador. Por ser capaz de guardar somente uma informação dentro de si, a variável acaba sendo uma estrutura inviável para se trabalhar com uma grande quantidade da dados. Por exemplo: imagine que você tenha seis pessoas e precise armazenar o nome delas, seria inviável em uma aplicação criar seis variáveis para armazenar cada um desses seis nomes, além de dar muito mais trabalho para tratar esses dados futuramente.&lt;/p&gt;

&lt;p&gt;Por conta disso nós utilizamos vetores. Vetores são uma forma eficiente e indexável de armazenamento de dados em uma aplicação, permitindo o armazenamento de mais de um dado em uma mesma estrutura, podendo assim economizar código atribuindo à uma única variável diversos dados.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tg2s5urgqcvcq72dygd.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%2F8tg2s5urgqcvcq72dygd.png" alt="Imagem explicando um vetor" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cada um dos nomes armazenados dentro desse vetor possuem um respectivo índice de identificação que começa do 0 e vai até N, sendo N o número total de elementos presentes no vetor.&lt;/p&gt;

&lt;p&gt;Normalmente vetores apenas armazenam dados do mesmo tipo, ou seja, um vetor de inteiros só armazena números inteiros. Entretanto, no JavaScript é possível armazenar dados de diferentes tipos em um mesmo vetor:&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%2F7fu30enie9zx8uosgxs0.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%2F7fu30enie9zx8uosgxs0.png" alt="Image description" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Além do vetor, nós também trabalhamos com as matrizes. Uma matriz é uma estrutura computacional bidimensional, capaz de armazenar dados de maneira em que os mesmos sejam guardados como uma tabela. Assim como nos vetores o acesso aos elementos presentes nas matrizes se da através de índices, mas nesse caso, utilizando as coordenadas da linha e coluna para indicar exatamente onde esse elemento se encontra.&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%2F5ml5k3wb5yrsop37uu5u.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%2F5ml5k3wb5yrsop37uu5u.png" alt="Image mostrando uma matriz de dados e como acessar os dados presentes nessamatriz" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dentro da memória, os vetores são armazenados de maneira similar à uma variável, tendo de diferente apenas que sua referência indexada aponta para mais de um espaço em memória.&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%2F8qs8iv8pd6h2za6ch6ww.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%2F8qs8iv8pd6h2za6ch6ww.png" alt="Imagem explicando a alocação de memória de uma variável e um vetor" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>algorithms</category>
      <category>estruturadedado</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
