Engenheiro de Software Sênior • Fullstack & Inteligência Artificial
Java & Spring BootNode.js & ReactIA Generativa & SDDCinnamon CSS
🚀
Engenharia Full Stack & Alta Complexidade
Aplicações escaláveis e resilientes com Java (Spring Boot), Node.js (NestJS/Express) e React.
🏛️
Arquitetura de Microsserviços & BFFs
Concepção ponta a ponta, Micro Frontends, mensageria, observabilidade e modernização tecnológica.
🤖
IA Generativa no Ciclo de Desenvolvimento
Spec-Driven Development, Kiro, Amazon Q, agentes especializados e skills customizadas.
🌿
Open Source & Cinnamon CSS
Criador da biblioteca Cinnamon CSS, trazendo a estética do Linux Mint para a web.
Sou Engenheiro de Software Sênior, com sólida experiência no desenvolvimento, evolução e modernização de aplicações escaláveis e de alta disponibilidade, atuando como Fullstack em ambientes de alta complexidade. Tenho experiência com Java (Spring Boot), Node.js (NestJS/Express) e React, com forte atuação em arquitetura de microsserviços, BFFs (Backend for Frontend) e Micro Frontends. Atuo desde a concepção e arquitetura até a implementação e sustentação de soluções, incluindo criação de serviços do zero, definição de padrões, observabilidade, integrações via APIs REST, processamento assíncrono, mensageria e estratégias de modernização tecnológica, incluindo evolução para versões mais recentes do Java.
Tenho forte atuação na aplicação de Inteligência Artificial Generativa ao ciclo de desenvolvimento, utilizando Spec-Driven Development, Kiro e Amazon Q. Desenvolvo e utilizo skills customizadas e agentes especializados para automatizar atividades de desenvolvimento, testes de integração entre microsserviços, publicação e consumo de mensagens, validação de fluxos ponta a ponta e migração e transformação de sistemas. Busco combinar visão arquitetural, excelência técnica e inovação para construir soluções robustas e sustentáveis, promovendo automação, melhoria contínua, qualidade de engenharia e colaboração entre equipes.
Como as Functional Interfaces se Aplicam ao Design Orientado a Objetos.
Abrir janela do artigo →Document Viewer
17 de abr. de 2026como-começar-terminar
Como começar a terminar?
Não seria essa a grande pergunta?
Abrir janela do artigo →Document Viewer
27 de mar. de 2026sliding-window-technique
Sliding Window Technique
The sliding window is a method that allows you to process a subset of elements, referred to as a window, and move it across the data structure in a way that reuses previous computations instead of recalculating everything from scratch.
Abrir janela do artigo →Document Viewer
02 de fev. de 2026mfe-strategies
Microfrontends com importmaps, iframe ou SingleSPA?
Com o crescimento de aplicações web complexas, surge a necessidade de escalar equipas e tecnologias de forma independente.
Abrir janela do artigo →Document Viewer
20 de jul. de 2025jornada-open-source
Minha jornada de "full open-source"
Adotar o modelo full open-source vai além de apenas usar software gratuito. Se trata de um compromisso com a transparência, colaboração, auditabilidade e autonomia tecnológica. A seguir, vou comentar os principais benefícios que notei, ferramentas recomendadas e boas substituições de tecnologias proprietárias por soluções abertas.
Abrir janela do artigo →Document Viewer
08 de fev. de 2025conhecendo-csp
Conhecendo Content Security Policy (CSP)
A segurança na web é uma preocupação constante. Todos os dias, milhares de sites são comprometidos por ataques como Cross-Site Scripting (XSS), permitindo que invasores executem scripts maliciosos em páginas legítimas.
Abrir janela do artigo →Document Viewer
02 de mai. de 2024c4-model
C4 Model - Document software at scale
The C4 model (Context, Containers, Components, Classes) is an approach for modeling and documenting software architecture proposed by Simon Brown. It provides a scalable and hierarchical framework for describing software systems at different levels of abstraction.
Abrir janela do artigo →Document Viewer
04 de abr. de 2024arquitetura-bff-beneficios
Arquitetura de BFF e seus benefícios
A arquitetura de BFF (Backends For Frontends) é uma abordagem que permite aos desenvolvedores frontend terem mais autonomia na criação de seus projetos. Isso é especialmente útil na maneira como eles irão agregar os endpoints.
Abrir janela do artigo →Document Viewer
28 de nov. de 2023projecao-conteudo-frontend
Projeção de conteúdo no Front-end
projeção de conteúdo é uma forma de um componente utilizar dados/estruturas vindas de um outro componente, com isso nós temos muito poder de criação e customização de componentes.
Abrir janela do artigo →Document Viewer
26 de mar. de 2023testando-com-marble-diagrams
Testando RxJS com marble diagrams
Quando estamos utilizando Observables sempre há uma dificuldade em como organizar os testes para que possamos entender e testar toda a nossa stream. Podemos testar da forma mais “simples” utilizando subscribes para validar os valores da nossa subscription, porém temos uma outra abordagem para testes reativos e os mesmos podem ser feitos com um Scheduler nativo da biblioteca.
Abrir janela do artigo →Document Viewer
02 de ago. de 2021js-es6-keyed-collections
JavaScript ES6 keyed collections
JavaScript has some built-in structures introduced on es6 version, even though these data structures have some time of existence has many developers has doubt about how to use them, today I wanna try to clarify the information about these.
Abrir janela do artigo →Document Viewer
08 de fev. de 2021js-module-pattern
JavaScript Module Pattern
It's a JavaScript pattern, that allows us to create modules with more lexical scope for our functions, create functions for executing simple tasks and immediately execute and help with code organization too.
Abrir janela do artigo →Document Viewer
23 de set. de 2020angular-http-interceptors
Angular HttpInterceptors ajudando no desenvolvimento mockado
Angular é um framework e uma plataforma para o desenvolvimento web, focado na modularização do código utilizando módulos ou ngModules para isso.
Abrir janela do artigo →Document Viewer
Nenhum artigo encontrado para "".
Total: 14 artigos carregadosClique em qualquer artigo para abrir em uma janela separada
⚙️Aparência & Temas
Configurações do Sistema
☀️
Modo Escuro (Dark Theme)
Alternar entre tema visual claro e escuro do Cinnamon
Cor de Destaque (Mint Accents)
Selecione a paleta de acentuação oficial do Linux Mint:
🌿
Linux Mint Cinnamon (Web OS)
Ambiente de desktop simulado com a biblioteca oficial Cinnamon CSS.
Versão do Cinnamon:6.2 (Wilma Edition)
Biblioteca de Estilos:cinnamon-css 0.0.1
Framework Web:Astro 7.0 + React 19
Desenvolvedor:Guilherme Gules Moreira
Aparência: Mint-Y Design SystemLinux Mint Cinnamon
Double spend e double submit são, na prática, variações do mesmo problema: a mesma ação sendo processada mais de uma vez.
A diferença entre eles está muito mais no contexto do que na essência:
Double spend aparece com mais frequência em pagamentos e transações financeiras
Double submit é mais comum em formulários e interações de interface
Mas no fundo, ambos representam uma falha de controle sobre idempotência e consistência.
Como isso acontece?
Esse tipo de problema é mais comum do que parece e nem sempre envolve má intenção.
Alguns cenários clássicos:
O usuário clica duas vezes no botão “Enviar”
O frontend dispara o mesmo request mais de uma vez (bug ou race condition)
Existe um mecanismo de retry automático (timeout, falha de rede, etc.)
Requests idênticos são enviados em paralelo (inclusive de forma maliciosa)
O resultado disso é o sistema acabar criando informações em duplicidade quando deveria existir apenas uma.
O erro comum: tentar resolver em um único lugar
Uma armadilha comum é tratar esse problema como se tivesse uma solução única.
Na prática, não existe bala de prata aqui.
A abordagem correta é trabalhar com camadas de proteção.
Camada 1: Frontend (prevenção de erro humano)
No frontend, o foco é melhorar a experiência do usuário e evitar ações duplicadas acidentais.
Algumas estratégias:
Desabilitar o botão após o primeiro clique
Exibir estado de loading
Bloquear novos submits enquanto a requisição está em andamento
Isso reduz bastante erros involuntários.
Mas tem um limite importante, frontend não é uma fonte confiável de verdade.
Ele pode ser ignorado, manipulado ou completamente bypassado.
Camada 2: Backend (idempotência)
Aqui é o coração da solução.
Idempotência significa que, ao executar a mesma operação múltiplas vezes produz o mesmo resultado que executá-la apenas uma vez.
Na prática, o backend precisa ser capaz de responder essa pergunta: “Eu já processei esse request antes?”
Como fazer isso?
A estratégia mais comum é o uso de chaves de idempotência.
Essa chave pode ser enviada pelo client (ex: UUID por ação) ou gerada pelo servidor com base nos dados da operação.
Cliente vs Servidor
Cliente: mais simples, mas depende de confiança
Servidor: mais robusto, pois define claramente o que é “a mesma ação”
Em sistemas críticos, a geração no servidor tende a ser a melhor escolha.
Camada 3: Banco de dados
Mesmo com idempotência, o banco ainda é uma linha de defesa essencial.
Aqui devemos utilizar constraints de unicidade.
Exemplos:
Um email único em uma newsletter
Um order_id único em compras
Um transaction_id único em pagamentos
Mesmo que dois requests escapem das camadas anteriores, o banco impede a duplicação.
Conclusão
Double submit/double spend não são problemas isolados são sintomas de sistemas que não tratam corretamente a repetição de ações.
Se você está lidando com qualquer tipo de operação crítica, devemos olhar para os sintomas, focar em corrigir e blindar o sistema contra esses problemas na causa raiz.
Historicamente, o Java foi construído sobre uma base estrita de Orientação a Objetos onde “tudo é um objeto”. Até o Java 7, quando precisávamos encapsular uma lógica para ser executada em outro ponto do sistema, como um filtro de lista ou um listener de eventos, uma abordagem era utilizar Classes Anônimas.
Embora funcional, essa abordagem trazia uma carga cognitiva e sintática pesada:
Collections.sort(usuarios, new Comparator<Usuario>() { @Override public int compare(Usuario u1, Usuario u2) { return u1.getNome().compareTo(u2.getNome()); }});
Nesse modelo, o que realmente importava era apenas a expressão dentro do método compare, mas o desenvolvedor era forçado a lidar com o “boilerplate” de instanciar uma classe e sobrescrever o método.
Composição > Verbosidade
A introdução das Interfaces Funcionais no Java 8 não representou um abandono da Orientação a Objetos, mas sim uma evolução em direção ao design híbrido. A tese central é que a Programação Funcional (FP) provê ao OOP ferramentas superiores de composição.
Enquanto a herança clássica tende a criar hierarquias rígidas, o uso de interfaces funcionais permite que o comportamento seja tratado como um dado: ele pode ser passado como parâmetro, retornado por métodos e, principalmente, combinado. Isso reduz o acoplamento e favorece o princípio de responsabilidade única, permitindo que objetos foquem em estado enquanto as interfaces funcionais definem a estratégia de execução.
Single Abstract Method (SAM)
O alicerce dessa mudança é o conceito de SAM (Single Abstract Method). Para o compilador do Java, qualquer interface que possua exatamente um método abstrato é considerada uma Interface Funcional.
Diferente de interfaces complexas que definem múltiplos comportamentos (como List ou Map), as SAMs focam em uma única intenção. A anotação @FunctionalInterface serve como um contrato de design:
Validação em tempo de compilação: Garante que a interface não terá acidentalmente um segundo método abstrato inserido no futuro.
Clareza Semântica: Indica para outros desenvolvedores que aquela interface é destinada a ser utilizada via Lambdas ou Method References.
É importante notar que métodos default ou métodos static dentro da interface não invalidam sua natureza funcional, pois eles possuem implementação. O critério reside estritamente na existência de apenas um método que demande implementação futura.
Conclusão
A introdução de interfaces funcionais não eliminou a Orientação a Objetos no Java ela removeu pontos que geravam mais problema no como do que no porque. O que antes exigia estruturas verbosas passou a ser expresso de forma direta e com encademaneto de funções que podem ser compostas.
Retomando o exemplo inicial, podemos reescrevê-lo utilizando uma interface funcional já existente na API (Comparator), explorando lambdas e method references:
Nesse formato, a intenção do código se torna evidente: estamos apenas definindo como comparar, sem nos preocupar com como estruturar essa lógica.
Não estamos apenas reduzindo linhas de código, essa abordagem reforça um princípio importante: comportamento deve ser simples de declarar, fácil de combinar e natural de reutilizar.
É nesse ponto que a programação funcional deixa de ser um paradigma separado e passa a atuar como uma extensão prática da própria orientação a objetos no Java moderno.
Nós não sofremos por falta de ideias mas sofremos por excesso de começos ainda mais na era da informação com a quantidade absurda de dados que são jogados em nós todos os dias.
Todo mundo começa coisas: projetos, hábitos, planos, cursos, metas. A empolgação inicial vem fácil. O problema é o que acontece depois. Com o tempo, surgem distrações, novas ideias mais interessantes, urgências do dia a dia… e aquilo que parecia importante vai ficando para trás.
No fim, sobra uma coleção de coisas inacabadas e a sensação de estar sempre ocupado e sempre no mesmo lugar sem sentir avanço seja na vida profissional, academica ou até pessoal.
Foi aí que uma ideia simples, popularizada no livro A Única Coisa, do Gary Keller, me fez refletir e buscar, sem perfeição, onde estava errando por sempre sentir que faltava algo. Após a leitura, algumas coisas ficaram claras, não importa o volume de coisas que temos para fazer mas sim o que vamos priorizar, de preferência uma coisa por vez.
A pergunta central é direta e desconfortável: qual é a única coisa que, se feita, torna todo o resto mais fácil ou até desnecessário?
Terminar não depende tanto de força de vontade quanto a gente imagina. Depende de clareza. Porque quando tudo parece importante, nada realmente é. E quando nada é prioridade, tudo fica pela metade.
A verdade é que não terminamos porque tentamos fazer coisas demais ao mesmo tempo. Acreditamos que somos bons em multitarefa, mas na prática só estamos dividindo nossa atenção. Começar é estimulante traz novidade, sensação de progresso. Já terminar exige repetição, foco e, muitas vezes, enfrentar o tédio e a nossa busca inevitável pela perfeição.
E é justamente aí que a maioria desiste. Se você quer começar a terminar, precisa inverter a lógica.
Primeiro, foque em uma única prioridade. Não três, não cinco. Uma. Algo que realmente mova você para frente. Algo que, se avançar, puxa o resto junto.
Depois, pare de pensar no projeto inteiro e foque na próxima ação. Terminar não é um grande evento é uma sequência de pequenos passos executados com consistência.
Em seguida, proteja tempo para isso. Não o tempo que sobra, mas o tempo que você decide que é intocável, aquele bloco de tempo que não pode ou não deve ser perdido.
E, por fim, aceite o desconforto. Terminar quase nunca é tão empolgante quanto começar. Mas é no final que as coisas ganham valor. É ver que seu esforço valeu a pena ainda mais se você conseguir aproveitar a viagem e os momentos que te levaram até a conclusão.
No fim das contas, ideias são comuns. Começos são comuns. Estar ocupado é comum.
O que é raro e valioso é terminar e ver o seu progresso.
In algorithms, efficiency often comes down to avoiding unnecessary repetition.
Many problems involving arrays or strings require analyzing contiguous segments of data, and a naive approach typically leads to redundant computations.
This is where the sliding window technique becomes powerful.
The sliding window is a method that allows you to process a subset of elements, referred to as a window, and move it across the data structure in a way that reuses previous computations
instead of recalculating everything from scratch.
Why it Matters
Consider a simple problem, finding the maximum sum of a subarray of size k. A brute-force approach would compute the sum of every possible subarray of length k, resulting in a time complexity of O(n²).
The sliding window approach improves this dramatically. By maintaining a running sum and updating it as the window moves, the same problem can be solved in O(n) time.
This optimization makes the technique especially useful in performance-critical applications and coding interviews.
Core idea
At its core, the sliding window technique involves maintaining a range of elements and adjusting that range as you iterate through the data.
Instead of restarting calculations for each new position:
You add the next element entering the window
You remove the element leaving the window
This incremental update is what makes the method efficient.
Fixed-Size Window
In this variation, the window size remains constant throughout the process.
Example: Maximum sum of a subarray of size k
function maxSum(arr, k) { let windowSum = 0; for (let i = 0; i < k; i++) { windowSum += arr[i]; } let maxFound = windowSum; for (let i = k; i < arr.length; i++) { windowSum += arr[i] - arr[i - k]; maxFound = Math.max(maxFound, windowSum); } return maxFound;}
Here, the window slides one position at a time. Each step involves adding a new element and removing the oldest one, ensuring the window size stays fixed.
Variable-Size Window
In many problems, the window size is not fixed and must adjust dynamically based on a condition.
Example: Smallest subarray with sum ≥ target
function minSubarrayLen(target, arr) { let left = 0; let currentSum = 0; let minLen = Infinity; for (let right = 0; right < arr.length; right++) { currentSum += arr[right]; // Shrink window while condition is satisfied while (currentSum >= target) { minLen = Math.min(minLen, right - left + 1); currentSum -= arr[left]; left++; } } return minLen === Infinity ? 0 : minLen;}
In this case:
The window expands by moving the right pointer
It shrinks by moving the left pointer when the condition is satisfied
When to Use
The sliding window technique is most effective when dealing with:
Most sliding window solutions follow a similar structure:
// Pseudo codeleft = 0for right in range(n): # expand window while condition_not_valid: # shrink window left += 1
This pattern provides a flexible framework for solving a wide range of problems.
Big(O) Advantage
One of the biggest strengths of the sliding window technique is its efficiency.
By ensuring that each element is processed at most twice (once when entering the window and once when leaving), the overall time complexity is typically O(n).
This is a significant improvement over brute-force methods, which often operate in quadratic time.
Intuition
A helpful way to visualize the sliding window is to imagine looking out of a moving train window. As the train moves forward, your view changes gradually; you don’t “reset” your perspective at every second. Instead, you continuously adjust what you see. Similarly, the sliding window maintains continuity, updating only the changes as it moves through the data.
Conclusion
The sliding window technique is a fundamental tool for efficiently solving problems involving contiguous data. By reducing redundant computation and leveraging incremental updates, it transforms expensive algorithms into linear-time solutions. Understanding this technique not only improves problem-solving speed but also deepens your grasp of algorithmic optimization.
Introdução
Com o crescimento de aplicações web complexas, surge a necessidade de escalar equipas e tecnologias de forma independente. Os microfrontends vêm como uma solução arquitetónica que permite a separação do frontend em múltiplos módulos autónomos, semelhantes aos microserviços no backend.
Este artigo explora três abordagens populares para implementar microfrontends: Import Maps, iFrames e SingleSPA.
O Que São Microfrontends?
Microfrontends são uma forma de dividir uma aplicação frontend monolítica em partes menores, permitindo que equipas diferentes desenvolvam, implementem e escalem funcionalidades de forma independente.
1. Import Maps
Import maps permitem especificar mapeamentos de módulos JavaScript diretamente no HTML, facilitando o carregamento de scripts hospedados separadamente.
Vantagens:
Necessidade de integração com frameworks diferentes
Aplicações SPA com navegação complexa
Conclusão
Microfrontends não são uma solução universal, mas sim uma decisão arquitetónica que deve ser tomada com base no contexto do produto, da equipe e dos requisitos técnicos.
As abordagens apresentadas Import Maps, iFrames e SingleSPA, resolvem o mesmo problema central (desacoplamento e escalabilidade do frontend), porém com trade-offs bem diferentes.
Import Maps destacam-se pela simplicidade e aderência aos padrões modernos da web, sendo ideais para cenários em que se deseja baixo overhead e integração direta entre módulos. iFrames, apesar das limitações de comunicação e performance, continuam relevantes quando o isolamento total e a segurança são prioridades. Já o SingleSPA oferece uma solução mais robusta e madura para aplicações SPA complexas, especialmente quando há múltiplos frameworks e equipas a coexistirem no mesmo ecossistema.
A escolha da abordagem correta depende do nível de isolamento necessário, da complexidade da navegação, da diversidade tecnológica e da maturidade do time.
Adotar o modelo full open-source vai além de apenas usar software gratuito. Se trata de um compromisso com a transparência, colaboração, auditabilidade e autonomia tecnológica. A seguir, vou comentar os principais benefícios que notei, ferramentas recomendadas e boas substituições de tecnologias proprietárias por soluções abertas.
Esse artigo destaca um pouco do experimento que venho fazendo a nível pessoal para utilizar novas ferramentas minha principal ideia aqui é mostrar um pouco dessas ferramentas que podem ser desconhecida
Minha experiencia virando “full open-source”
Depois de assistir a um vídeo do PewDiePie sobre como ele estava migrando para ferramentas de código aberto, resolvi seguir o mesmo caminho. Comecei a pesquisar alternativas livres para tudo que uso no cotidiano. Desde edição de código até e-mail, produtividade e fui substituindo aos poucos.
O resultado foi melhor do que o esperado: encontrei soluções open-source que não só cobriram minhas necessidades, mas em muitos casos entregaram mais controle, performance e privacidade do que as ferramentas fechadas que usava antes.
Ferramentas
As ferramentas que listo aqui são as que testei e adotei no meu fluxo de trabalho pessoal. Algumas funcionam melhor em ambientes self-hosted (ou somente self-hosted), o que garante total controle e privacidade. Mesmo assim, muitas delas oferecem versões hospedadas confiáveis e acessíveis.
Vale destacar: você não precisa fazer tudo de uma vez. Migrar pode (e deve) ser um processo gradual. Comece substituindo o que for mais simples ou crítico para você e vá evoluindo a stack conforme se sentir confortável.
💡 Hoje é totalmente viável usar ferramentas open-source em toda a jornada do desenvolvimento ao deploy, além de ser possível conhecer novos produtos e comunidades.
VS Code (VSCodium)
Versão open-source do Visual Studio Code, sem o rastreamento e as extensões proprietárias adicionadas pela Microsoft. Oferece a mesma experiência de uso do VS Code tradicional, mas com foco em privacidade e transparência.
Editor de texto minimalista e altamente extensível, pensado para desenvolvedores que buscam velocidade, eficiência e personalização. É uma evolução do Vim, com melhorias em performance, plugins e integração com linguagens modernas.
Framework serverless open-source que permite rodar funções como serviços usando Kubernetes ou Docker, funcionando como uma alternativa self-hosted ao AWS Lambda.
Plataforma self-hosted que facilita o deploy de aplicações de forma simples e automatizada. Funciona como uma alternativa open-source ao Render ou Heroku, permitindo hospedar seus próprios apps, bancos de dados e serviços.
Plataforma open-source que transforma seu servidor em uma nuvem privada, funcionando como uma alternativa ao Google Drive, Dropbox ou Microsoft OneDrive, mas com controle total dos dados.
Ferramenta de CI/CD leve e minimalista, ideal para quem busca pipelines simples e fáceis de manter. Baseado em YAML e inspirado no Drone CI, ele é totalmente open-source e pensado para auto-hospedagem.
Organizador pessoal que funciona com arquivos em Markdown, permitindo criar e gerenciar notas simples e rápidas. Seu principal diferencial são os backlinks (ligações entre notas) e a visualização em grafo, que mostram como suas ideias se conectam.
Ferramenta minimalista para anotações rápidas e organização de ideias, com sincronização automática entre dispositivos. Seu foco está na simplicidade: apenas texto, sem distrações.
É um serviço de e-mail focado em privacidade e segurança, oferecendo criptografia de ponta-a-ponta e política de zero rastreamento. Desenvolvido por cientistas do CERN, o Proton Mail é uma boa ideal para quem busca proteger suas comunicações contra interceptações e coleta de dados. Além de serviço de e-mail o Proton também oferece outros serviços como calendário, armazenamento e outros no suíte.
é uma ferramenta open-source para gestão de projetos ágeis, pode ser utilizada por equipes de desenvolvimento de software, design e outros segmentos criativos. Possui uma interface simples e bastante recursos para equipes ou até para controle de projetos particulares.
💡 Dica: Com o Bitwarden (App Authenticator), é possível armazenar também tokens de autenticação TOTP, substituindo apps como Google Authenticator ou Authy, mantendo tudo sincronizado de forma segura.
Inteligência Artificial
DeepSeek LLM open-source de alto desempenho com foco em tarefas de codificação, linguagem natural e raciocínio lógico.
Grok e LM Studio ainda estou testando e parecem ser muito interessantes principalmente o LM studio para executar LLMs localmente.
Conclusão
Nem tudo são flores para migrar para o mundo open-source tem seus desafios, bugs, interfaces menos polidas e curva de aprendizado. Mas, na maioria dos casos, a comunidade é ativa, colaborativa e dedicada a melhorar continuamente os ecossistemas.
Foi uma ótima experiência e que pretendo continuar explorando. Se você busca mais controle, privacidade e aprendizado profundo sobre as ferramentas que usa, recomendo tentar esse caminho.
Se tiver sugestões de outras ferramentas ou quiser trocar experiências, bora conversar!
A segurança na web é uma preocupação constante. Todos os dias, milhares de sites são comprometidos por ataques como Cross-Site Scripting (XSS), permitindo que invasores executem scripts maliciosos em páginas legítimas.
Imagine um cenário onde um usuário acessa um site confiável, mas um script injetado rouba seus dados de login e os envia para um atacante. Infelizmente, esse tipo de ataque ainda é comum e pode ter consequências graves.
Uma forma eficaz de mitigar ataques XSS e proteger aplicações web é a implementação do Content Security Policy (CSP).
O que é XSS?
XSS ou Cross site Scripting é um tipo de vulnerabilidade de segurança em aplicações web que permite a injeção de scripts maliciosos em páginas visualizadas por outros usuários. Esses scripts podem roubar informações sensíveis, como cookies de sessão, redirecionar usuários para sites maliciosos, modificar o conteúdo da página e até mesmo realizar ações em nome do usuário sem seu consentimento.
O que é CSP?
Content Security Policy (CSP) é um recurso de segurança essencial que ajuda a prevenir vários tipos de ataques, como Cross-Site Scripting (XSS) e ataques de injeção de dados. Ao definir um conjunto de regras de segurança para aplicativos web, o CSP restringe as fontes de onde o conteúdo pode ser carregado, reduzindo o risco de execução de código malicioso.
CSP é um cabeçalho de resposta HTTP ou uma meta tag que define quais recursos podem ser carregados e executados dentro de uma página da web. Ele funciona como um mecanismo de segurança baseado em lista de permissões, garantindo que apenas scripts, estilos, imagens e outros recursos explicitamente permitidos sejam carregados.
Principais características do CSP
Mitigação contra ataques XSS: Impede a execução de scripts não autorizados.
Controle sobre o carregamento de recursos: Limita as fontes de onde scripts, imagens e outros ativos podem ser carregados.
Prevenção de ataques de injeção de dados: Bloqueia a injeção e execução de código malicioso no navegador.
Redução da superfície de ataque: Ajuda a impor boas práticas de segurança, limitando dependências externas.
Como implementar CSP?
O CSP pode ser implementado adicionando um cabeçalho HTTP Content-Security-Policy na configuração do servidor web ou através de uma <meta> tag diretamente no HTML.
Exemplo via tag meta
Pode se definir a CSP diretamente no HTML dessa forma:
Esse cabeçalho pode ser implementado no backend ou até diretamente em um servidor web como NGINX, onde conseguimos as vantagens da meta tag e mais flexibilidade caso haja a necessidade de múltiplas definições.
Conclusão
O CSP é uma ferramenta poderosa para melhorar a segurança de aplicativos web. Ao aprender e implementar o CSP de maneira eficaz, os desenvolvedores podem proteger os usuários contra ameaças de segurança comuns, garantir conformidade com padrões de segurança e criar aplicativos web mais resilientes. Com a abordagem certa, o CSP pode melhorar significativamente a postura geral de segurança de qualquer projeto web.
Observação importante: Não se trata de um conteúdo elaborado por um especialista em segurança, sendo uma tentativa de aprofundamento no tema. Caso sejam identificados pontos de melhoria, comentários construtivos são bem-vindos.
The C4 model (Context, Containers, Components, Classes) is an approach for modeling and documenting software architecture proposed by Simon Brown. It provides a scalable and hierarchical framework for describing software systems at different levels of abstraction.
Abstraction Types
System Context Diagram
This will be the first diagram to be developed. It will provide a more general view of the system, allowing us to have a broad understanding of the environment in which the software will be developed and implemented.
The System Context Diagram serves as an overview of the software system and its surrounding environment. It identifies the external entities (actors, systems, or services) interacting with the system and illustrates the relationships between them. This diagram helps stakeholders grasp the high-level context of the system and understand its boundaries and dependencies.
Containers Diagram
The second diagram will be a container diagram. It represents a “zoom-in” on the core of the context diagram, allowing us to show the major containers of the application. For example, we can include elements such as the web client (a Single Page Application), the mobile application, the application’s API, and the database.
Components Diagram
This will be the third diagram. It represents another “zoom-in”, this time into one of the containers from the previous diagram. It will provide a more detailed view of elements such as controllers, facades, etc. This diagram will also show what surrounds this component, meaning the environment in which it is embedded.
Code/Class Diagram
This will be the fourth and final diagram. It may not always be necessary, but when it is, it can be generated automatically by an IDE. Class diagrams (UML) are well suited for this purpose, as they allow exemplifying the classes at a lower level.
Observations
IMHO we can follow some tips of other authors and init ours diagrams with some things in mind.
Maintain consistency in figures: This allows the reader to easily understand the relationships and hierarchies between diagram elements.
Explain terms and acronyms: Especially if those consuming your diagrams are non-technical people: This ensures that everyone can understand the diagram’s content.
Avoid abbreviations and prefer to make everything more explicit: This prevents confusion and misunderstandings.
Make it clear what is being diagrammed: whether it’s an API, a client, or just a user: This allows the reader to know exactly what each diagram element represents.
Make connections between containers clear: This helps the reader understand how different system elements interact with each other.
Use legends to clarify diagrammed objects: This provides additional information that can be useful for understanding the diagram.
Conclusion
The C4 model empowers software engineers to articulate and elucidate their ideas in a lucid and multi-layered structure. With its framework, we can craft diagrams for the technical team’s, while also with the System Context diagrams are comprehensible to the entire team. This approach creates a comprehensive understanding of the application flow among team members, ensuring alignment and clarity across all stakeholders.
A arquitetura de BFF (Backends For Frontends) é uma abordagem que permite aos desenvolvedores frontend terem mais autonomia na criação de seus projetos. Isso é especialmente útil na maneira como eles irão agregar os endpoints. O principal benefício desta estratégia é que ela permite que o desenvolvimento seja guiado por aqueles que irão consumir o BFF, neste caso, os desenvolvedores frontend. Isto porque, em geral, são estes profissionais que têm um maior entendimento do contexto de uso e das necessidades específicas do projeto. Portanto, eles são os mais indicados para tomar decisões sobre a implementação.
O padrão de desenvolvimento Backend For Frontend (BFF) é uma abordagem que nos ajuda a repensar a forma como as aplicações client-side interagem com as APIs de serviço. De acordo com esse padrão, as aplicações client-side não devem se comunicar diretamente com a API de serviço, mas sim com uma camada separada que funciona como um middleware. Essa camada intermediária tem a responsabilidade de agrupar as chamadas da API backend. Dessa forma, é possível simplificar a comunicação e melhorar a eficiência do sistema, já que as solicitações são agrupadas de maneira inteligente, reduzindo a necessidade de múltiplas chamadas de API individuais.
Gateway API pattern
API Gateway é um componente central em arquiteturas de microserviços e na exposição de serviços na web. Funciona como um ponto de entrada único para várias APIs, permitindo a criação, gerenciamento e monitoramento de APIs de forma centralizada.
Ele atua como um intermediário entre clientes e serviços, fornecendo funcionalidades como roteamento de solicitações, autenticação, autorização, transformação de dados, controle de acesso e monitoramento de tráfego.Isso simplifica o desenvolvimento de aplicativos, garantindo segurança, escalabilidade e desempenho consistentes.
Atuando como um ponto único de entrada na utilização das APIs, o padrão em questão assegura a resiliência mencionada anteriormente. Com base nesse padrão, foi desenvolvida a arquitetura de BFF (Backend For Frontend), na qual cada BFF funciona como um gateway dedicado para cada cliente.
Por que BFF?
A estratégia de desacoplar o Backend e o Frontend é essencial para criar uma experiência de desenvolvimento Frontend eficaz e agradável. Isto envolve a construção de um Backend específico, concebido para atender às necessidades únicas do projeto Frontend.
Esta abordagem permite que cada componente seja desenvolvido e otimizado independentemente, resultando em um produto final mais robusto e eficiente. Além disso, a separação do Backend e do Frontend promove uma melhor colaboração entre as equipes, já que cada uma pode se concentrar em sua área de especialidade.
Quando usar BFF?
O uso de BFFs se torna necessário quando temos múltiplos microserviços e a aplicação client-side precisa integrar e orquestrar todas essas integrações. Com um BFF para atender às necessidades desse cliente, a tendência é que a complexidade dessas integrações seja transferida do lado do cliente para o BFF, simplificando assim integrações que poderiam envolver 3 ou 4 endpoints, tornando-se uma integração de apenas 1 endpoint para o Frontend.
No diagrama abaixo podemos ver um exemplo de como é uma arquitetura utilizando um API Gateway, onde as chamadas via client batem nesse gateway e o mesmo direciona para o serviço certo.
Já aqui podemos ver uma implementação de BFF, onde temos “gateways” específicos para cada necessidade, podendo ter um agrupamento de chamadas para WEB e para o mobile não ter necessidade visto que cada BFF irá lidar com as necessidades de cada produto.
Contras do uso de BFF?
Como todas as escolhas técnicas, a aplicação do padrão BFF tem algumas desvantagens. A principal é que temos mais um serviço para manter e escalar. Mesmo que um BFF tenda a ser simples em sua essência, ele pode crescer e se tornar difícil de manter. Isso exige que a equipe responsável (normalmente a equipe de Frontend) seja prudente nas implementações e assuma uma responsabilidade adicional além da criação das interfaces, pois agora integraremos o BFF no serviço backend e o Frontend no BFF.
Como a arquitetura de BFF gera valor ao negócio?
Como mencionado anteriormente, com BFFs podemos reduzir a carga de processamento no Frontend, obtendo payload e respostas com apenas o necessário para o client-side, com a simplificação da agregação de chamadas de API e a otimização dos processos de Frontend. Isso nos permite executar processos complexos no lado do servidor, tornando o Frontend mais leve e proporcionando uma melhor experiência para o usuário final.
Por todos esses motivos, a arquitetura de BFFs demonstra ser uma estratégia valiosa, que pode trazer diversos benefícios tanto para o processo de desenvolvimento quanto para o produto final.
O que é projeção de conteúdo (content projection)?
projeção de conteúdo é uma forma de um componente utilizar dados/estruturas vindas de um outro componente, com isso nós temos muito poder de criação e customização de componentes.
Bibliotecas ou frameworks nos dão essas possibilidades, Como no angular, que temos ng-content para projeção direta de conteúdo*, ngTemplateOutlet* para definirmos o que será projetado no local desse template, ngTemplateOutletContext para passarmos dados de um template para o pai desse template e por fim o ngTemplateOutletInjector que nos permite passar algum injector via template.Já com React temos os Slots, que são muito similares ao ng-content e com ele podemos definir diferentes partes do nosso componente que irão receber conteúdo para ser projetado.
Quais problemas a projeção de conteúdo quer resolver?
Com a projeção de conteúdo conseguimos criar componentes mais customizáveis e agnósticos a estrutura geral, assim caso necessário podemos realizar mudanças no uso desse componente sem a necessidade de modificá-lo internamente.
Como projetar conteúdo com Angular?
Dentro do Angular temos como projetar conteúdo de um jeito simples com o ng-content, utilizando seletores ou não.
A principal vantagem de utilização do ng-content é a simplicidade do seu uso e como podemos adicionar “poderes” às nossas projeções com os seletores, assim podemos customizar as seções
dessa projeção.
Com React podemos passar valores como children, dessa forma conseguimos, por exemplo utilizar uma div com outros três componentes que o nosso componente (Card) que está esperando essa prop e irá renderizar essa porção de UI.
<Card> <p>Content</p></Card>
Além da prop children, podemos utilizar outras props para compor melhor o nosso componente
Até aqui já temos algo bem interessante, com type-safety se estivermos usando TypeScript. Mas podemos melhorar e facilitar essa implementação com uma composição de componentes.
Para atingirmos o resultado acima iremos criar mais dois componentes, header e footer, esses componentes nos ajudarão a compor melhor o componente Card
Componente Header:
type CardHeaderProps = { children: React.ReactNode;};export const CardHeader = ({ children }: CardHeaderProps) => { return <header>{children}</header>;};
Componente Footer:
type CardFooterProps = { children: React.ReactNode;};export const CardFooter = ({ children }: CardFooterProps) => { return <footer>{children}</footer>;};
Agora conseguimos uma solução bem mais flexível e customizável, além de que temos uma nomenclatura que faz bastante sentido, assim conseguimos ver cada parte do nosso componente sendo utilizada em conjunto.
A RFC (Request for Comments) de Slots do React ainda está sendo discutida e visa implementar uma api simplificada para o uso de slots dentro do React.
Indo além, portais!
Quando precisamos projetar conteúdo de um componente filho para outro componente podemos aplicar o conceito de portais, onde conseguimos passar um esse componente e referenciar o mesmo a outra parte do DOM. Com essa técnica conseguimos criar comportamentos que muitas vezes seriam mais complexos de implementar, conseguindo assim criar abstrações e deixar o nosso Front-end mais simples e escalável.
Portais com Angular
Para a criação de portais com Angular é comum utilizarmos diretivas para compor o componente que será passado pelo portal e o portal de fato. Caso opte por uma diretiva já pronta, podemos utilizar o Portal do Angular Material, que já tem uma implementação de portal.
Primeiro vamos criar uma diretiva para lidarmos com o outlet, ou o local que irá renderizar o componente via portal.
Essa diretiva irá controlar mudanças e mapeará o conteúdo do portal, e após a diretiva do portal (PortalDirective) ativar o evento de mudança, essa diretiva irá criar o um EmbeddedView do nosso elemento do portal no outlet
Agora na nossa diretiva de portal, o nosso setter appPortal vai disparar as ações da nossa diretiva de outlet quando adicionarmos uma chave para o mesmo.
O uso de ambas as diretivas são bem simples, só precisamos passar a chave do pedaço de UI que onde queremos renderizar **appPortalOutlet* e no **appPortal* é onde vamos definir a parte da UI que será renderizada.
<div class="main-content"> <p>Main content</p> <div *appPortalOutlet="'my-portal'"></div></div><h3 *appPortal="'my-portal'">My sub header</h3>
Portais com React
React já tem uma implementação de portais pronta, o createPortal, essa função nos permite injetar um componente em um elemento do DOM. Para utilizar o portal, precisamos buscar o elemento no dom. No exemplo criamos um custom hok que recebe um id e retorno esse elemento ou null.
Para utilizar essas funcionalidades em conjunto, podemos fazer dessa forma:
function App() { const element = usePortal("main-content"); return ( <section> <div id="main-content" className="main-content"> main content </div> {element && ( <PortalComponent elementOutlet={element} reactNode={<h3>Sub header</h3>} /> )} </section> );}
Conclusão
Como pudemos ver temos diversas formas de projetar conteúdo, slots, ng-content, etc. Algumas mais acopladas aos seus frameworks e outras são mais conceituais e podem ser utilizadas independentemente de frameworks, é sempre importante sabermos o jeito mais recomendado de
implementação na plataforma que estamos atuando, porém ao entender o conceito principal de projeção/portais conseguiremos aplicar essas mesmas técnicas sem problema.
Quando estamos utilizando Observables sempre há uma dificuldade em como organizar os testes para que possamos entender e testar toda a nossa stream. Podemos testar da forma mais “simples” utilizando subscribes para validar os valores da nossa subscription, porém temos uma outra abordagem para testes reativos e os mesmos podem ser feitos com um Scheduler nativo da biblioteca.
Por isso foi criado o TestScheduler que é uma classe de utilidade e que podemos usar para ter um ambiente de teste assíncrono e confiar no resultado.Para começar, primeiro precisamos entender o conceito de hot e cold Observables. Com isso podemos entender mais facilmente como os diagramas funcionam.
Hot Observables
Esse tipo de Observable pode emitir mais de uma vez, ou seja, geram um producer para multiplos subscribes, podem muitas vezes atuar como listeners e conter informações diferentes com o passar do tempo. Esses Observables vão “produzir” valores mesmo quando não houver ninguém se inscrevendo nos mesmos, diferentemente do cold Observable, um exemplo prático de um hot Observable seria um Subject.
Cold Observables
Os Observables “gelados” geram um producer para cada subscriber, ou seja, toda vez que alguém se interessa e se inscreve, o Observable irá gerar um novo produtor de dados, um exemplo prático de um cold Observable seria uma requisição HTTP.
Sintaxe de marble
Isso é uma simplificação da explicação disponível na documentação
’ ’ whitespace: espaços são ignorados, é útil para alinhar múltiplos diagramas;
‘-’ frame: 1 “frame” de tempo decorrido;
[0-9]+[ms|s|m] time progression: Essa é a sintaxe de progressão virtual de tempo, que se baseia em um número junto de uma unidade de tempo, ms (milisegundos), s (segundos), m (minutos). Leia a documentação na seção de Time progression syntax para mais detalhes;
‘|’ complete: Simboliza a conclusão com sucesso de um Observable, equivalente a utilizarmos um complete() de um Subject;
‘#’ error: Simboliza a finalização com erro de um Observable, utiliza-se para ativar producer de error() ;
[a-z0-9]: Qualquer caractere alfanúmerico: Representa um valor emitido por um producer next();
‘^’ subscription point: (Apenas para Observables hot) Simboliza o ponto em que o Observable irá ter a sua inscrição iniciada, esse é o frame zero para esses Observables, todos os frames antes do ^ serão negativos;
‘()’ sync groups: Quando multiplos eventos precisam estar no mesmo frame de forma sincrona, os eventos que precisamos emitir nesse mesmo frame vamos adicionar nesse grupo com os parenteses, por exemplo (abc) , aqui vamos emitir os valores abc no mesmo frame.
Primeiro vamos testar um service de todos, esse service contém um BehaviorSubject dois getters e um método que modifica o esse mesmo Subject:
import { Injectable } from "@angular/core";import { BehaviorSubject, filter, map } from "rxjs";import { Todo } from "../models/todo.model";@Injectable({ providedIn: "root",})export class TodoService { private todos = new BehaviorSubject<Todo[]>([]); get todos$() { return this.todos.asObservable(); } get todosCount$() { return this.todos$.pipe( map((todos) => todos.filter((todo) => !todo.completed).length) ); } addTodo(todo: Todo) { this.todos.next([...this.todos.value, todo]); }}
Testes com o padrão subscribe/done:
Um caso em que não há necessidade de utilizar marbles são os testes de serviços http, o test scheduler foi criado para testar regras negócio reativas complexas, operadores que podem ter muitos fluxos, aqui temos mais alguns pontos sobre isso.
Um pequeno exemplo de testes de serviços http, utilizando HttpClientTestingModule:
Implementação:
import { HttpClient } from "@angular/common/http";import { Injectable } from "@angular/core";import { map, Observable } from "rxjs";import { Post } from "../models/posts.mode";@Injectable({ providedIn: "root",})export class PostsService { private readonly API = "https://jsonplaceholder.typicode.com"; constructor(private http: HttpClient) {} getPosts(): Observable<Post[]> { return this.http .get<Post[]>(`${this.API}/posts`) .pipe( map((posts) => posts.map((post) => ({ ...post, title: post.title.toUpperCase() })) ) ); }}
Teste:
import { TestBed } from "@angular/core/testing";import { HttpClientTestingModule, HttpTestingController,} from "@angular/common/http/testing";import { PostsService } from "./posts.service";describe("PostsService", () => { let service: PostsService; let httpMock: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], }); service = TestBed.inject(PostsService); httpMock = TestBed.inject(HttpTestingController); }); it("should be created", () => { expect(service).toBeTruthy(); }); describe("getPosts", () => { it("should get posts", (done) => { const postsMock = [ { userId: 1, id: 1, title: "Test title 1", body: "test body 1", }, { userId: 2, id: 2, title: "Test title 2", body: "test body 2", }, ]; service.getPosts().subscribe((posts) => { expect(posts).toEqual( postsMock.map((post) => ({ ...post, title: post.title.toUpperCase(), })) ); done(); }); httpMock .expectOne("https://jsonplaceholder.typicode.com/posts") .flush(postsMock); }); });});
Agora vamos partir para os casos que serão demonstrados aqui no artigo com o TodoService.
it("should add todo on list with done", (done) => { service.addTodo({ completed: true, id: 1, title: "First todo" }); service.todos$.subscribe((todos) => { expect(todos).toEqual([{ completed: true, id: 1, title: "First todo" }]); done(); });});
Esse caso de teste é bem simples, onde estamos assinando um valor no nosso Subject e logo após nos inscrevendo para interagir com o Observable de todos$, nesse caso também devemos observar o uso da função done(), essa função que é a responsável por fechar a nossa Subscription e garantir que o expect foi executado da maneira esperada, sem ela não podemos confiar em expects dentro do Subscribe, falsos positivos podem ocorrer por conta de um delay em fluxos mais complexos de Observables ou pequenas alterações, que podem ocorrer durante o decorrer do fluxo reativo, nos testes de Observables.
Mas nem sempre precisamos do done(), quando os expects não necessitam serem validados em algum fluxo async, por exemplos cold Observables, conseguimos evitar o uso do done(), o mesmo pode ter alguns comportamentos errôneos, ainda mais em casos de callback de error que podem ser um pouco complexos de testar.
Exemplo de um teste de Observable, sem done e que não tem falso positivo:
import { of } from "rxjs";describe("Test with observables without done", () => { it("should set values on the list", () => { const list: string[] = []; of("Hello", "RxJS").subscribe((value) => { list.push(value); }); expect(list).toEqual(["Hello", "RxJS"]); });});
Testes com TestScheduler:
Para haver um bom uso de marbles, podemos pegar pequenas partes das streams e ir testando a passagem de tempo, por exemplo, serviços de estado podem se valer muito bem dos testes com marbles, assim como fluxos reativos com regra de negócio que podem ter muitos desvios e complexidade para mockar e validar passo a passo utilizando o padrão de subscribe/done.
Como podemos ver, foi representado com o mesmo cenário testado com subscribe/done por se tratar de um cenário bem simples pode-se notar que há bem mais complexidade nesses testes, portanto devemos utilizar essas abordagens com calma e em casos mais complexos, como os próximos casos demonstrados.
Um caso em que há uma grande vantagem ao utilizar marbles é quando precisamos lidar com a passagem de tempo, de uma forma que seja simples de escrever e não precisamos nos preocupar com efeitos de passagem do tempo como tick().
Tendo em vista esse Observable de contagem de todos:
Precisamos garantir que será retornado o número de todos completados, porém, temos um delay de 200 milissegundos nessa operação, como podemos testar isso?
it("should get the count of todos when is incomplete", () => { testScheduler.run((helpers) => { const { expectObservable } = helpers; const expected = "200ms a"; const value = { a: 1, }; const todo1 = { completed: true, id: 1, title: "First todo", }; const todo2 = { completed: false, id: 2, title: "Second todo", }; service.addTodo(todo1); service.addTodo(todo2); expectObservable(service.completedTodosCount$).toBe(expected, value); });});
O que precisamos fazer é apenas adicionar a passagem de tempo antes da emissão de “a”, no nosso caso 200ms, dessa forma passamos em forma de diagrama o que é esperado da nossa stream.
Conclusão
Como podemos ver, nesses cenários, os testescom marble são superpoderosos e são ferramentas a mais no nosso cinto de utilidades para criarmos testes confiáveis, mas também não temos necessidade de aplicarmos esse modelo de teste em todo os casos possíveis, pois eles acabam adicionando complexidade em casos que apenas um subscribe/done já resolveria muito bem.
With Data Structures we can store, organize, order and handle data. We need to understand how and when use determinate structures.
JavaScript has some built-in structures introduced on es6 version, even though these data structures have some time of existence has many developers has doubt about how to use them, today I wanna try to clarify the information about these.
Map
Map is an object and works as a common object, the major difference between them is because map lets you work with internal functions to make the insertion, deletion or get one element with a more simplistic form.
Also, Map only permit a unique key with diferents values. So if I create a map like this:
We can note the value is changed but only one key as stored on our Map.
Map is iterable, so we can use a for..of or for each to iterate through our structure and make operations there.
const map = new Map();map.set("first", 1);map.set("second", 2);for (let item of map) { console.log(item);}for (let [key, value] of map.entries()) { console.log(key, value);}for (let key of map.keys()) { console.log(key);}for (let value of map.values()) { console.log(value);}map.forEach((item, key) => { console.log(key, item);});
With for...of each iteration return an array like this [key, value], with forEach on each we have three parameters, first the value, them the key and finally the map itself.
Why/When use Map?
We wanna use Map structure when it’s necessary to keep control of information about the object, and we need to keep keys unique, also Map has a simple usage, so it’s simple to get used to using.
WeakMap
WeakMap is a collection of key/value in which keys are weakly referenced.
Because keys are weakly referenced, they cannot be enumerated, so we can’t iterate them like Map and cannot obtain the keys.
We can use WeakMaps like this:
const weakMap = new WeakMap();const value1 = {};const value2 = function () {};const value3 = "I'm the third value";const value4 = { foo: "foo" };const value5 = { key: "foo" };weakMap.set(value1, value2);console.log(weakMap.has(value3)); // false// Returns the value based on key, in this case function() {}console.log(weakMap.get(value1));weakMap.delete(value1);weakMap.set(value5, value4);// Using a object that already in memory, we can access the positionconsole.log(weakMap.get(value5));weakMap.set({ myKey: "myKey" }, { value: 1 });// Will return undefined, because the object on function// call is one and the object on the set function is anotherconsole.log(weakMap.get({ myKey: "myKey" }));
Note: We can’t use primitives values like keys with WeakMaps
Why/When use WeakMap?
Some use cases for WeakMaps, here have some discussion on the topic, here I will put some tests and my understandings about the data structure:
When we need to handle some private data and do not want to iterate that data, only getting the specific property, WeakMap can be a good choice.
Set
Sets are collections that permits the storage of any type of unique values. With sets we can avoid duplicate data, remembering that objects references can be added like a new value too.
We can use Sets like that:
/code
Like Maps, Sets can also be iterated:
const set = new Set();set.add(1);set.add("text");set.add({ foo: "bar", bar: "foo" });for (let item of set) { console.log(item);}// Keys will have the inserted valuesfor (let item of set.keys()) { console.log(item);}/** * key and values are the same here */for (let [key, value] of set.entries()) { console.log(key); console.log(value);}
Using spread operator we can create a copy of a Set and use as an array:
const set = new Set();set.add(1);set.add("text");set.add({ foo: "bar", bar: "foo" });const setToArray = [...set];setToArray.forEach((item) => { console.log(item);});
Why/When use Set?
We would like to use Sets when it’s necessary to keep unique values without the need to use key/value on our structure. For that Sets are the best choice because they will keep the consistency of our data.
Note: it’s valid to think about the objects references example because even though you pass the same object to the set, it will be saved because are different references.
WeakSet
WeakSet objects permit you to store weakly held objects. Like as Set collection, WeakSet will permits each object occurrence only once.
What the difference of WeakSet and Set? WeakSet only accepts objects, so they cannot contain any values like Sets. Another difference is like the WeakMap, WeakSet has weak references of the objects they held, if no other references of an object store exist this object can be garbage collected. Last but not less important, the WeekMap collection cannot be enumerable.
In the documentation have an interesting example about the usage of that structure.
Simple example of using WeakSet:
const weakSet = new WeakSet();const foo = {};const bar = {};weakSet.add(foo);weakSet.add(bar);console.log(weakSet.has(foo)); // trueconsole.log(weakSet.has(bar)); // trueweakSet.delete(foo);console.log(weakSet.has(foo)); // falseconsole.log(weakSet.has(bar)); // trueconsole.log(weakSet.has({})); // false because is another reference
Useful links:
Key equality is based on the sameValueZero algorithm
Sidenote: ES6 introduced a new feature in javascript called ‘modules’, they’re essentially a syntax for importing and exporting code between JavaScript files. They’re powerful and bring a new horizon for javascript development but it’s not the subject today.
Today we will talk about IIFEs or Immediately Invoked Functions Expressions, working with vanilla javascript we can use these functions for better-scoped and responsibilities definition on our code.
What is IIFE?
It’s a JavaScript pattern, that allows us to create modules with more lexical scope for our functions, create functions for executing simple tasks and immediately execute and help with code organization too.
Creating a module
We will init using an anonymous closure. Thus we create a lexical scope, and the code that it’s inside of the scope will be only accessed by the function, including properties too.
(function () { // Your functions and variables will be scoped in this function // In this case our console.log will be executed immediately console.log("My first module");})();
Exporting module
With that we can use only a returned properties and functions by our module, thus that way keeping our code scoped on the module.
With this pattern, we can create simple scripts for immediate execution, a great example is the use of login scripts for adding more agility to the development process:
With this simple script, we can create an automated login.
IIFE on ES6+
When ES6 was released, it brought some new additions, one of which is const and let, this type of variable declaration has the scope of blocks, so when we get a curly braces block, our let or const has the respective curly braces scope. So we do not need this pattern all the time but the knowledge always helps in certain situations.
Angular é um framework e uma plataforma para o desenvolvimento web, focado na modularização do código utilizando módulos ou ngModules para isso.
Quando estamos desenvolvendo uma aplicação Angular sempre pensamos na melhor forma para construir componentes, módulos, serviços etc.
Seguindo nessa linha contarei algo que foi necessário desenvolver durante um projeto, eu e o meu time estavámos sem back-end no momento e precisávamos seguir no desenvolvimento dos serviços para podermos concluir algumas tasks.
Começamos com um server json para simularmos as requisições, mas acabou se tornando insustentável esse método por questões do projeto e arquitetura, assim com Interceptors podiámos criar o serviços de forma que fosse simples atualizar os mesmos quando fosse necessário, assim melhorando a modularidade da aplicação.
O que são os HttpInterceptors?
Básicamente falando são interceptadores de requisições Http, eles servem para configurar e enviar requisições para o servidor.
Assim podemos fazer um milhão de coisas, um exemplo seria uma autenticação via token em qualquer requisição, ou até tratar respostas antes de terminar a requisição.
Partiu desenvolver :)
Para que você consiga realizar todos os passos mostrados aqui você precisará do Node instalado, caso você não tenha aqui têm um bom tutorial de instalação:
Agora vamos alterar o nosso arquivo backend.interceptor.ts
Vamos adicionar um pequeno código para intecerptar a requisição quando for solicitado pelo nosso get que foi criado agora a pouco.
@Injectable()export class BackendInterceptor implements HttpInterceptor { intercept( request: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { apiUrl = "http://localhost:4200"; const { url, method, body } = request; if (method === "GET") { return of( new HttpResponse({ status: 200, body: JSON.parse(localStorage.getItem("users")), statusText: "Get all users", url: `${this.apiUrl}/users`, }) ); } }}
Bom agora o nosso interceptor vai ficar esperando a nossa rota enviar o que precisa e assim responder de acordo caso nosso método for um GET.
Nesse exemplo vamos retornar no objeto body da nossa requisição o json que temos no nosso localStorage.
Para mais informações consultem esse repositório aqui temos a implementação completa de um CRUD utilizando interceptors.
Mas não esqueça que os interceptors são ferramentas que podemos utilizar para muitas outras situações, a ideia aqui é mostrar o que pode ser feito de uma maneira simples para que possamos entender o funcionamento dessa interface do Angular.
Guilherme GulesSobre Mim
Disponível
Guilherme Gules Moreira
Engenheiro de Software Sênior • Fullstack & Inteligência Artificial
Java & Spring BootNode.js & ReactIA Generativa & SDDCinnamon CSS
Aplicações escaláveis e resilientes com Java (Spring Boot), Node.js (NestJS/Express) e React.
🏛️
Arquitetura de Microsserviços & BFFs
Concepção ponta a ponta, Micro Frontends, mensageria, observabilidade e modernização tecnológica.
🤖
IA Generativa no Ciclo de Desenvolvimento
Spec-Driven Development, Kiro, Amazon Q, agentes especializados e skills customizadas.
🌿
Open Source & Cinnamon CSS
Criador da biblioteca Cinnamon CSS trazendo a estética Linux Mint para a web.
📖Sobre Mim
Sou Engenheiro de Software Sênior, com sólida experiência no desenvolvimento, evolução e modernização de aplicações escaláveis e de alta disponibilidade, atuando como Fullstack em ambientes de alta complexidade. Tenho experiência com Java (Spring Boot), Node.js (NestJS/Express) e React, com forte atuação em arquitetura de microsserviços, BFFs (Backend for Frontend) e Micro Frontends. Atuo desde a concepção e arquitetura até a implementação e sustentação de soluções, incluindo criação de serviços do zero, definição de padrões, observabilidade, integrações via APIs REST, processamento assíncrono, mensageria e estratégias de modernização tecnológica, incluindo evolução para versões mais recentes do Java.
Tenho forte atuação na aplicação de Inteligência Artificial Generativa ao ciclo de desenvolvimento, utilizando Spec-Driven Development, Kiro e Amazon Q. Desenvolvo e utilizo skills customizadas e agentes especializados para automatizar atividades de desenvolvimento, testes de integração entre microsserviços, publicação e consumo de mensagens, validação de fluxos ponta a ponta e migração e transformação de sistemas. Busco combinar visão arquitetural, excelência técnica e inovação para construir soluções robustas e sustentáveis, promovendo automação, melhoria contínua, qualidade de engenharia e colaboração entre equipes.
The sliding window is a method that allows you to process a subset of elements, referred to as a window, and move it across the data structure in a way that reuses previous computations instead of recalculating everything from scratch.
Adotar o modelo full open-source vai além de apenas usar software gratuito. Se trata de um compromisso com a transparência, colaboração, auditabilidade e autonomia tecnológica. A seguir, vou comentar os principais benefícios que notei, ferramentas recomendadas e boas substituições de tecnologias proprietárias por soluções abertas.
A segurança na web é uma preocupação constante. Todos os dias, milhares de sites são comprometidos por ataques como Cross-Site Scripting (XSS), permitindo que invasores executem scripts maliciosos em páginas legítimas.
The C4 model (Context, Containers, Components, Classes) is an approach for modeling and documenting software architecture proposed by Simon Brown. It provides a scalable and hierarchical framework for describing software systems at different levels of abstraction.
A arquitetura de BFF (Backends For Frontends) é uma abordagem que permite aos desenvolvedores frontend terem mais autonomia na criação de seus projetos. Isso é especialmente útil na maneira como eles irão agregar os endpoints.
projeção de conteúdo é uma forma de um componente utilizar dados/estruturas vindas de um outro componente, com isso nós temos muito poder de criação e customização de componentes.
Quando estamos utilizando Observables sempre há uma dificuldade em como organizar os testes para que possamos entender e testar toda a nossa stream. Podemos testar da forma mais “simples” utilizando subscribes para validar os valores da nossa subscription, porém temos uma outra abordagem para testes reativos e os mesmos podem ser feitos com um Scheduler nativo da biblioteca.
JavaScript has some built-in structures introduced on es6 version, even though these data structures have some time of existence has many developers has doubt about how to use them, today I wanna try to clarify the information about these.
It's a JavaScript pattern, that allows us to create modules with more lexical scope for our functions, create functions for executing simple tasks and immediately execute and help with code organization too.