Logo Tech Writers

Tech writers

Esse é o nosso blog para apaixonados por tecnologia! Aqui, softplayers e outros especialistas compartilham conhecimentos fundamentais para o desenvolvimento dessa comunidade.

SEJA UM TECH WRITER
A Evolução Digital no Setor Público: Gestão de Produtos B2G
Tech Writers Abril 16, 2025

A Evolução Digital no Setor Público: Gestão de Produtos B2G

Nos últimos anos, o setor público tem ampliado seus serviços digitais para os cidadãos. A pandemia de 2020 acelerou essa tendência, impulsionando a modernização de órgãos como Tribunais de Justiça, Ministérios Públicos e Defensorias. Essa transformação visa melhorar a eficiência dos serviços públicos e facilitar o acesso da população. Historicamente, as áreas de Tecnologia da Informação desses órgãos adotaram modelos de gestão de projetos que priorizam a entrega de escopos definidos, com prazos e equipes limitadas às demandas específicas. Contudo, a necessidade crescente de agilidade tem impulsionado a transição para a gestão de produtos. Nesse contexto, o conceito de Business-to-Government (B2G) ganha relevância, destacando a importância da gestão de produtos na oferta de soluções inovadoras para o governo. Como Product Manager atuando em produtos B2G, meu foco é entregar soluções alinhadas às necessidades dos usuários finais. Diferente do setor B2B, onde há um funil de vendas estruturado, a gestão de produtos no setor público exige a adoção de métricas e ferramentas adaptadas a esse ecossistema. Dia a dia de Products Managers no setor público As interações com os clientes iniciam-se após a assinatura do contrato, quando o primeiro contato ocorre com o grupo gestor, formado por servidores responsáveis pela implantação do produto. A partir desse ponto, são obtidos insights sobre as necessidades dos usuários finais, permitindo um entendimento inicial do fluxo de trabalho. Para priorizar o backlog, utilizamos a matriz RICE (Reach, Impact, Confidence, Effort), garantindo que as decisões considerem tanto as exigências contratuais quanto as necessidades dos usuários. Essa priorização ocorre de forma contínua, acompanhando a evolução do produto e dos contratos estabelecidos. No ciclo de desenvolvimento, aplicamos técnicas de experimentação, prototipação e testes de usabilidade com grupos pilotos. Coletamos dados quantitativos e qualitativos para medir a adesão e definir melhorias nas funcionalidades do produto. Exemplo de matriz RICE Com estas premissas em mente, aplicamos aos usuários piloto por exemplo, técnicas de experimentação, prototipação, condução de testes de usabilidade para novas funcionalidades. Também realizamos coletas constantes de dados quantitativos e qualitativos da jornada utilizada por eles, conforme aumenta a adesão. A partir das métricas coletadas, podemos definir se as funcionalidades principais ou acessórias da jornada do nosso usuário precisam ser evoluídas. Exemplo de insights quantitativos com informações da jornada do usuário na organização de tarefas utilizando a ferramenta MixPanel Exemplo de uso da ferramenta INDECX para informações qualitativas a respeito do produto ou funcionalidade Tríade de produto entregando resultados eficientes A gestão de produtos no setor público exige uma abordagem colaborativa, integrando time técnico, equipe de experiência do usuário e cliente. Essa interação contínua fortalece o alinhamento estratégico e a clareza sobre a evolução do produto. O roadmap do produto é compartilhado com o cliente para garantir transparência e previsibilidade nas entregas. Tríade de produto O Grupo Softplan se consolidou como referência na transformação digital do setor público, gerando impactos positivos para os cidadãos. Soluções como o Sistema de Automatização da Justiça (SAJ) proporcionam eficiência e celeridade nos serviços públicos. Como Product Manager na Softplan, contribuo para a gestão de produtos voltados ao setor público. Um exemplo é o SAJ Defensorias, cujo painel de tarefas foi desenvolvido após estudo de negócios e análise técnica baseada na tríade de produtos. Esse painel centraliza atividades diárias, priorizando tarefas a serem executadas imediatamente e organizando as finalizadas para referência futura. Solução SAJ Softplan Nosso objetivo é oferecer produtos intuitivos e eficientes, que atendam às demandas cotidianas dos defensores públicos e contribuam para a melhoria da prestação de serviços à sociedade. Painel de tarefas do Defensor no SAJ Defensorias As iniciativas digitais no setor público têm grande potencial de crescimento, impulsionadas pela cultura de produtos. A transformação digital é irreversível e continuará evoluindo para atender às expectativas da sociedade por serviços mais ágeis, eficientes e transparentes.

Exaustão do ThreadPool do .Net
Tech Writers Março 25, 2025

Exaustão do ThreadPool do .Net

Mais de uma vez em minha carreira me deparei com este cenário: a aplicação .Net frequentemente mostrando tempos de resposta elevados. Esta alta latência pode ter várias causas, como lentidão no acesso a algum recurso externo (um banco de dados ou uma API, por exemplo), uso de CPU “batendo” em 100%, sobrecarga de acesso a disco, entre outras. Quero adicionar à lista anterior outra possibilidade, muitas vezes pouco considerada: exaustão do ThreadPool. Será apresentado de forma bem rápida como o ThreadPool do .Net funciona, e exemplos de códigos onde isto pode acontecer. Por fim, será demonstrado como evitar este problema. O ThreadPool do .Net O modelo de programação assíncrona baseado em Tasks (Task-based asynchronous programming) do .Net é bastante conhecido pela comunidade de desenvolvimento, mas acredito que seja pouco compreendido em seus detalhes de implementação - e é nos detalhes onde mora o perigo, como bem diz o ditado. Por trás do mecanismo de execução de Tasks do .Net existe um Scheduler, responsável, como seu nome já indica, por escalonar a execução das Tasks. Salvo alguma mudança explícita, o scheduler padrão do .Net é o ThreadPoolTaskScheduler, que também como o nome indica, utiliza o ThreadPool padrão do .Net para realizar seu trabalho. O ThreadPool gerencia então, como já se esperava, um pool de threads, para as quais ele atribui as Tasks que recebe usando uma fila. É nesta fila onde as Tasks ficam armazenadas até que haja um thread livre no pool, para então iniciar seu processamento. Por padrão, o número mínimo de threads do pool é igual ao número de processadores lógicos do host. E aqui está o detalhe em seu funcionamento: quando existem mais Tasks a serem executadas do que o número de threads do pool, o ThreadPool pode esperar uma thread ficar livre ou criar mais threads. Se escolher criar uma nova thread e se o número atual de threads do pool for igual ou maior que o número mínimo configurado, este crescimento demora entre 1 e 2 segundos para cada nova thread adicionada ao pool. Observação: a partir do .Net 6 foram introduzidas melhorias neste processo, permitindo que haja um aumento mais rápido no número de threads do ThreadPool, mas ainda assim a ideia principal se mantém. Vamos a um exemplo para deixar mais claro: suponha um computador com 4 cores. O valor mínimo do ThreadPool será 4. Se todas as Tasks que chegarem processarem rapidamente seu trabalho, o pool poderá inclusive ter menos do que o mínimo de 4 threads ativas. Agora, imagine que 4 Tasks de duração um pouco mais longa chegaram simultaneamente, utilizando então todas as threads do pool. Quando a próxima Task chegar à fila, ele precisará esperar entre 1 e 2 segundos, até que uma nova thread seja adicionada ao pool, para então sair da fila e começar a processar. Se esta nova Task também tiver uma duração mais longa, as próximas Tasks esperarão novamente na fila e precisarão “pagar o pedágio” de 1 a 2 segundos antes de poderem começar a executar. Se esse comportamento de novas Tasks de longa duração se mantiver por algum tempo, a sensação para os clientes deste processo será de lentidão, para qualquer nova tarefa que chegar à fila do ThreadPool. Este cenário é chamado de exaustão do ThreadPool (ThreadPool exhaustion ou ThreadPool starvation). Isso ocorrerá até que as Tasks finalizem seu trabalho e comecem a retornar as threads ao pool, possibilitando a redução da fila de Tasks pendentes, ou que o pool consiga crescer suficientemente para atender à demanda atual. Isso pode demorar vários segundos, dependendo da carga, e só então a lentidão observada anteriormente deixará de existir. Código síncrono x assíncrono É preciso agora fazer uma distinção importante sobre tipos de trabalho de longa duração. Geralmente eles podem ser classificados em 2 tipos: limitados pela CPU/GPU (CPU-bound ou GPU-bound), como a execução de cálculos complexos, ou limitados por operações de entrada/saída (I/O-bound), como o acesso a bancos de dados ou chamadas à rede. No caso de tarefas CPU-bound, salvo otimizações de algoritmos, não há muito o que fazer: é preciso ter processadores em quantidade suficiente para atender à demanda. Mas, no caso de tarefas I/O-bound, é possível liberar o processador para responder a outras requisições enquanto se espera a finalização da operação de I/O. E é exatamente isso que o ThreadPool faz quando APIs assíncronas de I/O são usadas. Neste caso, mesmo que a tarefa específica ainda seja demorada, a thread será retornada para o pool e poderá atender a uma outra Task da fila. Quando a operação de I/O finalizar, a Task será enfileirada novamente para então continuar a executar. Para saber mais detalhes sobre como o ThreadPool aguarda o fim de operações de I/O, clique aqui. Entretanto, é importante observar que ainda existem APIs síncronas de I/O, que causam o bloqueio da thread e impedem sua liberação para o pool. Estas APIs - e qualquer outro tipo de chamada que bloqueie uma thread antes de retornar a execução - comprometem o bom funcionamento do ThreadPool, podendo causar sua exaustão quando submetidos a cargas suficientemente grandes e/ou longas. Podemos dizer então que o ThreadPool – e por extensão o ASP.NET Core/Kestrel, desenhados para operar assincronamente – é otimizado para execução de tarefas de baixa complexidade computacional, com cargas I/O bound assíncronas. Neste cenário, um pequeno número de threads é capaz de processar um número bastante elevado de tasks/requisições de maneira eficiente. Bloqueio de threads com ASP.NET Core Vamos ver alguns exemplos de código que causam o bloqueio de threads do pool, usando ASP.NET Core 8. Observação: estes códigos são exemplos simples, que não visam representar nenhuma prática, recomendação ou estilo em especial, exceto os pontos relacionados à demonstração do ThreadPool especificamente. Para manter o comportamento idêntico entre os exemplos, será usado uma requisição a banco de dados SQL Server que simulará uma carga de trabalho que demora 1 segundo para retornar, usando a sentença WAITFOR DELAY. Para gerar uma carga de utilização e demonstrar os efeitos práticos de cada exemplo, utilizaremos o siege, um utilitário de linha de comando gratuito destinado a esta finalidade. Em todos os exemplos será simulada uma carga de 120 acessos concorrentes durante 1 minuto, com um atraso aleatório de até 200 milissegundos entre as requisições. Estes números são suficientes para demonstrar os efeitos sobre o ThreadPool sem gerar timeouts no acesso ao banco de dados. Versão Síncrona Vamos começar com uma implementação completamente síncrona: A action DbCall é síncrona, e o método ExecuteNonQuery do DbCommand/SqlCommand é síncrono, portanto, bloqueará a thread até que haja o retorno do banco de dados. Abaixo está o resultado da simulação da carga (com o comando siege utilizado). Vejam que conseguimos uma taxa de 27 requisições por segundo (Transaction rate), e um tempo de resposta médio (Response time) de cerca de 4 segundos, com a requisição mais longa (Longest transaction) durando mais de 16 segundos – um desempenho bastante precário. Versão Assíncrona – Tentativa 1 Vamos agora utilizar uma action assíncrona (retornando Task<string>), mas ainda utilizar o método síncrono ExecuteNonQuery. Executando o mesmo cenário de carga anterior, temos o seguinte resultado. Vejam que o resultado foi ainda pior neste caso, com taxa de requisições de 14 por segundo (contra 27 da versão completamente síncrona) e tempo médio de resposta de mais de 7 segundos (contra 4 da anterior). Versão Assíncrona – Tentativa 2 Nesta próxima versão, temos uma implementação que exemplifica uma tentativa comum – e não recomendada – de transformar uma chamada de I/O síncrona (no nosso caso, o ExecuteNonQuery ) em uma “API assíncrona”, usando Task.Run. O resultado, após a simulação, mostra que o resultado é próximo da versão síncrona: taxa de requisições de 24 por segundo, tempo médio de resposta de mais de 4 segundos e requisição mais longa levando mais de 14 segundos para retornar. Versão Assíncrona – Tentativa 3 Agora a variação conhecida como “sync over async”, onde utilizamos métodos assíncronos, como o ExecuteNonQueryAsync deste exemplo, mas é chamado o método .Wait() da Task retornada pelo método, como mostrado abaixo. Tanto o .Wait() quanto a propriedade .Result de uma Task tem o mesmo comportamento: causam o bloqueio da thread em execução! Executando nossa simulação, podemos ver abaixo como o resultado também é ruim, com taxa de 32 requisições por segundo, tempo médio de mais de 3 segundos, com requisições chegando a levar 25 segundos para retornar. Não à toa, o uso de .Wait() ou .Result em uma Task é desaconselhado em código assíncrono. Solução do problema Finalmente, vamos ao código criado para funcionar da forma mais eficiente, através do de APIs assíncronas e aplicando async / await corretamente, seguindo recomendação da Microsoft. Temos então a action assíncrona, com a chamada ExecuteNonQueryAsync com await. O resultado da simulação fala por si só: taxa de requisições de 88 por segundo, tempo médio de resposta de 1,23 segundos e requisição levando no máximo 3 segundos para retornar - números em geral 3 vezes melhores que qualquer opção anterior. A tabela abaixo sumariza os resultados das diferentes versões, para uma melhor comparação dos dados entre elas. Versão do códigoTaxa de requisições ( /s)Tempo médio (s)Tempo máximo (s)Síncrona27,384,1416,93Assíncrona 114,337,9414,03Assíncrona 224,904,5714,80Assíncrona 332,433,5225,03Solução88,911,233,18 Solução paliativa Vale mencionar que podemos configurar o ThreadPool para ter um número mínimo de threads maior do que o padrão (o número de processadores lógicos). Com isto, ele conseguirá rapidamente aumentar o número de threads sem pagar aquele “pedágio” de 1 ou 2 segundos. Existem pelo menos 3 formas para se fazer isto: por configuração dinâmica, usando o arquivo runtimeconfig.json, por configuração do projeto, ajustando a propriedade ThreadPoolMinThreads, ou por código, chamando o método ThreadPool.SetMinThreads. Isto deve ser encarado como uma medida temporária, enquanto não se faz os devidos ajustes em código como mostrado anteriormente, ou após os devidos testes prévios para confirmar que traz benefícios sem efeitos colaterais de desempenho, conforme recomendação pela Microsoft. Conclusão A exaustão do ThreadPool é um detalhe de implementação que pode trazer consequências inesperadas. E que podem ser difíceis de detectar se considerarmos que .Net possui várias maneiras de obter o mesmo resultado, mesmo em suas APIs mais conhecidas – acredito que motivado por anos de evoluções na linguagem e do ASP.NET, sempre visando compatibilidade retroativa. Quando falamos de funcionamento em taxas ou volumes crescentes, como ao passar de dezenas para centenas de requisições, é essencial conhecer as práticas e recomendações mais recentes. Além disso, conhecer um ou outro detalhe de implementação pode ser um diferencial para se evitar problemas de escala ou diagnosticá-los mais rapidamente. Fique de olho nas próximas publicações do Proud Tech Writers. Em um próximo artigo, vamos explorar como diagnosticar a exaustão do ThreadPool e identificar a origem do problema em código a partir de um processo em execução.

O que é UX Writing e tudo o que você precisa saber para criar experiências incríveis
Tech Writers Fevereiro 11, 2025

O que é UX Writing e tudo o que você precisa saber para criar experiências incríveis

O que é UX Writing e como impacta positivamente o produto de um negócio? Veja boas práticas, responsabilidades, metodologias, e muito mais! UX Writing envolve a criação de conteúdo valioso em interfaces e produtos digitais, incluindo textos, baseando-se na experiência do usuário, ou seja, visando entregar a melhor experiência para o público. Esta prática está relacionada a conceitos de marketing, design e arquitetura da informação, e tem como objetivo encantar e oferecer valor por meio de peças informativas. Um exemplo de UX Writing é quando você acessa alguma plataforma de ensino online, ou algum aplicativo que, assim que você faz o seu login, já demonstra com um tutorial objetivo cada passo que o usuário deve dar. Abaixo, um exemplo do aplicativo ProJuris ADV da Softplan, que mostra uma interface clean e amigável antes do usuário decidir se vai criar uma conta no aplicativo ou efetuar login, mostrando algumas coisas que podem ser executadas na aplicação. A experiência do usuário tem se tornado cada vez mais importante para a atração, conversão e retenção de clientes. Aspectos como a agilidade de navegação do seu site, a escaneabilidade e a maneira intuitiva de navegar, e, até mesmo as cores escolhidas para o design das páginas afetam diretamente as decisões dos usuários em qualquer plataforma digital. Quando falamos de plataformas digitais com UX Writing, podemos ver como exemplo, o Gestor Obras, que, já na primeira página do sistema, mostra um tutorial prático de como ele funciona. Ao passo que você clica onde tem a indicação, ele irá mostrar os próximos passos e funções de cada parte do sistema. Um outro exemplo de UX Writing, que passa informações diretas e objetivas, é no Sienge, mostrando em uma imagem algumas vantagens de utilizar o sistema, além de uma comunicação direta no CTA “Peça uma Demonstração”, fugindo do comum “Saiba Mais” e chamando para uma ação bem objetiva. Por isso, se a sua empresa ainda não otimiza constantemente seus canais de comunicação com o usuário, principalmente seu website, chegou a hora de revisar algumas escolhas. Afinal, o usuário é quem utiliza seu produto ou serviço. Para isso, não apenas o design do site é crucial na hora da otimização, mas também o atendimento e a  comunicação clara e objetiva nos canais da marca serão essenciais para criar mais conexão com seus consumidores. Para se ter uma ideia da experiência do usuário e como ela é agregadora, este ano foi realizada uma pesquisa pela Foundever, que revelou que 80% dos clientes consideram a experiência como um aspecto muito mais valioso do que os próprios produtos e serviços. Quando executada de forma eficiente, a prática de UX Writing se torna uma vantagem competitiva significativa em um mercado cada vez mais rigoroso em termos de qualidade e usuários que exigem os melhores produtos digitais. Quais são as principais características de UX Writing? UX Writing consiste em algumas características importantes para que seja executado de maneira correta e consistente. É importante ter isso em mente para melhores criações que realmente irão causar impacto na experiência do usuário positivamente. Clareza e Objetividade: o conteúdo deve ser claro e direto, facilitando a compreensão rápida pelo usuário. Consistência: a linguagem e o tom devem ser uniformes em todos os pontos de contato com o usuário, criando uma experiência coesa. Empatia: entender e antecipar as necessidades e expectativas dos usuários para criar textos que realmente os ajudem. Foco na Ação: orientar os usuários sobre o que fazer a seguir, utilizando chamadas para ação (CTAs) claras. Brevidade: utilizar o menor número de palavras possível sem sacrificar a clareza, respeitando o tempo e a atenção dos usuários. Escaneabilidade: estruturar o texto de forma que seja fácil de ler e percorrer rapidamente, com o uso de títulos, subtítulos, listas e parágrafos curtos. Acessibilidade: garantir que o conteúdo seja acessível a todos os usuários, incluindo aqueles com algum tipo de deficiência, através de uma linguagem simples e inclusiva. Orientação Visual: integrar o texto de maneira harmoniosa com os elementos visuais da interface, contribuindo para uma experiência de usuário agradável e intuitiva. Personalização: adaptar o conteúdo ao contexto e às preferências do usuário, oferecendo uma experiência mais relevante e personalizada. Tom e Voz da Marca: refletir a personalidade e os valores da marca em todos os textos, fortalecendo a identidade e a conexão com o público. Exemplos da aplicação de UX Writing É fácil confundir UX Writing com outras estratégias de escrita. Por isso, vamos demonstrar como aplicar UX Writing em seu website ou aplicações digitais. Personalização Quer ver um exemplo de UX Writing com personalização? O Spotify é um serviço de streaming, que, ao passo que você o utiliza, personaliza músicas que acabam sendo indicadas para os usuários, com músicas parecidas que o usuário costuma ouvir. Além disso, todo fim de ano, a plataforma entrega para cada usuário, seu resumo anual do que mais foi ouvido durante todo o período, além de quais artistas, podcasts e gêneros foram ouvidos. Tudo isso é feito com uma linguagem objetiva e clara para o usuário entender exatamente todo seu resumo, sem espaços para dúvidas. Foto: Reprodução/Spotify  Textos Objetivos e Claros Agora, para um aplicativo, por exemplo, é essencial que os textos sejam bem objetivos! Assim, as taxas de erro do usuário ao utilizá-lo certamente serão muito menores, além da navegabilidade ser mais intuitiva. Exemplo bom e ruim de um botão de ação com UX Writing aplicado. Fonte: Adobe Se antecipe aos erros Já falamos sobre a importância de dar ao usuário uma boa experiência, e isso inclui antecipar para ele, qualquer possibilidade de erro futuro. No exemplo abaixo, podemos ver o preenchimento de um formulário, onde o e-mail não é preenchido corretamente e a aplicação comunica que o usuário veja a mensagem, próxima do campo preenchido com erro, do lado esquerdo. Fonte: Adobe Diferenças entre Copywriting, UX Writing e Tech Writing  Embora relacionadas, as estratégias de copywriting, UX Writing e Tech Writing têm suas diferenças. Vamos ver quais são as principais dentro de algumas abordagens? Objetivo Copywriting: o objetivo é persuadir o leitor a tomar uma ação específica, como comprar um produto, inscrever-se em uma newsletter ou clicar em um link. Portanto, é focado em conversões e vendas. UX Writing: facilita a interação do usuário com um produto ou serviço digital, tornando a experiência mais intuitiva, agradável e eficiente. Com UX Writing, o usuário é guiado através da interface e na conclusão de tarefas. Tech Writing: o objetivo é explicar de maneira clara e precisa como usar produtos ou tecnologias complexas. Focado em fornecer instruções detalhadas e informativas. Abordagem Copywriting: utiliza técnicas de persuasão e retórica para capturar a atenção do leitor e motivá-lo a ter alguma ação. O tom é mais emocional e apelativo. UX Writing: adota uma abordagem funcional e informativa, priorizando clareza, simplicidade e utilidade. O tom é objetivo, focado em orientar e ajudar o usuário. Tech Writing: foca em detalhamento e precisão, proporcionando instruções passo a passo e explicações técnicas. O tom é técnico e informativo, com linguagem clara e objetiva. Local de Aplicação Copywriting: encontrado em materiais de marketing como anúncios, e-mails promocionais, páginas de vendas, posts de blog e conteúdo para redes sociais. UX Writing: presente em interfaces digitais, como aplicativos, sites, e-commerce, dashboards, e qualquer ponto de interação do usuário com o sistema. Exemplos incluem botões, mensagens de erro, instruções e menus de navegação. Tech Writing: aparece em manuais de usuário, guias de instalação, documentação de software, FAQs, tutoriais e bases de conhecimento. Métricas de Sucesso Copywriting: medido por métricas de conversão, como taxa de cliques (CTR), taxa de conversão, volume de vendas e retorno sobre investimento (ROI). UX Writing: medido pela usabilidade e satisfação do usuário, como taxas de erro reduzidas, tempo de conclusão de tarefas, retenção de usuários e feedback positivo sobre a experiência do usuário. Tech Writing: medido pela clareza e eficácia da documentação, como número de tickets de suporte, feedback dos usuários, tempo para encontrar informações e facilidade de uso da documentação. Colaboração Copywriting: colabora com equipes de marketing, vendas e branding. UX Writing: trabalha com designers de UX/UI, desenvolvedores, pesquisadores de experiência do usuário e gerentes de produto para integrar a escrita ao design e à funcionalidade do produto. Tech Writing: colabora com engenheiros, desenvolvedores, gerentes de produto e equipes de suporte para garantir que a documentação seja precisa e útil. Por fim, enquanto o copywriting busca persuadir e converter, o UX writing visa facilitar e orientar, e o tech writing se concentra em explicar e instruir. Cada estratégia utiliza a escrita como ferramenta principal, mas com focos e aplicações diferentes, mas que se complementam em algum momento da jornada do usuário. Como aplicar UX Writing em Produto para agregar valor? Agora que você já entende o que é UX Writing, já pode entender como aplicar a estratégia. Nesse caso, quando falamos de UX Writing e Produto, esses termos devem andar lado a lado na criação e otimização constante de um produto. Aqui inclusive podemos falar sobre o “Product Writer”, profissional totalmente focado em atuar em Produto que busca melhorias, pesquisando e entendendo o ponto de vista dos usuários sobre um determinado produto e definem soluções de escrita. Então, devemos entender como UX Writing agrega valor a produtos digitais de diversas maneiras, contribuindo significativamente para a experiência do usuário e, consequentemente, para o sucesso do produto. Vamos ver algumas práticas que podem ser executadas em produtos digitais? 1. Clareza nas Mensagens de Erro e Sucesso Mensagens de Erro: devem ser claras e específicas, informando ao usuário o que deu errado e como corrigir o problema. Por exemplo, "A senha deve ter pelo menos 8 caracteres" é mais útil do que "Erro na senha". Mensagens de Sucesso: confirmações claras que informam ao usuário que a ação foi concluída com sucesso. Por exemplo, "Sua compra foi realizada com sucesso!". 2. Instruções e Guias de Uso Onboarding: fornecer tutoriais e guias passo a passo para novos usuários, ajudando-os a se familiarizar com o produto. Tooltips e Pop-ups: instruções contextuais que aparecem no momento certo para orientar o usuário sem interromper sua experiência. 3. Chamadas para Ação (CTAs) Eficientes Botões e Links: usar verbos de ação claros e diretos, como "Compre Agora", "Inscreva-se" ou "Saiba Mais". Evitar termos vagos como "Clique Aqui". Hierarquia Visual: garantir que os CTAs sejam visualmente destacados para guiar a atenção do usuário. 4. Melhoria na Navegação Menus e Labels: usar terminologia familiar e intuitiva nos menus e rótulos. Por exemplo, "Conta" em vez de "Perfil do Usuário". Breadcrumbs: implementar breadcrumbs para ajudar os usuários a entenderem onde estão na navegação do site e como voltar a páginas anteriores. 5. Microcopy Formulários: fornecer instruções claras e concisas para cada campo de entrada. Exemplos: "Digite seu e-mail" ao invés de apenas "E-mail". Feedback Imediato: oferecer feedback instantâneo ao preencher formulários, como marcar campos corretos com um sinal verde. 6. Ajustes para Acessibilidade Texto Alternativo (Alt Text): adicionar descrições úteis em imagens, gráficos e ícones para melhorar a acessibilidade. Linguagem Simples: evitar jargões e termos técnicos complexos, tornando o conteúdo acessível a todos os usuários, incluindo aqueles com deficiências cognitivas. 7. Consistência no Tom de Voz Manual de Estilo: desenvolver e aderir a um manual de estilo que define a voz e o tom da marca, garantindo uma comunicação consistente em todas as plataformas. Revisão Regular: revisar e atualizar regularmente os conteúdos para manter a consistência e a relevância. 8. Conteúdo Educativo FAQs e Documentação: criar e manter seções de perguntas frequentes e documentação de ajuda que sejam claras, detalhadas e fáceis de navegar. Vídeos Tutoriais e Dicas: integrar vídeos e dicas rápidas que ajudem os usuários a entenderem e utilizar melhor as funcionalidades de um produto. 9. Testes e Interações Testes A/B: realizar testes A/B para avaliar a eficácia de diferentes versões de microcopy, CTAs e mensagens de erro. Feedback do Usuário: coletar e analisar feedback dos usuários para identificar áreas de melhoria e ajustar o conteúdo conforme necessário. Conclusão Perceba como muitas ações podem ser bem simples e que irão ajudar muito um produto a entregar uma boa experiência do usuário, com mais eficiência e satisfação. O seu produto pode acabar criando mais conexão com seus usuários, estimulando a fidelização e, assim, criando uma rede de consumidores que organicamente irão evangelizar sobre o seu produto e como ele vale a pena. Por fim, não perca tempo. Investir em Experiência do Usuário irá render melhorias para seu produto ou serviço e tornará a sua empresa mais acessível e em destaque no mercado.

Angular: Por que você deve considerar esse framework front-end para sua empresa
Tech Writers Fevereiro 02, 2024

Angular: Por que você deve considerar esse framework front-end para sua empresa

Um medo de toda equipe é escolher uma ferramenta que se tornará obsoleta rápido. Caso você esteja desenvolvendo aplicações há alguns anos, possivelmente já vivenciou isso. Por isso, a escolha de boas ferramentas é uma tarefa que envolve responsabilidade, pois pode guiar o projeto (e a empresa) para o sucesso ou para um mar de problemas e gastos. Neste artigo, vamos entender os usos e benefícios do framework Angular. Escolher um framework front-end não é diferente e também envolve pesquisas e estudos. A escolha de uma “stack”, como chamamos nesse mundo, é trivial tanto para o presente mas também para o futuro. No entanto, algumas perguntas irão surgir no meio dessa escolha:  Encontraremos profissionais capacitados para lidar com esse framework?  Conseguiremos manter um ritmo de atualizações?  Há um plano bem definido para a direção que o framework está indo?  Há uma comunidade (entendemos aqui também por grandes empresas apoiando) engajada?  Todas essas perguntas devem ser respondidas antes de começar qualquer projeto, pois negligenciar uma telas pode levar a cenários devastadores para o produto, e consequentemente para a empresa e seus lucros.  Motivações para se usar um framework  A resposta talvez mais direta seja que as vezes é bom não ficar reinventando a roda. Problemas rotineiros como lidar com rotas de uma aplicação web, ou mesmo o controle de dependências, geração do bundle otimizado para publicação em produção, todas essas tarefas já tem boas soluções desenvolvidas, e, por isso, escolher um framework que te entrega esse conjunto de ferramentas é perfeito para ganhar produtividade, solidez no desenvolvimento de uma aplicação e também manter a mesma sempre atualizada seguindo as melhores práticas.  Bem como as motivações diretas, posso citar também:  A facilidade de encontrar ferramentas que se integram com o framework  A busca por um software com qualidade, integrado a testes e outras ferramentas que irão tornar o processo de desenvolvimento maduro  Muitas situações e problemas já resolvidos (porque há muita gente trabalhando com a tecnologia)  Motivações para o uso do framework do Angular:  Construído usando Typescript, uma das linguagens mais populares do momento  Arquitetura MVC  Controle e Injeção de dependências  Modularização (com opção de carregamento lazy load)  Boas bibliotecas para integração  Comunidade grande e engajada  1835 contribuidores no repositório oficial  Oficialmente apoiada e mantida pelo time do Google  A solidez do Angular  Atualmente, podemos afirmar com clareza que o framework é estável, recebendo atualizações frequentes por sua natureza fundada no open-source. Isso porque é mantido pelo time do Google, que procura sempre deixar o roadmap do que está por vir o mais claro possível, o que é muito bom. Além disso, a comunidade Angular é bastante ativa e engajada. É difícil você ter um problema que já não foi resolvido.  Uma das preocupações de todo desenvolvedor é em relação a mudanças drásticas de uma ferramenta. Quem viveu a época da mudança da V1 para V2 do Angular sabe essa dor, praticamente a mudança foi total. Porém, o framework de forma correta se fundou com base no Typescript que trouxe robustez e mais um motivo para sua adoção: com o Typescript, temos possibilidades que o Javascript sozinho não consegue resolver: tipagem forte, integração com o IDE facilitando a vida dos desenvolvedores, reconhecimento de erros em tempo de desenvolvimento, e muito mais.  Atualmente, o framework está na versão 17 e vem ganhando cada vez mais maturidade e solidez, com o incremento de funcionalidades inovadoras como defer lançada recentemente.  Upgrade facilitado  O framework fornece um guideline para todo upgrade através do site https://update.angular.io, esse recurso ajuda muito a guiar a atualização do seu projeto.  CLI completo  O Angular é um framework. Logo, ao instalar seu pacote teremos o CLI pronto para inicializar novos projetos, gerar componentes, executar testes, gerar o pacote final e manter as atualizações da sua aplicação:  Para criar seu primeiro projeto, basta abrir o seu terminal, e rodar o comando a seguir:  Sólidos projetos de interface  Se você precisa de um design para sua aplicação que fornece componentes já prontos para uso como alertas, janelas de modal, avisos snackbar, tabelas, cards, uma das possibilidades mais populares é a escolha do Material Angular, um bom ponto para seguir o seu software com ele é porque é mantido pelo Google, logo sempre que o framework avança em versão o Material usualmente acompanha essa atualização.  Além do Material, existem outras opções na comunidade, como o PrimeNG, que trás um conjunto muito interessante (e grande) de componentes.  Suporte a biblioteca Nx  O Angular possui suporte completo ao projeto Nx, que possibilita escalar seu projeto de forma bastante consistente, garantindo principalmente cache e possibilidades avançadas para você manter e escalar sua aplicação local ou no seu ambiente de CI.  Aqui estão alguns exemplos específicos de como o Nx pode ser usado para melhorar um projeto Angular:  Você pode criar uma biblioteca Angular que pode ser reutilizada em vários projetos.  Você pode criar um monorepo que contenha todos os seus projetos Angular, o que facilita a colaboração entre equipes.  Você pode automatizar tarefas de desenvolvimento comuns, como a execução de testes e a implantação de seus projetos.  Testes (unitários e E2E)  Além do Karma e o Protactor que nasceram com o framework, agora você é livre para usar projetos populares como Jest, Vitest e Cypress.  State com Redux  Uma das bibliotecas mais utilizadas pela comunidade é a NgRx Store, que fornece gerenciamento de estado reativo para aplicativos Angular inspirados em Redux.  GDEs brasileiros  No Brasil temos atualmente dois GDEs Angular, o que é importante para nosso país e também para geração de conteúdo Angular em português, trazendo para nossa comunidade novidades e insights sempre atualizados direto do time do Google.  Loiane Gronner William Grasel Alvaro Camillo Neto Grandes empresas utilizando e apoiando  Talvez a mais notória seja o Google, mantenedor oficial do framework. A empresa possui diversos produtos construídos usando Angular e nos últimos anos vem apoiando ainda mais o desenvolvimento e evolução da ferramenta.  Um ponto importante ao escolher um framework é saber que grandes empresas estão utilizando, isso porque nos dá um sinal de que aquela ferramenta terá apoio para atualizações e evolução já que ninguém gosta de ficar reescrevendo produtos do zero, aqui vou citar algumas empresas globais que usam Angular em seus produtos, sites, serviços para a web:  Google  Firebase  Microsoft  Mercedes Benz  Santander  Dell  Siemens  Epic  Blizzard’s  No cenário nacional também temos exemplos de grandes empresas utilizando o framework com sucesso, podemos citar algumas:  Unimed Cacau Show  Americanas  Checklist Fácil  Picpay  Quer saber mais? Se interessou em começar com Angular? Acesse https://angular.dev/, a mais nova documentação do framework que trás tutoriais, playground e uma boa documentação bem explicada.  Bom código! 

Modelo Arquitetural: como escolher o ideal para seu projeto
Tech Writers Janeiro 17, 2024

Modelo Arquitetural: como escolher o ideal para seu projeto

O que é um Modelo Arquitetural e qual a sua importância?  Basicamente, um modelo arquitetural é a estrutura abstrata sobre a qual sua aplicação será implementada.  "A arquitetura de software de um programa ou sistema computacional é a estrutura ou estruturas do sistema que abrange os componentes de software, as propriedades externamente visíveis desses componentes e as relações entre eles.” (Bass, Clements, & Kasman, Software Architecture in Practice)  Para definir o modelo que mais irá se adequar ao seu projeto, precisamos conhecer bem as estratégias de curto, médio e longo prazo da empresa, os requisitos não funcionais e arquiteturais do software, assim como a curva de crescimento de usuários ao longo do tempo e a volumetria de requisições.  Bem como os pontos citados ao longo deste artigo, existem ainda outros a levar em conta na decisão de qual modelo arquitetural aplicar. Como exemplo, podemos listar:  Preocupações de segurança;  Armazenamento de dados;  Lockins;  Volume total de usuários;  Volume de usuários simultâneos;  TPS (transações por segundo);  Plano de disponibilidade/SLA;  Requisitos legais;  Disponibilidade em um ou mais tipos de plataformas;  Integrações.  O levantamento de arquitetura, RAs (requisitos arquiteturais), VAs (variáveis arquiteturais), RFs (requisitos funcionais), RNFs (requisitos não funcionais) e os critérios que definem cada um desses itens influenciam diretamente na escolha do modelo correto.  A escolha do modelo arquitetural pode impactar todo o life cycle da aplicação. Por isso, esse é um assunto que devemos tratar com grande atenção. O uso de MVPs (principalmente aqueles que não vão para produção), podem auxiliar muito nessa tarefa. Eles dão uma oportunidade única de errar, ajustar, errar novamente, provar conceitos, ajustar e errar quantas vezes forem necessárias para que no final o software possua a arquitetura na versão mais acertada, trazendo assim os verdadeiros ganhos dessa escolha.  Como estão divididos os modelos arquiteturais  É ideal deixar claro que como muitas definições no mundo do software, o que é, e quais são os modelos arquiteturais podem variar. Por isso, neste artigo procurei dividi-los em quatro grandes grupos: monolítico, semimonolítico (ou monolito modular), monolito distribuído (ou microlito) e microcomponentizado.  Monolítico  Modelo em que todos os componentes formam um único aplicativo ou executável integrados em um único código-fonte. Neste caso, todo ele é desenvolvido, implantado e escalado como unidade única.  Figura 1 – Exemplo de Modelo Monolítico. Prós  Simplicidade: como o aplicativo é tratado como uma unidade única e coesa, ele se torna mais simples, já que todas as partes estão contidas em um único código-fonte.  Maior aderência a Padrões de Projeto: levando em conta que temos um único código-fonte, outro fator que facilita é que os próprios padrões de projetos (Design Patterns, 01/2000) foram escritos em épocas de dominância dos monolitos, tornando a aplicação dos mesmos mais aderente.  Maior desempenho: devido à baixa latência na comunicação, os monolitos costumam ter um bom desempenho, mesmo utilizando tecnologias mais defasadas.  Menor consumo de recursos: a baixa complexidade, a simplicidade e a menor sobrecarga de comunicação entre camadas favorecem o menor consumo de recursos.  Troubleshooting facilitado: Criação dos ambientes de desenvolvimento e debugs são feitos de forma facilitada em monolitos, pois neles os componentes partilham dos mesmos processos. Outro fator que podemos levar em conta é que monolitos possuem menos pontos de falha externos, simplificando a busca por erros.  Contras  Tamanho de times limitados: quebras relacionadas a Continuous Integration e conflitos de merge acontecem com maior regularidade em monolitos, gerando dificuldades de trabalho paralelo para grandes times.  Escalabilidade: a escalabilidade pode ser limitada em certos aspectos. Mesmo com facilidade na escalabilidade vertical, muitas vezes a escalabilidade horizontal pode se tornar um problema que poderá afetar o crescimento da aplicação.  Disponibilidade de janelas: normalmente, para um monolito ocorrem trocas de executáveis, o que necessita de uma janela de disponibilidade sem que usuários acessem o aplicativo, o que não acontece com outros modelos arquiteturais que podem utilizar outras técnicas de deploy como o Blue-Green ou até mesmo trabalhar com imagens ou pods. Tecnologia única: a baixa diversidade tecnológica muitas vezes pode se tornar um impedimento para o crescimento da aplicação por atender somente um tipo de sistema operacional, por exemplo, ou não atender de forma integral novas features solicitadas pelos clientes por não possuir atualizações que tenham capacidade de solucionar problemas complexos.  Maior gasto com compilação e execução: grandes monolitos geralmente têm um tempo elevado para compilar e fazer a execução local, gerando um maior comprometimento de tempo de desenvolvimento.  Quando Usar  Escalabilidade e Disponibilidade Baixas: se a aplicação possui uma escala limitada em que, por exemplo, o número de usuários é baixo ou a alta disponibilidade não é mandatória, o modelo monolítico é uma boa solução.  Aplicações Desktop: o modelo monolítico é altamente indicado para aplicações desktop.  Times de baixa senioridade: modelos monolíticos, devido à sua simplicidade e localização dos componentes, possibilitam que times de baixa senioridade possam trabalhar com uma melhor performance.  Recursos limitados: para uma infraestrutura limitada com recursos escassos.  Semimonolítico (ou Monolito Modular)  Modelo em que as aplicações são compostas por partes de estruturas monolíticas. Neste caso, a combinação tenta equilibrar a simplicidade do modelo monolítico e a flexibilidade do microcomponentizado. Atualmente, esse modelo arquitetural costuma ser bastante confundido com microsserviços.  Figura 2 – Exemplo de Modelo Semimonolítico.  Prós  Traz benefícios dos modelos monolítico e microcomponentizado: com isso, é possível manter partes como estruturas monolíticas e microcomponentizar somente componentes que têm a real necessidade.  Diversidade tecnológica: possibilidade de usar diversas abordagens tecnológicas.  Infraestrutura diversificada: este modelo pode ser desenvolvido para utilizar tanto infra On-Premise quanto Cloud, favorecendo a migração entre ambas.  Suporta times maiores: a segmentação dos componentes favorece que vários times trabalhem paralelamente, cada qual em seu escopo. Especialidades Técnicas: devido à segmentação, aproveita-se melhor as hard skills do time, tais como frontend, UX, backend, QA, arquitetos, etc.  Contras  Padronização: devido ao grande número de componentes que podem surgir em um modelo semimonolítico, a padronização (ou falta dela) pode se tornar um grande problema.  Complexidade: a complexidade inerente a esse tipo de modelo também tende a aumentar com novas features. Logo, novos recursos como mensageria, cache, integrações, controle de transações, testes, entre outros, podem adicionar ainda mais complexidade ao modelo.  Budget: em modelos que amparam o uso de diversas tecnologias com times grandes, são necessários mais profissionais especialistas e com nível maior de senioridade, ocasionando muitas vezes um gasto maior com despesas de pessoal.  Troubleshooting complexo: a própria complexidade do modelo e a diversidade de tecnologias tornam cada vez mais difícil o troubleshooting da aplicação. Isso se dá devido à grande quantidade de pontos de falhas (inclusive externos à aplicação) que passam a existir e à comunicação entre eles.  Quando Usar  Aceito em Vários Cenários: é um modelo flexível que pode atender vários cenários, porém nem sempre da melhor forma.  Pouca Definição: em projetos que possuem incertezas ou até mesmo que não possuem a total definição de seus requisitos, este modelo é o mais indicado.  Em médias e grandes equipes: como dito, a divisão dos componentes em vários grupos facilita o trabalho paralelo de médias e grandes equipes. Normalmente, os grupos possuem seus próprios repositórios de código, o que torna o trabalho paralelo mais ágil.  Senioridade Diversa: este modelo se beneficia de times com essa formatação, pois softwares semimonolíticos apresentam desafios variados, tanto nas camadas de frontend e backend quanto em questões de infraestrutura (IaC – Infrastructure as a Code).  Infraestrutura: para uma infraestrutura Cloud-based ou híbrida, este modelo é mais aplicável. É um modelo que permite, por exemplo, uma adoção gradual entre On-Premise e Cloud, facilitando a adaptação e minimizando impactos operacionais.  Monolito Distribuído  Essa modelagem é uma modelagem "moderna" que também vem sendo implementada e confundida com o modelo microcomponentizado/microsserviços.   "You shouldn't start a new project with microservices, even if you're sure your application will be big enough to make it worthwhile." (Fowler, Martin. 2015)  Em resumo, neste modelo arquitetural o software é construído com bases do modelo monolítico, mas implantando conforme o modelo microcomponentizado. Atualmente, muitos o consideram um antipattern.  Figura 3 – Exemplo de Modelo Monolíto Distribuído. Não valeria à pena listarmos as características de prós (não sei se tem), mas ainda vale citar características que vão contra: este modelo arquitetural reúne pontos negativos dos outros dois estilos com o qual é confundido.   Nele, os serviços são altamente acoplados e também possuem vários tipos de complexidade, tais como: operacional,  testabilidade, deployment, comunicação e infraestrutura.  O alto acoplamento, principalmente entre os serviços de backend, traz sérias dificuldades no deployment, sem contar com o aumento expressivo de pontos de falha no software.  Microcomponentizado  Modelo de software em que todos os componentes são segmentados em pequenas partes totalmente desacopladas. Dentro de microcomponente, podemos citar:  Microfrontends  Microdatabases  Microvirtualizações  Microsserviços  Microbatches  BFFs  APIs  Figura 4 – Exemplo de Modelo Microcomponentizado. "A microservice is a service-oriented application component that is tightly scoped, strongly encapsulated, loosely coupled, independently deployable, and independently scalable" (Gartner, n.d.).  As opiniões se convergem a dizer que, todo o microsserviço que deu certo, primeiro foi um monolito que ficou grande demais para ser mantido e chegou a um ponto comum de ter que ser separado.  Prós  Escalabilidade: a escalabilidade neste modelo torna-se bastante flexível. Conforme a necessidade, escala-se os componentes de maneira específica. Desenvolvimento Ágil: as equipes podem trabalhar de forma independente em cada componente, facilitando a implantação contínua e acelerando o ciclo de desenvolvimento.  Resiliência: caso um componente falhe, não afeta necessariamente toda a aplicação. Isso melhora a resiliência geral do sistema. É importante salientar que existem abordagens de pontos únicos de falha a fim de evitar esse tipo de problema.  Tecnologia Diversificada: cada componente pode ser desenvolvido utilizando tecnologias diferentes, permitindo a escolha da melhor ferramenta para cada tarefa específica. Além disso, também favorece as skills existentes em cada time.  Facilidade de Manutenção: mudanças em um componente não afetam automaticamente outros, facilitando a manutenção e atualização contínua.  Desacoplamento: os componentes são independentes uns dos outros, o que significa que as mudanças em um serviço não afetam automaticamente outros, facilitando a manutenção.  Contras  Custo: custo elevado de todos os componentes deste modelo (input, output, requisições, armazenamento, ferramentas, segurança, disponibilidade, entre outros).  Tamanho: softwares microcomponentizados costumam ser maiores em sua essência. Não somente o tamanho da aplicação, mas todo o ecossistema que a permeia desde o commit até o ambiente produtivo.  Complexidade Operacional: existe um aumento exponencial da complexidade nesse modelo. Projetar bons componentes arquiteturais para que essa complexidade seja gerida é de grande relevância. É importante escolher e gerir bem ferramentas de logging, APM e Continuous Monitoring, por exemplo. Gerenciar muitos microsserviços pode ser complexo. É necessário um esforço adicional para monitorar, orquestrar e manter os serviços em execução.  Latência: a comunicação entre microsserviços pode se tornar complexa, especialmente em sistemas distribuídos, exigindo estratégias adequadas de comunicação e gerenciamento de API.  Overhead de Rede: o tráfego de rede entre microsserviços pode aumentar, especialmente em comparação com arquiteturas monolíticas, o que pode afetar o desempenho.  Consistência entre Transações: garantir consistência em operações que envolvem vários microsserviços pode ser desafiador, especialmente quando se trata de transações distribuídas.  Testabilidade:  testar interações entre microsserviços pode ser mais complexo do que testar uma aplicação monolítica, exigindo estratégias de teste eficientes.  Infraestrutura: pode ser necessário investir em uma infraestrutura robusta para suportar a execução de vários microsserviços, incluindo ferramentas de orquestração de contêineres e sistemas de monitoramento.  Dispersão Técnica: neste ponto, podemos dizer que existe uma ação da Lei de Conway "Reversa", pois os times, assim como tecnologias e ferramentas, tendem a seguir a dispersão e segregação. Nos times, cada um passa a ter conhecimento de uma pequena parte de um grande todo. Dessa forma, para tecnologias e ferramentas, cada desenvolvedor usa o framework ou ferramentas que mais lhe convém.  Domain-Driven Design: para aumentar as chances de sucesso desse modelo é necessário que os times tenham conhecimento de DDD. Quando Usar  Volumetria: a arquitetura de microsserviços/microcomponentes tem se mostrado eficaz em sistemas de alta volumetria, ou seja, aqueles que precisam lidar com grandes quantidades de transações, dados e usuários.  Disponibilidade: uma das principais razões para se adotar esse tipo de arquitetura é a disponibilidade. Quando bem construído, um software que adota microcomponentização não tende a falhar como um todo quando pequenas partes apresentam problema. Sendo assim, outros componentes continuam em operação enquanto o componente problemático se recupera.  Escalabilidade: se diferentes partes de sua aplicação têm requisitos de escalabilidade distintos, os microsserviços podem ser úteis. Você pode dimensionar apenas os serviços que precisam de mais recursos, em vez de escalar toda a aplicação.  Tamanho dos Times: times pequenos podem ser problemas. Configurações, boilerplates, ambientes, testes, integrações, processos de input e output.  Resiliência > Desempenho": em casos de incerteza, por exemplo, do volume de requisições e até onde ele pode chegar como grandes e-commerces em períodos de alto acesso (black friday) onde é necessário que o software seja mais resiliente e tenha um desempenho mediano.  Checklist Comparativo Figura 5 – Checklist Comparativo entre modelos. Conclusão  Em síntese, a escolha do modelo arquitetural é crucial para o sucesso do projeto, exigindo uma análise criteriosa das necessidades e metas. Cada modelo arquitetural tem suas vantagens e desvantagens e devemos guiar a decisão pelo alinhamento com os requisitos específicos do projeto. Ao considerar as estratégias da empresa, requisitos e levantamentos arquiteturais, é possível tomar uma decisão que impactará positivamente o ciclo de vida da aplicação.  O trabalho (e apoio) do time de arquitetura é de extrema importância. Sendo também de grande importância que a gestão e áreas correlatas apoiem disponibilizando tempo para o levantamento de toda essa gama de informações.   Ficou em dúvida ainda? A princípio, comece pelo semimonolítco/monolito modular. Da mesma forma, dê muita atenção a modelagem do banco de dados.  Referências  Gartner. (s.d.). Microservice. Recuperado de https://www.gartner.com/en/information-technology/glossary/microservice  Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.  Bass, L., Clements, P., Kazman, R. (2013). Software Architecture in Practice (3rd ed.). Addison-Wesley.  Microservices Architecture (12/2023). Recuperado de https://microservices.io/  Fowler, S. J. (2017). Microsserviços Prontos para a Produção. Novatec.  Treinamento ArchExpert. (s.d.). Conteúdo Premium. Recuperado de https://one.archoffice.tech/  Monolith First (06/2015). Recuperado de https://martinfowler.com/bliki/MonolithFirst.html  Microservices. Acessado em 01/2024. Recuperado de https://martinfowler.com/tags/microservices.html

GraphQL em aplicações dotNET
Tech Writers Janeiro 15, 2024

GraphQL em aplicações dotNET

Nesse artigo falarei sobre GraphQL com enfoque em aplicações dotNet. Aqui, mostrarei como os problemas inerentes de REST motivaram a criação do GraphQL. Em seguida, apresentarei os conceitos básicos da especificação desta linguagem. Depois, apresentarei a biblioteca Hot Chocolate, que é uma das muitas bibliotecas que implementam a especificação do GraphQL. Finalmente, mostrarei um pequeno exemplo de uso da referida biblioteca em uma aplicação dotNet.  REST Antes de falarmos sobre GraphQL é necessário falar sobre REST. O termo foi cunhado por Roy Thomas Fielding (2000) em sua tese de doutorado. Neste trabalho, Fielding apresenta REST como um padrão arquitetural para aplicações web definido por cinco restrições:  Client-server: Essa restrição define que a interface com o usuário deve estar separada dos componentes do sistema que processam e armazenam os dados.  Stateless: Essa restrição diz que o cliente não precisa estar ciente do estado do servidor, bem como o servidor não precisa ter ciência do estado do cliente.  Cache: Essa restrição indica que, quando possível, a aplicação servidora deve indicar à aplicação cliente que os dados podem ser armazenados em cache.  Layered system: Essa restrição indica que a aplicação deve ser construída pelo empilhamento de camadas que adicionam funcionalidades umas às outras.  Uniform interface: Essa restrição indica que os recursos da aplicação devem ser disponibilizados de maneira uniforme, de modo que, ao aprender a acessar um recurso, automaticamente sabe-se como acessar os demais. Segundo o trabalho de Fielding, essa é uma das características centrais que distinguem REST dos demais padrões arquiteturais. No entanto, o próprio autor afirma que isso degrada a eficiência da aplicação, pois não há a disponibilização dos recursos de uma maneira que atenda as necessidades específicas de uma determinada aplicação.  Como é o REST na prática Na Figura 1 pode-se ver parte da API do OneDrive da Microsoft. Nessa imagem percebe-se a uniformidade no acesso aos recursos. Isso é perceptível quando notamos que, para obter dados, basta enviar uma requisição GET para um endpoint que começa com o termo drive e é seguido pelo nome do recurso e o seu ID. A mesma lógica vale para a criação de recursos (POST), a modificação de recursos (PUT) e a remoção deles (DELETE).  Acessando a documentação do Google Drive, podemos ver o típico retorno de uma API REST. Percebe-se, na referida documentação, o grande volume de dados que uma única requisição REST pode trazer. Apesar de grande, uma aplicação cliente ainda pode precisar fazer requisições extras para obter mais dados sobre o dono de um arquivo, por exemplo.  Considerando-se as restrições determinadas por Fielding e os exemplos mostrados, é fácil perceber dois problemas inerentes à REST. O primeiro deles é o tráfego de dados que o consumidor não precisa e o segundo é a eventual necessidade de fazer várias requisições para se obter os dados necessários para se montar uma página web.  Figura 1 - Acesse o artigo completo aqui. Entendendo o GraphQL O GraphQL surgiu em 2012 no Facebook como uma solução para os problemas encontrados no padrão REST. Em 2015, a linguagem tornou-se open source e em 2018 criou-se a GraphQL Foundation que passou a ser responsável pela especificação da tecnologia.  É importante destacar que GraphQL não é uma biblioteca ou ferramenta. Assim como SQL, GraphQL é uma linguagem para busca e manipulação de dados. Enquanto usamos o SQL em banco de dados, utiliza-se o GraphQL em APIs. O quadro 1 mostra uma expressão em SQL para recuperar o número de um pedido e o nome do cliente de um banco de dados. Analogamente, o quadro 2 mostra uma expressão GraphQL para obter os mesmos dados de uma API que suporta GraphQL. Nos exemplos, podemos perceber duas grandes vantagens de GraphQL sobre REST. A primeira está presente no fato de GraphQL permitir que o consumidor busque apenas os dados de que necessita para montar sua página web. A segunda está presente no fato de que o consumidor poderia buscar os dados do pedido e do cliente em uma única chamada.  Quadro 1: Exemplo de um select em um banco de dados relacional.  Quadro 2: Exemplo de uma expressão GraphQL. Considero interessante mencionar mais duas características de uma API GraphQL. A primeira delas é a existência de um único endpoint. Ao contrário de REST, em que cria-se um endpoint para cada recurso, em uma API GraphQL todas as queries e mutations são enviadas para o mesmo endpoint.   A segunda é o fato de uma API GraphQL suportar apenas o verbo POST. Essa é mais uma diferença com relação a uma REST, onde deve-se usar verbos HTTP diferentes em função da intenção da requisição. Portanto, enquanto em uma API REST devemos usar os verbos GET, POST, PUT e DELETE, em uma API GraphSQL usaremos o verbo POST para obter, criar, alterar e remover dados.   Schema Definition Language Vamos, agora, falar um pouco sobre a SDL (Schema Definition Language). Ao usar-se um banco de dados relacional é necessário, primeiramente, definir-se o esquema do banco, ou seja, é necessário definir as tabelas, colunas e relacionamentos. Com GraphQL ocorre algo semelhante, ou seja, a API precisa definir um esquema para que os consumidores possam fazer buscas dos dados. Para criar este esquema, usa-se a SDL.   O site oficial do GraphQL tem uma seção dedicada à SDL. Na referida seção pode-se encontrar uma descrição completa da linguagem para criação de esquemas GraphQL. Nesse texto, apresentarei a sintaxe básica para a criação de um esquema GraphQL. Na Figura 2 pode-se ver parte de um esquema GraphQL criado a partir do uso do Apollo. Podemos ver que o esquema inicia-se com a definição de dois tipos fundamentais: Query e Mutation. No primeiro tipo definimos todas queries que nossa API possuirá. No nosso exemplo, os consumidores poderão buscar por customers, products e orders. O tipo Mutation define quais operações de manipulação de dados estarão disponíveis para o consumidor. No exemplo apresentado, o consumidor poderá criar, alterar e remover customers e products. Porém, quando se trata de pedidos (orders), ele poderá criar, adicionar um item, cancelar e fechar o pedido.  Além dos tipos Query e Mutation pode-se perceber a presença dos tipos Customer e Product. Em ambos, existem propriedades do tipo ID, String e Float. Esses três tipos, juntos com os tipos Int e Boolean, são chamados de tipos escalares. O esquema também mostra a definição de um enumerado chamado OrderStatus. Já a Figura 3 mostra a definição de tipos Input que são usados para fornecer dados de entrada para queries e mutations.  Acho importante salientar que a forma de criar-se o esquema varia de acordo com a biblioteca que você escolher. Ao usar-se a biblioteca Apollo para javascript, a definição do esquema pode ser feita através de uma string passada como parâmetro para a função gql ou através da criação de um arquivo (geralmente chamado de schema.graphql). Porém, ao usar-se bibliotecas como Hot Chocolate para dotNet, a definição do esquema é feita pela criação de classes e da configuração de serviços na aplicação. Portanto, a forma de criação do esquema GraphQL pode variar bastante em função da linguagem e da biblioteca escolhida.   Figura 2. Figura 3. Elementos básicos da linguagem GraphQL Como mencionado anteriormente, GraphQL é uma linguagem e, portanto, possui uma sintaxe. Você pode encontrar o guia completo com sintaxe da linguagem no site oficial do GraphQL. No entanto, neste artigo, descreverei os elementos básicos dela.   Realiza-se a busca de dados através de queries, que devem iniciar com a palavra-chave query seguida do nome da query. Se ela possui parâmetros, deve-se abrir parênteses e, dentro deles, deve-se colocar o nome de cada parâmetro seguido do seu valor. Os dois pontos (:) devem ser usados para separar o nome do parâmetro do seu valor. Tendo-se finalizado a lista de parâmetros, deve-se fechar os parênteses. Em seguida, deve-se abrir chaves ({) e colocar dentro delas o nome dos campos que se deseja. Com a lista de campos finalizada, deve-se fechar a chave  (}). O quadro 3 mostra um exemplo simples de query.  Quadro 3: Exemplo de query. Existem cenários onde os parâmetros da query podem ser complexos. Quando um parâmetro é complexo, ou seja, é um objeto com um ou mais campos, deve-se abrir chaves logo depois dos dois pontos. Dentro das chaves, deve colocar o valor de cada campo do objeto e o seu respectivo valor, sendo que ambos devem ser separados por dois pontos (ver quadro 4).  Também existem cenários onde os campos da query podem ser complexos. Nesses casos, deve-se abrir chaves logo depois do nome do campo. Dentro das chaves, deve-se colocar os nomes do campo do objeto (ver quadro 5).  Quadro 4: Exemplo de query. Quadro 5: Exemplo de query. As regras descritas até aqui também servem para mutations. No entanto, essas devem ser iniciadas com a palavra chave mutation em vez de query. É interessante notar que existem outros elementos na sintaxe do GraphQL, mas os elementos descritos até aqui são suficientes para a execução da maior parte das queries e mutations.  Sendo uma linguagem, GraphQL precisa ser implementado por alguma aplicação ou biblioteca. Para que nossa API suporte queries e mutations, precisamos, geralmente, de uma biblioteca. Evidentemente que poderíamos implementar a especificação da linguagem por conta própria, mas isso seria muito improdutivo. A seção “Code” do site GraphQL.org mostra uma lista de bibliotecas que implementam GraphQL para as mais variadas linguagens. Para o mundo dotNet, por exemplo, existem as bibliotecas “GraphQL for .NET”, “Hot Chocolate” e outras.  Quando fala-se sobre as implementações de GraphQL, é necessário falar-se sobre o conceito de “resolvers”. Um resolver é uma função que é acionada pela biblioteca que implementa GraphQL. Essa função é a responsável por efetivamente buscar os dados solicitados pela query. O mesmo ocorre com as mutations, ou seja, quando a biblioteca recebe a solicitação de execução de uma mutation, a biblioteca identifica o resolver que executará as mudanças no banco de dados (insert, update e delete). Perceba, então, que na maioria das bibliotecas a execução das buscas e das alterações nos dados é feita pelo seu próprio código. Percebe-se, então, que as bibliotecas que implementam GraphQL encarregam-se por interpretar a query/mutation enviada pelo chamador e descobrir qual a função adequada para resolver a query/mutation solicitada. Para ver um exemplo de uma API simples que usa Hot Chocolate, acesse o meu GitHub.  Resumindo tudo, GraphQL é uma linguagem criada pelo Facebook com o objetivo de superar os problemas inerentes a REST. A linguagem fornece uma sintaxe simples para se obter dados de uma API bem como alterar dados da mesma. Ela é implementada por uma grande variedade de bibliotecas para as mais diversas linguagens, permitindo que o desenvolvedor crie uma API GraphQL usando a sua linguagem predileta. Referências “GraphQL.” Wikipedia, 9 June 2022, en.wikipedia.org/wiki/GraphQL. Acessado em 6 Nov. 2023.  The GraphQL Foundation. “GraphQL: A Query Language for APIs.” Graphql.org, 2012, graphql.org/.  Thomas Fielding, Roy. “Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST).” Ics.uci.edu, 2000, ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm. Acessado em 6 Nov. 2023.