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
Tipos de Arquitetura no Desenvolvimento de Software
Tech Writers Setembro 05, 2023

Tipos de Arquitetura no Desenvolvimento de Software

No que se refere a disciplina de arquitetura no desenvolvimento de software, nos deparamos com vários tipos de arquitetura. Cada qual, com suas características e especificidades.  Nesse artigo iremos abordar três tipos de arquitetura, são elas: Arquitetura de Software, Arquitetura de Soluções e Arquitetura Corporativa. Apesar de serem arquiteturas diferentes e terem funções específicas em algum momento durante o desenvolvimento elas acabam fazendo interface entre si.  Arquitetura de Software  Essa arquitetura é a que está mais próxima do código e do desenvolvedor. Concentra-se na organização interna de um sistema e aborda a divisão do sistema em componentes, como: classes, módulos ou serviços. Também define qual será a interação entre eles, sendo responsável por aspectos como a modularidade, escalabilidade, segurança e reutilização de código.  Algumas das definições dessa arquitetura:  • Qual a linguagem de programação será adotada; Por exemplo: Java ou .NET C#.  • Quais frameworks e bibliotecas usar na solução;Por exemplo, caso opte pela linguagem de programação Java, utilizar o Spring Boot Framework para facilitar e agilizar o desenvolvimento de aplicativos web e de microsserviços.  • Qual a forma de acesso a camada de dados, e de que maneira ocorrerá a persistência, se por algum framework de Mapeamento Objeto-Relacional ou manualmente. Caso opte por um ORM definir qual será a ferramenta utilizada; Por exemplo: Hibernate ou EclipseLink.  • Qual o modelo arquitetural será usado na implementação e os padrões arquiteturais adequados que atendam aos requisitos do sistema. Por exemplo: MVC, Arquitetura em Camadas, Cliente-Servidor, Hexagonal ou Orientada a Serviços.  • Qual a forma de comunicação entre os componentes do sistema; Por exemplo: HTTP, REST ou Mensageria Assíncrona.  • De que forma se dará a escalabilidade e como o sistema será dimensionado para lidar com aumentos de carga e demanda; Por exemplo: Balanceamento de Carga, Escalonamento Horizontal/Vertical ou Particionamento de Dados.  Qual seguir? Esses tópicos listaram alguns exemplos de decisões arquiteturais que podem ser tomadas durante o processo de definição de uma arquitetura de software. Para essas definições não existe a resposta certa ou errada. Apenas a que melhor se encaixa para atender uma necessidade dentro de um contexto do negócio. Essas decisões são muito importantes e devem ser tomadas com bastante embasamento e cautela, pois mudá-las mais tarde afetará toda a estrutura do sistema, ocasionando custos e atrasos indesejados no projeto. Por isso, é indicado a realização de Provas de Conceitos (POC's) para que as decisões tomadas sejam as mais assertivas possíveis e que garantam um sistema robusto, escalável e de alta qualidade.  É importante que o arquiteto de software tenha um bom conhecimento técnico e com base em suas experiências tome a melhor decisão para que o desenvolvimento seja fluido e cadenciado. Além do mais, o arquiteto deve estar preparado para agir em momentos de crise. Muitas vezes, ele poderá ser demandado para identificar problemas específicos e de complexidade elevada, como por exemplo debugar uma biblioteca ou framework para tomar uma decisão importante e corrigir um problema em produção, fazendo com que a operação volte a funcionar normalmente.  Arquitetura de Soluções  A Arquitetura de Soluções está em uma camada superior em relação a Arquitetura de Software, e é responsável por transformar o requisito do negócio em componentes de engenharia que irão atuar em conjunto para resolver o problema e atender as necessidades do negócio. Abrange a definição dos sistemas envolvidos, a integração entre eles, as interfaces de comunicação e as estratégias para garantir que a solução como um todo funcione de maneira eficaz e atinja seus objetivos. Esse tipo de arquitetura é ideal para identificar áreas dentro de uma organização onde pode haver uma redução de custos, melhorar seu desempenho e automatizar os processos de modo a tornarem mais eficientes, otimizados e padronizados.  A principal diferença entre Arquitetura de Software e Arquitetura de Soluções é que a primeira trata apenas de questões relacionadas a engenharia, enquanto a segunda é responsável por garantir que um produto de software resolva um problema de negócio específico dentro da estratégia de uma empresa. Atividades que estão associadas a esta disciplina: • Compreender e analisar os requisitos do negócio e do sistema para identificar as necessidades e os objetivos da solução; Por exemplo: Entrevistas/workshops, estudos de viabilidade e criação de diagramas de contexto.  • Criar e liderar os processos de integração entre sistemas para atender os requisitos de negócio; Por exemplo: Definição de interfaces, serviços web (API's REST) e Middleware de integração (Kafka).  • Identificar e definir as medidas de segurança necessárias para proteger a solução contra ameaças; Por exemplo: Autenticação, Autorização, Criptografia e proteção de dados sensíveis.   • Colaborar com stakeholders (desenvolvedores, analistas de negócios e gerentes de projeto), para garantir a compreensão e a adoção da arquitetura de soluções; Por exemplo: Comunicação clara das decisões arquiteturais, realização de revisões técnicas e a obtenção de feedback dos envolvidos.  • Identificar os riscos associados à solução e desenvolver estratégia para mitigá-los; Por exemplo: Análise de riscos técnicos, operacionais e segurança.  • Considerar os requisitos de desempenho, como tempo de resposta e capacidade de processamento, e também a capacidade de escalar a aplicação para lidar com picos de aumento de carga; Por exemplo: Uso de caches, escalonamento horizontal, arquitetura de microsserviços ou serviços de mensageria.  Vínculo forte Podemos concluir que a arquitetura de soluções está na divisão entre o técnico e o negócio, e que há um vínculo forte entre essas duas vertentes, considerando a importância de que a solução técnica está sendo definida para resolver um problema de negócio. As habilidades de comunicação na arquitetura de soluções são imprescindíveis, pois há frequentemente um contato direto com a área de negócio, que normalmente tem o conhecimento técnico muito pequeno ou inexistente, com isso, cabe ao papel do arquiteto de soluções ser capaz de explicar e traduzir de forma simples toda a solução técnica que será construída, de uma forma abstrata o suficiente para que todos entendam. Algo importante a se considerar é o conhecimento necessário para representar a arquitetura através de diagramas, como por exemplo o C4, e que ele possa representar de forma clara os componentes do software, a interface com outros sistemas, o fluxo de dados e demais informações que forem pertinentes tanto para a engenharia quanto para o negócio.  Arquitetura Corporativa Define-se arquitetura corporativa como uma série de conhecimentos técnicos e habilidades de gestão que integram a tecnologia aos objetivos do negócio, é uma visão mais ampla que abrange a arquitetura de todo o ambiente de tecnologia da informação da organização. Determina os princípios, políticas e os padrões de governança que orientam a estruturação dos sistemas de software e infraestrutura de uma empresa, se concentrando em como os diferentes sistemas e soluções se encaixam dentro do contexto organizacional, garantindo a consistência, a integração e a eficiência dos recursos de TI.A arquitetura corporativa também considera fatores estratégicos e de negócios, alinhando a tecnologia da informação com os objetivos e necessidades da organização. Ela pode incluir a definição de modelos de processos de negócios, a gestão do portifólio de sistemas, a governança de TI e a definição de políticas de segurança da informação.  Decisões tomadas dentro desse tipo de arquitetura:  • Padronização de linguagem de programação e frameworks utilizados nas soluções dentro da empresa, delimitando quais tecnologias estarão habilitadas para serem utilizadas; Por exemplo: Utilizar a stack Java com Spring Boot Framework em soluções de API.  • Padronização de bases de dados; Por exemplo: SQLServer e Oracle.  • Processos de controle de mudança para auditoria de publicações em ambiente produtivo; Por exemplo: Controle de acesso e permissões, revisão e aprovação de alterações. • Definir e documentar um modelo de arquitetura que a organização irá trabalhar; Por exemplo: Criar um diagrama que ilustre as principais camadas da arquitetura, abrangendo as camadas de apresentação, negócio e dados.  • Avaliar a implantação de novas tecnologias; Por exemplo: Realização de Provas de Conceito (POC's) e pesquisas de mercado.  • Implementar soluções corporativas de DevOps; Por exemplo: Automação de compilação e empacotamento utilizando o Jenkins como esteira de CI/CD.  Importância da arquitetura corporativa Ter uma arquitetura corporativa é muito importante, pois define padrões que a empresa toda deve adotar e seguir, e ajuda a alinhar os objetivos estratégicos da organização com suas atividades operacionais, auxiliando na identificação de redundâncias, ineficiências e oportunidades de otimização, permitindo a simplificação, consolidação e racionalização dos recursos resultando em maior eficiência operacional e redução de custos. A arquitetura corporativa auxilia na gestão do ciclo de vida dos sistemas de TI, desde o planejamento estratégico, estabelecendo padrões de qualidade, governança e conformidade para o desenvolvimento, implantação, operação e manutenção dos sistemas garantindo uma gestão efetiva e sustentável dos ativos de TI.  Conclusão Nesse artigo, abordamos os três principais tipos de arquitetura existentes no desenvolvimento de software, os detalhes de cada tipo e como eles se relacionam entre si. Essa divisão é importante para que cada arquitetura foque em uma área específica de modo que as três juntas possam abranger todo o ciclo de desenvolvimento de um software. Como vimos a arquitetura de software está mais próxima ao código e ao desenvolvedor, a arquitetura de soluções mais voltada ao negócio que a solução pretende resolver e a arquitetura corporativa para definir os padrões tecnológicos que a organização deve adotar para as soluções de software implementadas. A aplicação desses tipos de arquitetura varia de acordo com o tamanho da organização, quanto maior, mais separado e específico os tipos de arquitetura, e quanto menor, menos separação entre os tipos de arquitetura, e muitas das vezes se fundindo em apenas um tipo. É importante salientar que, para cada tipo de arquitetura, pode-se executar um papel diferente por parte do arquiteto, e cada qual com suas habilidades específicas para cada área, porém as habilidades de comunicação, senso crítico, proatividade e resiliência são comuns para todos os tipos de arquitetura. Referências https://blog.xpeducacao.com.br/o-que-e-arquitetura-de-software https://www.infoq.com/br/articles/papel-arquiteto-solucao

Minha vida é como um ticket de suporte
Tech Writers Agosto 30, 2023

Minha vida é como um ticket de suporte

Aqui no time de suporte da Projuris, quando recebemos um ticket (também conhecido por muitos como chamado), analisamos cuidadosamente qual é a demanda do cliente e o qual o seu impacto na operação.   Já dizia o ditado: “a vida imita a arte”. Por isso, posso dizer que nossa vida tem muita relação com um ticket de suporte. Podemos até dividir nas mesmas etapas de análise para resolver um problema. Serviço, categoria e urgência.  Em relação a análise do serviço, podemos comparar com a vida ao separar em áreas todos os pontos da vida, sejam eles na área pessoal, profissional ou espiritual. Ou seja, buscar, de forma ampla, não só a sua a identificação, mas também, a sua melhor forma de atuação.  Logo depois do serviço, chegamos na categoria, que podemos relacionar como se fosse a dúvida, problema ou tarefa. Esta questão é importante para analisarmos as atitudes para contornar este problema. Os problemas, que são parte da vida dos seres humanos, podem ser confundidos com a dúvida. Mas, há diferença: enquanto as dúvidas se referem ao desconhecimento ou falta de interpretação sobre algo, os problemas não seguem o mesmo fluxo.   Os problemas são apontamentos em que deveriam seguir assim, mas não são. Por exemplo: Todas as demandas em que não temos respostas, ao menos no primeiro momento, são consideradas problemas, podendo haver soluções no momento ou no futuro.  Se os problemas se referem a como fazer, não é um problema, mas sim, uma dúvida.  O último item, na minha opinião é o mais simples. São as tarefas, que são operações sobre determinado ponto.   E quando falamos sobre urgência, classificamos em: blocante, urgente, alta, média e baixa.  Blocante é categoria na qual paralisa toda a operação e todos os usuários da organização. Sob a ótica da vida, a categoria blocante é algo que deve ser resolvido imediatamente.  A urgente se assemelha com a blocante, entretanto, não paralisa toda a operação. Pode ser resolvida com mais calma.  As categorias alta, média e baixa são problemas em que se devemos analisar quais serão os impactos no cotidiano.  As urgências ajudam a entender as demandas prioritárias. Até porque, quanto mais grave, maior o seu impacto.  Ticket de suporte ou da vida? Agora eu te pergunto: quem é o especialista por trás deste ticket da vida? Isso mesmo, sou eu, você, nós. Por isso, ser especialista nas suas próprias “demandas” (o que também podemos chamar de autoconhecimento) é um elemento essencial para compreender as questões que lhe afligem. E quanto mais você se organiza, classifica seus problemas e os prioriza, melhor você os resolverá, tornando sua vida mais tranquila. 

Padrão de Projeto Composite: O que é, prós e contras e como implementar?
Tech Writers Julho 04, 2023

Padrão de Projeto Composite: O que é, prós e contras e como implementar?

Se você atua com projetos de software e já teve a necessidade de tratar objetos individuais e coleções de objetos de forma uniforme, o padrão de projeto Composite vai te ajudar. Descubra a seguir como o padrão Composite auxilia na criação de interfaces de usuário mais intuitivas e no processamento de árvores de objetos de forma eficiente. Neste artigo, convidamos você a explorar os benefícios desse padrão, que simplifica estruturas hierárquicas e oferece maior flexibilidade na manipulação de elementos. Confira abaixo o que é o Composite, quais suas vantagens e desvantagens e veja um exemplo prático da sua implementação. O que é o Composite? O Composite é um padrão de projeto estrutural que permite tratar de maneira uniforme os objetos individuais e composições de objetos, formando uma estrutura hierárquica. Ele é o quarto padrão desta categoria apresentado pela "Gang of Four" (GoF). Como os próprios autores colocam, a intenção oficial deste padrão é “compor objetos em estruturas de árvore para representar hierarquias partes-todo". Com o Composite, um objeto individual e um grupo de objetos são tratados da mesma maneira, pois ambos implementam uma interface comum. Como funciona o padrão de projeto Composite? Dentro do padrão Composite, encontramos uma classe abstrata conhecida como "Componente", que estabelece uma interface compartilhada para objetos individuais e grupos de objetos. Os objetos individuais são representados por uma classe concreta que implementa o Componente, enquanto os grupos de objetos são representados por uma classe composta que também implementa o Componente. A estrutura hierárquica é criada através da composição, na qual um grupo de objetos pode incluir outros objetos individuais ou grupos de objetos. Essa estrutura permite a realização de interações recursivas, e dessa forma possibilita a aplicação de operações a todos os objetos na hierarquia. O padrão Composite é ideal para quando há a necessidade de tratar objetos individuais e coleções de objetos de forma homogênea, simplificando o código e conferindo maior flexibilidade. Seu uso é comum em interfaces de usuário, processamento de árvores e outras estruturas hierárquicas. Como é a implementação do Composite? Para entender melhor a implementação do Composite, vamos analisar o exemplo a seguir: Em um supermercado, você possui duas formas de comprar um refrigerante: É possível comprar a unidade Ou comprar um fardo com 12 unidades Aqui, nós temos um Composite: a noção de que o fardo (estrutura) se comporta como um produto, e que ele pode dizer seu preço delegando o seu valor, somando assim os custos das unidades filhas e utilizando como seu próprio. Claro, o cenário acima só faz sentido se o fardo não possuir um valor fixo, e sim que seu valor se resume no total de unidades que há dentro do mesmo. Vejamos um código do cenário descrito (no caso, utilizando Java para exemplificar): No exemplo acima, definimos uma interface Produto, que será implementada tanto pela unidade de “refrigerante”, quanto pelo fardo. O método em comum que será implementado por eles, no nosso caso, é o getValor(). O refrigerante, por sua vez, possui o valor e um nome. A implementação do getValor(), por ser uma única unidade, apenas retorna o seu próprio valor. Acima temos o fardo de refrigerantes, atuando como Composite, seu “valor” é delegado para as unidades que há dentro dele. Ou seja, se houver 5 unidades de refrigerante, cada uma com um valor “5”, o valor do fardo será de 25. O interessante neste design pattern é que a complexidade desse tipo de estrutura não acaba por aí. Como a classe FardoDeRefrigerante possui uma lista dentro de si do tipo Produto, ela é capaz de suportar até mesmo outros fardos dentro de si mesma. Exemplo da Estrutura de um Composite Apesar de o “fardo de refrigerante” não ser o melhor exemplo nesse caso, podemos imaginar uma caixa dentro de outra caixa, e cada uma delas com produtos de diferentes valores dentro de si, as caixas internas totalizaram o valor dos produtos internos, e a caixa externa totalizaria o valor das caixas internas, parecendo muito com a estrutura abaixo: Exemplo de como a estrutura de um composite pode ficar. Fonte: “Padrões de Projetos: Soluções Reutilizáveis de Software Orientados a Objetos” Normalmente utiliza-se o padrão de projeto Composite, por exemplo, para rodar uma bateria de validações em cima de um objeto. Enquanto o Decorator serve para estender funcionalidades dentro de uma classe já existente, sem alterar a classe original e preservando os princípios SOLID. A forma de implementação pode variar bastante dependendo de como que você utiliza, mas o importante é conhecer o padrão e saber quando encaixá-lo no seu sistema. Não siga à risca os diagramas propostos pela GoF, ou os exemplos citados, adeque-os de acordo com suas necessidades e situações. Quando usar o padrão de projeto Composite? Aplica-se o padrão de projeto Composite nas situações em que há uma estrutura hierárquica de objetos e quando se deseja tratar objetos individuais e grupos de objetos de forma uniforme, simplificando o código e fornecendo maior flexibilidade. Aqui estão algumas situações de exemplo do uso do padrão Composite: Estruturas Hierárquicas:  O padrão Composite é ideal para lidar com estruturas hierárquicas, como árvores ou grafos, onde os nós podem ser objetos individuais ou grupos de objetos. Modelagem de Interface de Usuário:  É comum utilizar o padrão Composite na construção de interfaces de usuário, especialmente quando há elementos que podem conter outros elementos, como menus, botões e painéis. Processamento de Documentos: O Composite pode ser útil para tratar elementos como documentos com seções, parágrafos, listas, etc, de forma uniforme. Manipulação de Gráficos: Se você precisa lidar com elementos gráficos, como formas, linhas e grupos de formas, o Composite permite tratá-los de maneira uniforme e facilita a aplicação de operações a todos os elementos. Implementação de Estruturas de Dados: Também é possível aplicar o Composite na implementação de estruturas de dados como arrays, listas e matrizes, em que tratam-se os elementos individuais e os grupos de elementos de forma semelhante. Quais os prós e contras de usar o Composite? Confira os principais prós e contras do Composite: PrósContrasSimplifica a estrutura hierárquicaPode ter impacto na eficiência em estruturas muito grandesOferece flexibilidade na manipulação de objetos e operações recursivasPode exigir maior esforço de design e implementação inicialPromove reuso do códigoRequer uma interface comum bem definida No entanto, é importante lembrar que os prós e contras do Composite podem variar dependendo do contexto e dos requisitos específicos do projeto. Portanto, é essencial considerar cuidadosamente esses aspectos antes de decidir utilizar o design pattern Composite. E você já sabia como implementar o Composite? Aproveite e confira outros conteúdos de tecnologia no Tech Writers.

IHC: Saiba o que é e os focos de estudo da Interação Humano-Computador
Tech Writers Maio 25, 2023

IHC: Saiba o que é e os focos de estudo da Interação Humano-Computador

Com o aumento exponencial do uso de aparelhos e engenhocas eletrônicas na nossa rotina, a importância da Interação Humano-Computador, também conhecida como IHC, têm aumentado cada vez mais na transformação da sociedade, impactando diretamente o cotidiano da vida de todos. Especialmente para pessoas desenvolvedoras de software e profissionais do design das interfaces, é fundamental entender os princípios da IHC para elaborar produtos bem-sucedidos, que atendam às expectativas das pessoas usuárias. Se você quer saber mais sobre o assunto, descubra a seguir o que é o IHC. Também abordaremos quais são os principais pilares de estudo desta área para entender melhor este conceito. O que é o IHC? A transformação digital ocorre com mais fluidez com o bom uso de boas interfaces, que devem ser cuidadosamente projetadas para permitir uma comunicação eficaz entre as pessoas e as máquinas. O foco é proporcionar uma experiência agradável e satisfatória para a pessoa usuária, em que ela consiga realizar sua tarefa e resolver sua “dor” de forma eficiente e sem frustrações. É aí que entra a IHC. A Interação Humano-Computador (IHC) é uma área multidisciplinar da tecnologia dedicada ao estudo e à melhoria da interação entre seres humanos e sistemas computacionais. Seu objetivo é criar interfaces da pessoa usuária que sejam intuitivas, eficientes e agradáveis de se usar.  A IHC abrange diversos aspectos do design de interfaces, como a usabilidade, a acessibilidade, a experiência do usuário e a ergonomia. Sendo assim, a área utiliza métodos e técnicas de origens diversas, como a psicologia, a engenharia de software, a ciência da computação e o design. Quais são os 3 focos de estudo de IHC? Os três focos principais de estudo da IHC (Interação Humano-Computador) são: 1. A pessoa usuária (e sua tarefa): A IHC foca primeiramente em entender as necessidades, habilidades e limitações das pessoas usuárias, visando projetar sistemas e interfaces intuitivas e otimizadas para as ações pretendidas. Para isso, a área utiliza técnicas de pesquisa, como entrevistas quantitativas e qualitativas, observação do modelo mental, mapeamento da jornada do usuário e testes de usabilidade. 2. O sistema: Outro foco da área é o sistema em si, isto é, entender como os sistemas computacionais funcionam e como projetá-los de forma otimizada e segura levando em consideração diversos fatores, como limitações de hardware e software, por exemplo. Na escalada desse conhecimento, é necessário o aprofundamento nas áreas da arquitetura de software, tecnologias de interação, usabilidade e design de interfaces. 3. O contexto de uso: Por último, outro foco de estudo da área é o contexto de uso dos sistemas. Por exemplo, as condições ambientais, as restrições de tempo, as habilidades ou possíveis limitações físicas da pessoa usuária, além de seus desejos, necessidades e sua cultura — digital, ou não. Todos esses elementos moldam a resposta às necessidades de quem está usando o sistema. Assim, é possível garantir que o sistema seja projetado levando em conta as expectativas de ação e reação da pessoa usuária em um grande espectro de situações. Feedback é vida A exemplo disso, o feedback que o usuário precisa receber do sistema computacional deve ser explicitamente planejado e programado. Isso é essencial para garantir que o usuário entenda claramente o que está acontecendo internamente no computador e possa interagir com ele de forma mais assertiva. Imagine a seguinte situação: você aciona a vinda de um elevador e não recebe resposta alguma do sistema, nenhum botão aceso, nenhum contador de andares, nenhuma seta para cima ou para baixo, e desse ponto em diante a sua interação com o sistema passa a ser baseada na fé – de que o elevador vai chegar em algum momento… Jakob Nielsen, o Yoda da usabilidade, não se contenta com designs bonitos; ele nos convida a pensar no usuário em primeiro lugar. Eficiência de uso e satisfação do usuário são como seu mantra do design. É mais do que criar algo bonito; é criar algo que funcione com maestria e faça o usuário “sorrir” enquanto usa. E aí entram as heurísticas de Nielsen, o mestre dos magos do pragmatismo funcional. Esses princípios não são apenas diretrizes acolhidas pelos designers; são como os próprios 10 mandamentos do design de interfaces. "Faça o status do sistema visível!" ele proclama. "Mantenha uma correspondência com as coisas no mundo real e de controle ao usuário!" Essas regras são como bússolas que guiam os designers pelos mares da experiência do usuário. Portanto, deixe de usá-las e navegue sem rumo à sua própria sorte. Podemos elencar 10 heurísticas principais e uma delas, comentada acima, é justamente a primeira de todas: Visibilidade do status do sistema. Para fechar o combo temos mais 9 heurísticas — que serão gradativamente abordadas nos próximos posts: Correspondência entre o sistema e o mundo real Controle e liberdade do usuário Consistência e padrões Prevenção de erros Reconhecimento em vez de memorização Flexibilidade e eficiência de uso Estética e design minimalista Reconhecimento, diagnóstico e recuperação de erros Ajuda e documentação Origem da IHC e Linha do Tempo Anteriormente, na aurora dos tempos das interfaces podemos apontar com segurança o Lisa, como o "papai forno" das interfaces. Lançado em 1983, pela Apple, o primeiro computador comercial que possuía uma Interface Gráfica de Usuário (UI). Essa tecnologia permitiu a utilização de computadores por pessoas sem nenhum treinamento especial, tornando-os acessíveis a todos. No entanto, a Apple já estava trabalhando em uma versão mais acessível e de menor custo. Um ano depois, a empresa lançou o Macintosh, o primeiro computador com uma interface gráfica realmente acessível. Vinha com um monitor, teclado e mouse com botão, e possuía uma organização maior que os computadores anteriores. As opções eram ordenadas em menus e havia ícones que podiam ser clicados para operar um programa. O foco aqui é perceber que o maior legado dessa movimentação disruptiva foi a metáfora da área de trabalho (Desktop folders metaphor), utilizada comercialmente pela primeira vez pela Apple no Lisa OS, o que alavancou também o uso de Interfaces Gráficas (Graphical User Interface, ou GUI) por outras empresas. Essa metáfora da área de trabalho representava uma mesa de escritório com vários papéis em cima, simbolizando os arquivos do computador, assim como outras representações do mundo real, como a lixeira, o calendário, o relógio, os arquivos, planilhas, o bloco de notas, a calculadora, etc. Nessa época os primeiros usuários e entusiastas dos computadores eram pessoas que trabalhavam em escritórios, com datilografia, máquinas de calcular, pastas de arquivos, etc. Por isso, percebeu-se que ao simular o ambiente físico de escritório em uma interface no computador, os usuários compreenderiam com mais facilidade o armazenamento das informações no Desktop. Esse era o modelo mental. Após esse rolê no passado, olhemos agora para o presente. Não podemos ignorar o burburinho da inteligência artificial (IA). Agora, a coisa está ficando real, com comandos de fala prestes a entrar em cena. Não estamos apenas clicando e arrastando; estamos falando com nossas máquinas. É tipo ter um assistente pessoal que não apenas entende o que você diz, mas também pode auxiliar ou executar qual tarefa você realmente precisa realizar, ao melhor estilo J.A.R.V.I.S., do Tony Stark. No entanto, com grandes poderes vêm grandes responsabilidades. À medida que a IA se torna onipresente, começamos a questionar a privacidade, a segurança e até mesmo a confiabilidade dessas interações. É como se estivéssemos flertando com uma nova era de conversas digitais, mas não sabemos ao certo todas as regras do jogo. É digno reparar também que mesmo com uma ajudinha de sua assistente pessoal falante, o menino Tony ainda faz amplo uso de interfaces gráficas. Conclusão Em suma, chamamos de Interação Humano-Computador o estudo, projeto e implementação de softwares para o uso direto com usuários. Tais sistemas apresentam Interfaces do Usuário (UI). É através delas que uma pessoa irá interagir com um computador. Dessa forma, foi através das interfaces que o computador se tornou no que conhecemos. Gostou de saber mais sobre a Interação Humano-Computador? Caso tenha alguma dúvida, deixe seu comentário e confira outros artigos da Tech Writers.

Observabilidade: O que é, vantagens, pilares e qual a diferença para o monitoramento!
Tech Writers Maio 11, 2023

Observabilidade: O que é, vantagens, pilares e qual a diferença para o monitoramento!

Nos últimos anos, percebemos um aumento da complexidade dos sistemas de software, o surgimento de SaaS e apps híbridos. Isso consequentemente aumenta os riscos de bugs, além de uma maior exposição dos sistemas à invasão e vazamento de dados. Neste novo contexto, a observabilidade pode ajudar a identificar problemas de forma mais rápida, otimizando o trabalho da equipe de desenvolvimento e melhorando a qualidade dos sistemas. Se você ainda não conhece este conceito, confira a seguir mais informações sobre uma das principais tendências em engenharia de software e descubra o que é a observabilidade, seus pilares, vantagens e como implementar! O que é observabilidade em TI? A observabilidade em TI é a aplicação de um sistema de constante monitoramento, rastreamento, análises e diagnósticos de um sistema, fazendo uso de algumas ferramentas de software. A observabilidade permite investigar e entender de maneira mais aprofundada como um sistema comporta, principalmente em arquiteturas cloud. A partir das ferramentas de observabilidade, é possível coletar e analisar um grande espectro de dados, detectar problemas, entender as causas dos problemas identificados e traçar um plano de ação para aprimorar o funcionamento dos sistemas. Dessa forma, a observabilidade ajuda a resolver problemas antes que eles afetem os KPIs da empresa. Além disso, transformam um problema incompreensível em algo mais detectável, visível e investigável. Este conceito é bastante aplicado por times DevOps, infraestrutura e desenvolvimento, pois já é difundido na Engenharia de Software que ele beneficia o software e facilita a solução de problemas. O ideal é adotar a implementação da observabilidade de software principalmente quando o foco do seu negócio for a experiência do cliente ou para otimizar o trabalho das pessoas desenvolvedoras da sua equipe. Aproveite para conferir também nosso artigo que aborda um pouco mais sobre a observabilidade orientada ao domínio, uma metodologia de implementação da observabilidade.  Quais as vantagens de implementar a Observabilidade em TI? Algumas vantagens de implementar a observabilidade são: Disponibilidade, pois facilita a identificação e correção de erros, minimizando o tempo de inatividade e o impacto nos usuários finais; Confiabilidade, pois, quando usada de forma preventiva, a observabilidade é capaz de mitigar problemas de escalabilidade ou performance de uma aplicação antecipadamente; Rastreabilidade do uso de funcionalidades, de forma a se obter insumos para decisões estratégicas que podem ser tomadas no âmbito do negócio; Produtividade, pois a observabilidade permite acompanhar mudanças em tempo real, aumentando a capacidade produtiva do seu time na hora de suprir as demandas diárias, auxiliando as equipes de DevOps, infraestrutura, ou outras; Custo menor, pois uma vez que a observabilidade previne problemas de segurança, a empresa gasta menos com manutenções inesperadas; Previsibilidade, pois quando a cultura do time está focada em rastrear e monitorar os sistemas em tempo real, a empresa consegue prever mais facilmente alguns dos problemas futuros. Entre essas vantagens, a rastreabilidade é a que mais impacta o negócio, pois é ela que define o sucesso da sua aplicação. As também chamadas “métricas de alto nível”, por serem intrinsecamente ligadas ao negócio, são as que mais entregam valor.  Através da rastreabilidade do uso das funcionalidades de um sistema é que conseguimos entender se ele está performando conforme os objetivos de um produto. Quais são os 3 pilares da observabilidade em TI? A eficácia na implementação da observabilidade de software só é possível a partir do uso de dados de telemetria, que são os pilares que reúnem todas as informações sobre o sistema investigado. No conceito de observabilidade, temos 3 pilares: logs de evento, métricas e rastreamento. Veja a seguir uma explicação dos 3 pilares da observabilidade em TI: Logs de evento: É um registro imutável que apresenta hora e data dos eventos. É possível captá-lo através de texto, binário ou estruturado. Métricas: As métricas são valores definidos de acordo com o objetivo. Elas são usadas para investigar o comportamento de um evento em um intervalo de tempo específico e fornecem dados como data, nome, hora e KPIs. Rastreamento: É o pilar que detalha todas as operações, desde seu início até o fim. Isso acontece com o monitoramento da jornada completa para entender se o sistema está em normalidade ou corre algum risco. Qual é a diferença entre monitoramento e observabilidade? A estratégia de observabilidade não é exatamente diferente do monitoramento, pois o monitoramento trata os problemas de forma mais reativa e não é capaz de lidar com problemas de alta complexidade. Isso porque desde a incorporação da arquitetura de microsserviços para os apps web, houve maior escalabilidade na criação de componentes de sistema sendo implementados de forma individual nas plataformas. Neste cenário em que novos problemas desconhecidos podem surgir, somente o monitoramento já não consegue ser tão eficiente. Por outro lado, a observabilidade é um grande conjunto de monitoramento focado na arquitetura de microsserviços na camada de aplicação e contêineres na área de infra. Além disso, ela faz uma correlação dos dados de telemetria coletados com o objetivo de criar soluções mais eficazes.  Assim, a observação constante consegue trazer dados que auxiliam o monitoramento, facilitando a identificação dos problemas e suas causas. Ou seja, o monitoramento garante que uma aplicação se torne observável, sendo assim, a observabilidade é uma evolução do conceito de monitoramento. Através da observabilidade, é possível ter um melhor monitoramento do sistema, sendo quase sinônimos nesse sentido. Como implementar a observabilidade? Para implementar a observabilidade de maneira adequada, você precisa, primeiramente, definir suas metas. Ou seja, identificar os principais indicadores de desempenho (KPIs), seus objetivos e listar os pontos críticos monitoráveis. Em seguida, instrumente seu código com métricas e registros. Algumas ferramentas podem te ajudar nessa tarefa, como o Prometheus para métricas e o Fluentd para registros. Você também pode utilizar ferramentas de monitoramento para analisar os dados gerados na aplicação. Por exemplo, o Grafana para visualizar métricas e o Kibana para analisar os registros. Além destes pontos, é importante ter alertas que notifiquem o time sempre que um evento crítico ocorrer. Para entender como as solicitações estão se comportando dentro do sistema, você pode usar o rastreamento de transações para rastrear o fluxo de solicitações através dos diferentes componentes. Seguindo estes passos e mantendo um processo contínuo, atualize suas metas e ajuste sua implementação conforme as necessidades forem mudando. Ainda tem alguma dúvida sobre observabilidade? Deixe seu comentário!

Observabilidade: O que é, pilares e como implementar a observabilidade orientada ao domínio
Tech Writers Março 16, 2023

Observabilidade: O que é, pilares e como implementar a observabilidade orientada ao domínio

Recentemente estamos vendo uma grande expansão dos sistemas web, o surgimento de aplicações híbridas e aplicações que utilizam cada vez mais os softwares como serviço (SaaS). Este crescimento traz um novo desafio aos times de DevOps, infra e desenvolvimento: somente o monitoramento de redes não é mais capaz de garantir a segurança dos dados. É aí que entra a observabilidade. A observabilidade auxilia no rastreamento e identificação de problemas a partir de ferramentas de software. E hoje, vamos te ensinar a implementar a observabilidade usando o método de Observabilidade Orientada ao Domínio. Assim, você vai tornar as diversas chamadas para serviços de log e frameworks de análises em seus sistemas menos técnicas e prolixas. Confira! O que é observabilidade em TI? A observabilidade em TI (também chamada de observability) é a capacidade de monitorar, rastrear, analisar e diagnosticar um sistema, a partir do uso de ferramentas de software. Com ela, é possível criar um sistema de constante monitoramento e observação sobre um sistema com o objetivo de compreender melhor como ele se comporta, principalmente em arquiteturas cloud. Este conceito é bastante aplicado por times DevOps, infraestrutura e desenvolvimento, pois já é difundido na Engenharia de Software que ele beneficia o software e facilita a solução de problemas. Observabilidade orientada ao domínio na prática: Valores Grandes aplicações voltadas para análises de métricas de alto nível, como Mixpanel, acreditam no conceito de “Momentos de valor” (value moments), que indica quais eventos de determinado produto são importantes para se instrumentar. Estes momentos de valor variam de acordo com o produto, por exemplo, um software voltado para soluções de assinatura eletrônica, como a 1Doc, pode considerar que a assinatura de um contrato é um “momento de valor”. Porém, o momento de valor que faz sentido para o seu negócio, não necessariamente faz sentido para os usuários. Isso porque o valor do seu negócio é composto do equilíbrio entre duas forças: a intenção e a expectativa. Se a intenção é facilitar o processo de assinatura de um contrato, e a expectativa dos seus usuários é exatamente essa, você alcançou o equilíbrio. Contudo, o desencontro dessas duas forças é uma perda de oportunidade, e consequentemente, de valor. Graças às métricas de alto nível, este desencontro não é uma causa perdida. Com elas é possível recuperar e manter o valor do seu negócio de acordo com os momentos de valor identificados pelo seu time de analistas de produto. A partir daqui, seu papel como pessoa desenvolvedora passa a envolver a verificação da viabilidade técnica e a implementação da captura dessas métricas para que o “time de negócio” consiga lidar com os dados. Como implementar a observabilidade orientada ao domínio? A partir de agora vamos para a parte prática aprender a implementar a observabilidade orientada ao domínio. Para entender melhor esta implementação, vamos imaginar um pequeno sistema de gerenciamento de tarefas. Este sistema cadastra tarefas agendadas e as executa de acordo com o agendamento. Porém, devido a uma necessidade dos usuários, em certos momentos, pode ser necessário adiantar a execução de uma dessas tarefas de forma manual. Para atender a esta necessidade de “execução adiantada”, a estrutura abaixo foi feita: GerenciadorTarefas: Classe responsável pela execução de uma determinada tarefa baseada em seu próprio código ─ é a classe de “caso de uso”; RecuperadorTarefas: Classe responsável pela abstração da recuperação das tarefas do banco de dados e retorno dos objetos de domínio ─ é a classe “repositório”. Tarefa: Classe que representa uma “tarefa” no sistema ─ é a “entidade de domínio”. Veja o exemplo abaixo: public class GerenciadorTarefas { private static boolean TAREFA_PROCESSADA = true; private static boolean TAREFA_NAO_PROCESSADA = false; private RecuperadorTarefas recuperadorTarefas; public GerenciadorTarefas(RecuperadorTarefas recuperadorTarefas) { this.recuperadorTarefas = recuperadorTarefas; } public boolean executaTarefaPeloCodigo(Integer codigoTarefa) { Tarefa tarefa = recuperadorTarefas.recuperaPeloCodigo(codigoTarefa); if (tarefa == null) { return TAREFA_NAO_PROCESSADA; } try { tarefa.iniciaProcesso(); return TAREFA_PROCESSADA; } catch (TarefaInterrompidaException e) { return TAREFA_NAO_PROCESSADA; } } } O código acima pode não ser o melhor exemplo, mas expressa bem a sua lógica de domínio. Agora, vamos aplicar a observabilidade no nosso método executaTarefaPeloCodigo. Para isto, vamos imaginar duas bibliotecas em nosso projeto: Log: É uma biblioteca genérica de logs, útil para atividades de throubleshooting por parte das pessoas desenvolvedoras. Analytics: É uma biblioteca genérica de eventos que metrifica interações de um usuário a uma determinada funcionalidade. public boolean executaTarefaPeloCodigo(Integer codigoTarefa) { Tarefa tarefa = recuperadorTarefas.recuperaPeloCodigo(codigoTarefa); if (tarefa == null) { Log.warn("A tarefa %d não existe, portanto, seu processo não foi iniciado.", codigoTarefa); return TAREFA_NAO_PROCESSADA; } try { Log.info("Processo da tarefa %d foi iniciado.", codigoTarefa); Analytics.registraEvento("tarefa_iniciada", tarefa); tarefa.iniciaProcesso(); Log.info("Processo da tarefa %d foi finalizado.", codigoTarefa); Analytics.registraEvento("tarefa_finalizada", tarefa); return TAREFA_PROCESSADA; } catch (TarefaInterrompidaException e) { Log.error(e, String.format("Processo da tarefa %d foi interrompido.", codigoTarefa)); Analytics.registraEvento("tarefa_interrompida", tarefa); return TAREFA_NAO_PROCESSADA; } } Agora, além da execução da regra de negócio previamente expressa pelo código, também estamos lidando com diversas chamadas de logs e análises sobre o uso desta funcionalidade. Analisando, não do ponto de vista da instrumentação da observabilidade, mas tecnicamente, com certeza, a manutenibilidade deste código caiu. Primeiro que se essa implementação for crucial para o negócio, ela deveria ser garantida com testes unitários. Além disso, a regra de negócio, que antes era claramente expressa, agora, está ofuscada com o uso dessas bibliotecas. Cenários como este são comuns de se ver nos mais diversos sistemas e, geralmente, não parece soar muito bem um “código voltado à observabilidade” e um “código voltado ao domínio”, juntos. Então, existe solução? Vamos entender melhor a seguir. Solução para o Case de Observabilidade Pensando na legibilidade do código escrito, instintivamente, acabamos pensando na criação de pequenos métodos que abstraiam esse conteúdo confuso de dentro do executaTarefaPeloCodigo, isolando o código voltado ao domínio do código voltado às análises. Porém, neste caso, a observabilidade introduzida é um requisito do negócio, portanto, mesmo sendo um “código voltado às análises”, ele continua sendo um “código voltado ao domínio”. Entenda melhor na imagem abaixo: Ou seja, nem todo código voltado ao domínio é voltado à observabilidade e nem todo código voltado à observabilidade é voltado ao domínio, mas há, em alguns casos, uma intersecção entre estes, como no nosso case apresentado. Por fim, também recomendamos fortemente a extração das Strings “mágicas”, pois torna a leitura mais agradável e mais fácil de entender o que cada uma representa. Talvez a introdução de alguns ENUMs também seja válida para abstrair o que seria os “trackings de eventos”, como tarefa_iniciada e tarefa_finalizada, mas não vamos nos aprofundar neste assunto, pois não é o foco. public boolean executaTarefaPeloCodigo(Integer codigoTarefa) { Tarefa tarefa = recuperadorTarefas.recuperaPeloCodigo(codigoTarefa); if (tarefa == null) { metrificaQueTarefaNaoExiste(codigoTarefa); return TAREFA_NAO_PROCESSADA; } try { metrificaQueTarefaFoiIniciada(tarefa); tarefa.iniciaProcesso(); metrificaQueTarefaFoiFinalizada(tarefa); return TAREFA_PROCESSADA; } catch (TarefaInterrompidaException e) { metrificaQueTarefaFoiInterrompida(tarefa); return TAREFA_NAO_PROCESSADA; } } private void metrificaQueTarefaNaoExiste(Integer codigoTarefa) { Log.warn(MENSAGEM_TAREFA_INEXISTENTE, codigoTarefa); } private void metrificaQueTarefaFoiIniciada(Tarefa tarefa) { Log.info(MENSAGEM_TAREFA_INICIADA, tarefa.getCodigo()); Analytics.registraEvento(TAREFA_INICIADA, tarefa);} private void metrificaQueTarefaFoiFinalizada(Tarefa tarefa) { Log.info(MENSAGEM_TAREFA_FINALIZADA, codigoTarefa); Analytics.registraEvento(TAREFA_FINALIZADA, tarefa);} private void metrificaQueTarefaFoiInterrompida(Tarefa tarefa) { Log.error(e, String.format(MENSAGEM_TAREFA_INTERROMPIDA, codigoTarefa)); Analytics.registraEvento(TAREFA_INTERROMPIDA, tarefa);} Este é um bom começo, com o código de domínio voltando a ser bem escrito ─ isso, claro, se você considerar que seu “código de domínio” é apenas o método executaTarefaPeloCodigo. Observando nossa classe, não leva muito tempo para notarmos que fizemos uma troca. Se extrairmos de dentro do método original vários outros métodos de metrificação os quais não se encaixam com o objetivo principal da classe GerenciadorTarefas, estamos apenas “jogando o problema para debaixo do tapete”. Quando algo assim acontece, geralmente nos indica que uma nova classe está querendo emergir. Portanto, talvez, a mais simples solução seja a segregação dessa classe em duas: uma para lidar com as métricas e outra para o processamento das tarefas. Ou seja, nossa proposta é a criação de uma nova classe responsável especificamente pelas análises e logs da aplicação, assim como demonstrado no desenho abaixo. Esta também é uma boa solução pois a segregação das responsabilidades originais e o encapsulamento das funções de métricas em uma nova classe, somado à possível injeção de dependências introduzida, favorece o design para testabilidade do GerenciadorTarefas, que é detentor das nossas regras de domínio. Podemos reforçar ainda mais essa ideia pensando no fato de que Java é uma linguagem orientada a objetos (POO) e a testabilidade de uma classe que utiliza de métodos estáticos é reduzida caso o método modifique um estado externo a si, e, geralmente, bibliotecas de logs atendem a este requisito. Desta forma, o resultado do nosso GerenciadorTarefas seria o seguinte: public class GerenciadorTarefas { private static boolean TAREFA_PROCESSADA = true; private static boolean TAREFA_NAO_PROCESSADA = false; private RecuperadorTarefas recuperador; private MetificadorTarefas metrificador; public GerenciadorTarefas(RecuperadorTarefas recuperador, MetificadorTarefas metrificador) { this.recuperador = recuperador; this.metrificador = metrificador; } public boolean executaTarefaPeloCodigo(Integer codigoTarefa) { Tarefa tarefa = recuperador.recuperaPeloCodigo(codigoTarefa); if (tarefa == null) { metrificador.metrificaQueTarefaNaoExiste(codigoTarefa); return TAREFA_NAO_PROCESSADA; } try { metrificador.metrificaQueTarefaFoiIniciada(tarefa); tarefa.iniciaProcesso(); metrificador.metrificaQueTarefaFoiFinalizada(tarefa); return TAREFA_PROCESSADA; } catch (TarefaInterrompidaException e) { metrificador.metrificaQueTarefaFoiInterrompida(tarefa); return TAREFA_NAO_PROCESSADA; } }} O processo de segregação da classe GerenciadorTarefas e o encapsulamento das métricas é chamado de Observabilidade Orientada ao Domínio, e a nova classe gerada é o nosso tão cobiçado Domain Probe. O nome deste padrão de projetos, “Domain Probe”, remete à “Sonda de Domínio”. Este nome não poderia ser mais adequado visto que nossa classe literalmente age como uma “sonda”, em uma classe que anteriormente carecia do levantamento de métricas. Como testar a observabilidade orientada a domínio? Antes de testar a observabilidade de fato, vamos retomar a primeira versão da nossa classe, e tentar imaginar um cenário de teste. public class GerenciadorTarefas { private static boolean TAREFA_PROCESSADA = true; private static boolean TAREFA_NAO_PROCESSADA = false; private RecuperadorTarefas recuperador; public GerenciadorTarefas(RecuperadorTarefas recuperador) { this.recuperador = recuperador; } public boolean executaTarefaPeloCodigo(Integer codigoTarefa) { Tarefa tarefa = recuperador.recuperaPeloCodigo(codigoTarefa); if (tarefa == null) { return TAREFA_NAO_PROCESSADA; } try { tarefa.iniciaProcesso(); return TAREFA_PROCESSADA; } catch (TarefaInterrompidaException e) { return TAREFA_NAO_PROCESSADA; } }} Se você está acostumado a fazer este tipo de análise, vai perceber alguns cenários: Ou não existe tarefa com o código informado, retornando FALSE; Ou existe tarefa e seu processamento é concluído, retornando TRUE; Ou existe tarefa e seu processamento é interrompido, retornando FALSE; Para simplificar, vamos utilizar apenas o terceiro cenário como exemplo. Abaixo, podemos observar como seria a implementação desta classe de testes. public class GerenciadorTarefasTest { private static final Integer CODIGO_TAREFA = 1; private GerenciadorTarefas gerenciadorTarefas; private RecuperadorTarefas recuperador; @BeforeEach public void setUp() { this.recuperador = Mockito.mock(RecuperadorTarefas.class); this.gerenciadorTarefas = new GerenciadorTarefas(recuperador); } @Test public void deveRetornarFalso_casoOcorraErroDeProcessamento_quandoExistirTarefaComCodigoInformado() throws TarefaInterrompidaException { doReturn(criaTarefaComExcecaoEmbutida()).when(recuperador).recuperaPeloCodigo(eq(CODIGO_TAREFA)); Boolean foiExecutado = gerenciadorTarefas.executaTarefaPeloCodigo(CODIGO_TAREFA); assertFalse(foiExecutado); } private Tarefa criaTarefaComExcecaoEmbutida() throws TarefaInterrompidaException { Tarefa tarefa = Mockito.spy(new Tarefa(CODIGO_TAREFA)); doThrow(new TarefaInterrompidaException()).when(tarefa).iniciaProcesso(); return tarefa; } } Seguindo o padrão GWT de nomenclatura (Given - When - Then), podemos expressar nossa regra de negócio no teste. Porém, vale mencionar que aqui estamos traduzindo e “abrasileirando” a escrita da GWT (Given - When - Then), transformando em “DCQ” (Deve ─ Caso ─ Quando). Dessa forma, usamos o DCQ significa: “Deve retornar falso”, que é equivalente à “Then returns false”; “Caso ocorra erro de processamento”, que equivale à expressão “When a processing error occurs”; “Quando existir tarefa com o código informado”, que representa o mesmo que “Given an existing task with the informed code”. A partir disso, quando reimplementamos nossa observabilidade, nossa classe GerenciadorTarefas volta a ser assim: public class GerenciadorTarefas { private static boolean TAREFA_PROCESSADA = true; private static boolean TAREFA_NAO_PROCESSADA = false; private RecuperadorTarefas recuperador; private MetificadorTarefas metrificador; public GerenciadorTarefas(RecuperadorTarefas recuperador, MetificadorTarefas metrificador) { this.recuperador = recuperador; this.metrificador = metrificador; } public boolean executaTarefaPeloCodigo(Integer codigoTarefa) { Tarefa tarefa = recuperador.recuperaPeloCodigo(codigoTarefa); if (tarefa == null) { metrificador.metrificaQueTarefaNaoExiste(codigoTarefa); return TAREFA_NAO_PROCESSADA; } try { metrificador.metrificaQueTarefaFoiIniciada(tarefa); tarefa.iniciaProcesso(); metrificador.metrificaQueTarefaFoiFinalizada(tarefa); return TAREFA_PROCESSADA; } catch (TarefaInterrompidaException e) { metrificador.metrificaQueTarefaFoiInterrompida(tarefa); return TAREFA_NAO_PROCESSADA; } }} É importante lembrarmos aqui que nenhum comportamento foi alterado com o incremento da observabilidade. Portanto, o teste feito anteriormente continua cumprindo seu papel mesmo estando desatualizado. No máximo, o que ocorreria neste caso é um erro de compilação, que já serviria de aviso aos testes que esta classe agora possui uma nova dependência. Sendo um incremento da nossa regra de negócio original, nada mais justo do que incrementar os testes garantindo as invocações corretas de nosso instrumentador. Veja o exemplo a seguir: public class GerenciadorTarefasTest { private static final Integer CODIGO_TAREFA = 1; private GerenciadorTarefas gerenciadorTarefas; private RecuperadorTarefas recuperador; private MetificadorTarefas metrificador; @BeforeEach public void setUp() { this.recuperador = Mockito.mock(RecuperadorTarefas.class); this.metrificador = Mockito.mock(MetificadorTarefas.class); this.gerenciadorTarefas = new GerenciadorTarefas(recuperador, metrificador); } @Test public void deveRetornarFalso_casoOcorraErroDeProcessamento_quandoExistirTarefaComCodigoInformado() throws TarefaInterrompidaException { doReturn(criaTarefaComExcecaoEmbutida()).when(recuperador).recuperaPeloCodigo(eq(CODIGO_TAREFA)); Boolean foiExecutado = gerenciadorTarefas.executaTarefaPeloCodigo(CODIGO_TAREFA); Mockito.verify(metrificador, times(1)).metrificaQueTarefaFoiIniciada(any()); Mockito.verify(metrificador, times(1)).metrificaQueTarefaFoiInterrompida(any()); Mockito.verifyNoMoreInteractions(metrificador); assertFalse(foiExecutado); } private Tarefa criaTarefaComExcecaoEmbutida() throws TarefaInterrompidaException { Tarefa tarefa = Mockito.spy(new Tarefa(CODIGO_TAREFA)); doThrow(new TarefaInterrompidaException()).when(tarefa).iniciaProcesso(); return tarefa; } } Aproveitando a dependência de um instrumentador dentro do nosso GerenciadorTarefas, podemos ainda injetar uma classe falsa para verificar apenas o número de invocações de cada método. No teste acima, verificamos se os métodos metrificaQueTarefaFoiIniciada e metrificaQueTarefaFoiInterrompida foram invocados, e então garantimos que mais nenhuma outra interação é feita com nossa classe instrumentadora. Assim, caso uma nova métrica surja, haja refatoração ou mudança na regra de negócio, teremos testes que garantem aquilo que o negócio espera, ou esperava. Opinião do Autor Este artigo é, em grande parte, uma releitura do estudo Domain-Oriented Observability, escrito por Pete Hodgson em 2019, e também inclui a visão de diversos outros autores sobre o assunto, inclusive a opinião pessoal do autor, Felipe Luan Cipriani, tech writer convidado pelo grupo Softplan.. Quando li o artigo de referência “Domain-Oriented Observability” pela primeira vez, não me surpreendi com algo revelador, pois já conhecia o método. Porém, depois de algumas conversas com colegas próximos e mais algumas tentativas de compreender a totalidade do artigo, percebi como o subestimei. Domain Probe não aborda encapsulamento, segregação ou injeção de dependência ─ embora estes sejam todos elementos que o compõem ─, mas sim a importância das métricas, e sua relevância para o negócio. E embora o padrão de projeto Domain Probe tenha semelhanças com um Facade, ele se preocupa com a essência de todo sistema: o domínio. Por isso, ele tem seu valor. Este é um padrão de projetos essencial de se conhecer e aplicar onde quer que haja instrumentos de métricas em um domínio que não tenha sido feito ou pensado para ser fácil de ler, interpretar, ou dar manutenção. Afinal, desenvolvedores passam mais tempo lendo códigos do que escrevendo. Além disso, este é um padrão de projetos com extrema flexibilidade no quesito de granularidade. Ou seja, você pode criar desde um Domain Probe para cada classe de domínio, sendo esta abordagem mais “específica”, a até mesmo um Domain Probe “genérico”. Não existe uma abordagem errada, apenas diferentes abordagens. Outro tipo de implementação de uma Observabilidade Orientada ao Domínio é através de eventos. Neste cenário, o padrão de projetos da vez é o Observer, e sua abordagem é igualmente interessante, valendo a pena um artigo dedicado somente a ele. Por fim, agradeço a você, caro(a) leitor(a), pelo seu tempo e interesse. Recomendo que leia os artigos dos links presentes no decorrer deste artigo e, se você gostou do conteúdo, tem alguma dúvida ou deseja iniciar uma discussão sobre a temática, deixe seu comentário abaixo!