<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Carlos Machel]]></title><description><![CDATA[Carlos Machel]]></description><link>https://carlosmachel.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1702570087525/WTNw2Njgk.png</url><title>Carlos Machel</title><link>https://carlosmachel.com</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 10:14:33 GMT</lastBuildDate><atom:link href="https://carlosmachel.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Entendendo a Biblioteca Azure AI Persistent Agents]]></title><description><![CDATA[Durante meus estudos para a AI-102 e a natureza do meu trabalho, eu trabalhei com a Assistant API da OpenAi e com os Agentes do Azure AI Foundry. Percebi que muita coisa tava sendo abstraída para fazer os meus agentes de uma maneira rápida e eficient...]]></description><link>https://carlosmachel.com/entendendo-a-biblioteca-azure-ai-persistent-agents</link><guid isPermaLink="true">https://carlosmachel.com/entendendo-a-biblioteca-azure-ai-persistent-agents</guid><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Azure AI Foundry]]></category><category><![CDATA[azure ai services]]></category><category><![CDATA[llm]]></category><dc:creator><![CDATA[Carlos Machel]]></dc:creator><pubDate>Tue, 15 Jul 2025 21:22:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/_0iV9LmPDn0/upload/86e516598e874f582ad03aa160e6d996.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Durante meus estudos para a AI-102 e a natureza do meu trabalho, eu trabalhei com a Assistant API da OpenAi e com os Agentes do Azure AI Foundry. Percebi que muita coisa tava sendo abstraída para fazer os meus agentes de uma maneira rápida e eficiente. A ideia da biblioteca <strong>Azure.AI.Agents.Persistent</strong> é parecido com a funcionalidade do Assistant Api, onde um é focado apenas na OpenAi e o outro é focado em vários modelos disponíveis no Azure AI Foundry.</p>
<p>Neste artigo, vou apresentar a biblioteca que facilita a criação de agentes inteligentes, abstraindo boa parte da lógica que outras frameworks exigem implementações manuais e explicar um pouco o que ela está abstraindo e dando alguns conceitos que vamos explorar em outros artigos usando Semantic Kernel.</p>
<p>Qual?? A que está no título obviamente. Estou falando do <strong>Persistent Agent</strong>, uma biblioteca da Microsoft que integra com modelos LLMs do <strong>Azure AI Foundry</strong>. Um diferencial importante: alguns desses modelos suportam <strong>memória de longo prazo de conversas</strong>, por meio do conceito de <strong>Threads</strong> e é ai que o negócio fica interessante.</p>
<h3 id="heading-mas-o-que-e-uma-thread">Mas o que é uma Thread?</h3>
<p>Uma <strong>Thread</strong> não é apenas um banco de dados onde o histórico da conversa é salvo. Embora tecnicamente ela armazene esse histórico, eu entendo melhor como um <strong>serviço</strong> que:</p>
<ul>
<li><p>Armazena <strong>tudo</strong> que passa pelo agente: mensagens do usuário, respostas do modelo e uso de ferramentas.</p>
</li>
<li><p>Reconstrói o <strong>contexto da conversa</strong> com base nesse histórico, alimentando o modelo com as informações relevantes no momento da resposta.</p>
</li>
</ul>
<p>Isso permite que a conversa evolua de forma mais natural, como se o agente "lembrasse" do que foi dito anteriormente.</p>
<h3 id="heading-como-esse-contexto-e-montado">Como esse contexto é montado?</h3>
<p>A estratégia exata usada pela thread para selecionar e injetar partes do histórico no prompt não é documentada, mas algumas abordagens são possíveis:</p>
<ul>
<li><p><strong>Sliding Window</strong>: mantém uma janela deslizante das últimas interações e inclui essas mensagens diretamente no prompt.</p>
</li>
<li><p><strong>VectorStore (RAG)</strong>: transforma as mensagens em embeddings vetoriais e armazena em uma base vetorial. Quando o usuário envia uma nova mensagem, o sistema busca no vetorstore as mensagens mais próximas semanticamente e as usa como contexto.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752428784568/48dcf5f0-8517-458a-802b-c4464845872a.png" alt="Diagrama de como é gerado o contexto" class="image--center mx-auto" /></p>
<h3 id="heading-qual-a-vantagem-da-thread">Qual a vantagem da Thread?</h3>
<p>Na prática, quando você usa <code>PersistentAgent</code> com suporte a threads, não precisa implementar esses mecanismos manualmente. A biblioteca se encarrega de:</p>
<ul>
<li><p>Criar e manter a thread automaticamente.</p>
</li>
<li><p>Armazenar interações.</p>
</li>
<li><p>Decidir como e o que trazer de volta para o prompt do modelo com base no histórico.</p>
</li>
<li><p>Rapidez na hora de implementar um agente com memória de longo prazo.</p>
</li>
</ul>
<h3 id="heading-qual-a-desvantagem">Qual a desvantagem?</h3>
<p>Você não tem controle de muita coisa. no Azure Ai Foundry (basic) a Thread é armazenada em algum local no seu tenant onde você não tem acesso (bom, você consegue consultar o contéudo, mas não sabe onde está armazenado), no Azure Ai Foundry (standard) a Thread é armazenada num banco CosmosDb.</p>
<p>Você não consegue definir quais estratégias vão ser usadas.</p>
<h3 id="heading-outras-tecnicas-para-montar-o-contexto">Outras técnicas para montar o contexto</h3>
<h4 id="heading-1-entity-tracking-memoria-de-entidades">1. <strong>Entity Tracking (Memória de Entidades)</strong></h4>
<ul>
<li><p><strong>Como funciona:</strong> o sistema extrai e rastreia entidades importantes (nomes, datas, locais, intenções) ao longo da conversa e reusa essas informações nos prompts. (Pra quem já viu Document Intelligence e outras ferramentas de IA, a Entidade Nomeada).</p>
</li>
<li><p><strong>Exemplo:</strong> se o usuário menciona "minha esposa Ana" e depois disser "ela quer ir ao médico", o sistema sabe que "ela" = "Ana".</p>
</li>
</ul>
<hr />
<h4 id="heading-2-summarization-memory-memoria-por-resumo">2. <strong>Summarization Memory (Memória por Resumo)</strong></h4>
<ul>
<li><p><strong>Como funciona:</strong> a cada N interações, o sistema gera um <strong>resumo condensado</strong> da conversa até aquele ponto. Esse resumo é usado nos próximos prompts como contexto. (Um snapshopt da conversa até aquele momento.)</p>
</li>
<li><p><strong>Vantagem:</strong> reduz consumo de tokens mantendo o “contexto" da conversa.</p>
</li>
<li><p><strong>Desvantagem:</strong> pode perder detalhes finos importantes.</p>
</li>
</ul>
<blockquote>
<p>🔁 Pode ser feito de forma incremental (soma novos eventos ao resumo) ou periódica.</p>
</blockquote>
<hr />
<h4 id="heading-3-hierarchical-memory-memoria-hierarquica">3. <strong>Hierarchical Memory (Memória Hierárquica)</strong></h4>
<ul>
<li><p><strong>Como funciona:</strong> mantém diferentes camadas de memória:</p>
<ul>
<li><p><strong>Curto prazo:</strong> últimas interações (Sliding Window)</p>
</li>
<li><p><strong>Médio prazo:</strong> contexto condensado (Summarization)</p>
</li>
<li><p><strong>Longo prazo:</strong> base vetorial ou banco de conhecimento</p>
</li>
</ul>
</li>
</ul>
<hr />
<h4 id="heading-4-topic-based-memory">4. <strong>Topic-based Memory</strong></h4>
<ul>
<li><p><strong>Como funciona:</strong> armazena trechos da conversa agrupados por tópicos detectados automaticamente (ex: “viagem”, “projeto”, “problema técnico”) e resgata o que for mais relevante.</p>
</li>
<li><p><strong>Técnica:</strong> geralmente via clustering de embeddings ou classificação supervisionada.</p>
</li>
</ul>
<blockquote>
<p>🔍 Útil em sessões longas onde o usuário muda de assunto com frequência.</p>
</blockquote>
<hr />
<h4 id="heading-5-fsm-workflow-driven-context">5. <strong>FSM / Workflow-driven Context</strong></h4>
<ul>
<li><p><strong>Como funciona:</strong> a memória é controlada por um fluxo de estado (ex: máquina de estados finita), onde cada etapa tem dados associados ao contexto.</p>
</li>
<li><p><strong>Vantagem:</strong> previsível e mais interpretável.</p>
</li>
<li><p><strong>Usado em:</strong> bots de atendimento, agentes com objetivo claro.</p>
</li>
</ul>
<hr />
<h4 id="heading-6-tool-calling-with-memory-storage">6. <strong>Tool-calling with Memory Storage</strong></h4>
<ul>
<li><p><strong>Como funciona:</strong> o agente chama uma ferramenta (plugin/API interna) que lê/escreve em um repositório de memória estruturada (ex: banco SQL, Redis, MongoDB).</p>
</li>
<li><p><strong>Uso comum:</strong> recuperação de fatos ou preferências salvas do usuário.</p>
</li>
</ul>
<h3 id="heading-show-me-the-code"><strong>Show me the code</strong></h3>
<h4 id="heading-requisitos-para-rodar-o-codigo">Requisitos para rodar o código</h4>
<ul>
<li><p>Azure Subscription</p>
</li>
<li><p>Recurso: Azure AI Foundry</p>
</li>
<li><p>Um modelo criado no Azure AI Foundry (usei o gpt-4.1-mini</p>
</li>
</ul>
<hr />
<p>A estrutura que eu sigo nessas samples é uma minimal api com um Register para os módulos e o serviço com os métodos que vamos utilizar. (vou fazer um template disso logo logo). Dessa maneira se você quiser expandir você pode criar novos registros e novos módulos.</p>
<p>O código completo está no meu <a target="_blank" href="https://github.com/carlosmachel/azure-ai-agents-basic">repositório do GitHub</a></p>
<p>Vou mostrar alguns pontos importantes, mas o código funcional está no meu github (Deixa uma estrelinha lá por favor? Me ajuda muito).</p>
<ol>
<li>Vamos criar a nossa Solution</li>
</ol>
<pre><code class="lang-bash">mkdir azure-ai-agents-basic &amp;&amp; <span class="hljs-built_in">cd</span> azure-ai-agents-basic &amp;&amp; dotnet new sln
</code></pre>
<ol start="2">
<li>Vamos criar o projeto em WebApi e associar com a nossa Solution</li>
</ol>
<pre><code class="lang-bash">dotnet new webapi -n AzureAiAgent.Api &amp;&amp; dotnet sln azure-ai-agents-basic.sln add AzureAiAgent.Api/AzureAiAgent.Api.csproj
</code></pre>
<p>(Se não quiser fazer assim, crie pela sua IDE preferida)</p>
<ol start="3">
<li>Vamos precisar da URI e do Modelo LLM que vamos usar. (Aqui lembrando do requisito para rodar, precisa desse recurso do Azure AI Foundry)</li>
</ol>
<pre><code class="lang-json"><span class="hljs-string">"AzureAiSettings"</span> : {
  <span class="hljs-attr">"Uri"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"Model"</span>: <span class="hljs-string">""</span>
},
</code></pre>
<p>E no <strong>Program.cs</strong> adicionar para utilizar mais tarde no serviço.</p>
<pre><code class="lang-csharp">builder.Services.Configure&lt;AzureAiSettings&gt;(builder.Configuration.GetSection(<span class="hljs-string">"AzureAiSettings"</span>));
</code></pre>
<ol start="4">
<li>Caminhe até a pasta onde está <strong>AzureAiAgent.Api</strong> e rode adicione esses pacotes (da para utilizar o Azure.AI.Agents.Persistent sozinho, mas vou mostrar a criação de Thread usando o Azure.AI.Projects.</li>
</ol>
<pre><code class="lang-bash">dotnet add package Azure.AI.Agents.Persistent --version 1.1.0-beta.1
dotnet add package Azure.AI.Projects --version 1.0.0-beta.9
</code></pre>
<ol start="3">
<li>A implementação das rotas que vamos utilizar</li>
</ol>
<pre><code class="lang-csharp">        app.MapPost(<span class="hljs-string">"/ai-agent"</span>, <span class="hljs-keyword">async</span> (
                [<span class="hljs-meta">FromServices</span>] Service service,
                [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">string</span> name, [FromQuery] <span class="hljs-keyword">string</span> instructions) =&gt;
            {
                <span class="hljs-keyword">var</span> agentId = <span class="hljs-keyword">await</span> service.CreateAgentAsync(name, instructions);
                <span class="hljs-keyword">return</span> Results.Ok(agentId);
            })
            .WithTags(<span class="hljs-string">"Ai Agents"</span>);

        app.MapGet(<span class="hljs-string">"/ai-agent/create-thread"</span>, <span class="hljs-keyword">async</span> (
                [<span class="hljs-meta">FromServices</span>] Service service) =&gt;
            {
                <span class="hljs-keyword">var</span> agentId = <span class="hljs-keyword">await</span> service.CreateThreadAsync();
                <span class="hljs-keyword">return</span> Results.Ok(agentId);
            })
            .WithTags(<span class="hljs-string">"Ai Agents"</span>);

        app.MapGet(<span class="hljs-string">"/ai-agent/run"</span>, <span class="hljs-keyword">async</span> (
                [<span class="hljs-meta">FromServices</span>] Service service,
                [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">string</span> agentId,
                [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">string</span> threadId,
                [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">string</span> userInput) =&gt;
            {
                <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> service.RunAsync(agentId, threadId, userInput);
                <span class="hljs-keyword">return</span> Results.Ok(result);
            })
            .WithTags(<span class="hljs-string">"Ai Agents"</span>);
</code></pre>
<p>E por fim o nosso exemplo de serviço de implementação:</p>
<ol start="4">
<li>Primeiro, criaremos o agente usando a biblioteca <strong>Azure.AI.Agents.Persistent</strong> onde tem que passar a URI e estou usando a DefaultAzureCredential (então tem que usar o az login para logar na sua subscription do Azure).</li>
</ol>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;<span class="hljs-keyword">string</span>&gt; <span class="hljs-title">CreateAgentAsync</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> agentName, <span class="hljs-keyword">string</span> instructions</span>)</span>
    {
        <span class="hljs-keyword">var</span> agentClient = <span class="hljs-keyword">new</span> PersistentAgentsClient(options.Value.Uri, <span class="hljs-keyword">new</span> DefaultAzureCredential());
        PersistentAgent agent = <span class="hljs-keyword">await</span> agentClient.Administration.CreateAgentAsync(options.Value.Model,
            name: agentName,
            instructions: instructions);
        <span class="hljs-keyword">return</span> agent.Id;
    }
</code></pre>
<p>Repare que nós temos que criar o PersistentAgentsClient e a partir dai criar o agente passando o nome do Agente que vc quer dar e a instruções (exemplo: agentName = Agente Básico, instructions = “Você é um agente útil”).</p>
<p>Com isso a gente tem o nosso Agente, mas ainda não tem a nossa Thread!</p>
<ol start="5">
<li>Método para criar a Thread.</li>
</ol>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;<span class="hljs-keyword">string</span>&gt; <span class="hljs-title">CreateThreadAsync</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">var</span> projectClient = <span class="hljs-keyword">new</span> AIProjectClient(<span class="hljs-keyword">new</span> Uri(options.Value.Uri), <span class="hljs-keyword">new</span> DefaultAzureCredential());
    <span class="hljs-keyword">var</span> agentClient = projectClient.GetPersistentAgentsClient();

    PersistentAgentThread thread = <span class="hljs-keyword">await</span> agentClient.Threads.CreateThreadAsync();
    <span class="hljs-keyword">return</span> thread.Id;
}
</code></pre>
<p>Estou mostrando um exemplo usando o AIProjectClient que com ele vc pega o cliente do PersistentAgentClient. Percebe que a gente chega no mesmo client no final? A biblioteca do <strong>Azure.AI.Projects</strong> dd é uma abstração maior ao Projeto no Azure Ai Foundry. Você consegue com ele criar outros clients.</p>
<ol start="6">
<li>Por fim, criar um serviço para fazer um Run que é quando o usuário interage com o Agente.</li>
</ol>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IEnumerable&lt;<span class="hljs-keyword">string</span>&gt;&gt; RunAsync(<span class="hljs-keyword">string</span> assistantId, <span class="hljs-keyword">string</span> threadId, <span class="hljs-keyword">string</span> userInput)
{
    <span class="hljs-keyword">var</span> projectClient = <span class="hljs-keyword">new</span> AIProjectClient(<span class="hljs-keyword">new</span> Uri(options.Value.Uri), <span class="hljs-keyword">new</span> DefaultAzureCredential());
    <span class="hljs-keyword">var</span> agentClient = projectClient.GetPersistentAgentsClient();

    PersistentAgent agent = <span class="hljs-keyword">await</span> agentClient.Administration.GetAgentAsync(assistantId);
    PersistentAgentThread thread = <span class="hljs-keyword">await</span> agentClient.Threads.GetThreadAsync(threadId);

    <span class="hljs-keyword">await</span> agentClient.Messages.CreateMessageAsync( threadId, role: MessageRole.User, content: userInput);

    ThreadRun run = <span class="hljs-keyword">await</span> agentClient.Runs.CreateRunAsync(thread, agent);

    <span class="hljs-keyword">do</span>
    {
        <span class="hljs-keyword">await</span> Task.Delay(TimeSpan.FromMilliseconds(<span class="hljs-number">500</span>));
        run = <span class="hljs-keyword">await</span> agentClient.Runs.GetRunAsync(thread.Id, run.Id);
    }
    <span class="hljs-keyword">while</span> (run.Status == RunStatus.Queued
           || run.Status == RunStatus.InProgress
           || run.Status == RunStatus.RequiresAction);

    <span class="hljs-keyword">var</span> messagesResponse = agentClient.Messages.GetMessagesAsync(thread.Id, order: ListSortOrder.Descending);
    <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;();

    <span class="hljs-keyword">await</span> <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> threadMessage <span class="hljs-keyword">in</span> messagesResponse)
    {
        <span class="hljs-keyword">if</span> (threadMessage.Role == MessageRole.Agent &amp;&amp;
            threadMessage.ContentItems.FirstOrDefault() <span class="hljs-keyword">is</span> MessageTextContent messageTextContent)
        {
            result.Add(messageTextContent.Text);
        }
    }

    <span class="hljs-keyword">return</span> result;
}
</code></pre>
<p>A princípio parece que é muita coisa ocorrendo nesse código.</p>
<p>O objetivo desse método é usar as informações obtidas nos últimos dois métodos e rodar a pergunta de um usuário</p>
<p>Obter o Agente pelo assistantId, pegar a thread pelo threadId.</p>
<p>Observem que aqui começa a mágica: a criação da mensagem não tem nada haver com o Agente e sim com a Thread. Adiciona a mensagem do Usuário na Thread.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">await</span> agentClient.Messages.CreateMessageAsync( threadId, role: MessageRole.User, content: userInput);
</code></pre>
<p>Desse ponto do método pra desse ponto pra baixo é a conexão do Agente com a Thread a execução do Agente e recuperando o retorno do agente.</p>
<p>Esse ponto tem uma sequencia de coisas acontecendo.</p>
<ul>
<li><p>A Thread seleciona o que vai pro contexto do Agente. (O ponto central desse post)</p>
</li>
<li><p>e o agente gera a mensagem e a resposta é salva na Thread</p>
</li>
<li><p>A busca da resposta do agente é feita pela Thread novamente. Você pode ver que não tem mais relação nenhuma agora com o agente. O papel ele ja fez.</p>
</li>
</ul>
<p>Nos próximos artigos eu vou trazer um pouco mais da flexibilidade do <a target="_blank" href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Semantic Kernel</a> para implementar essas estratégias e dar o total controle sobre o agente. E sinceramente aprender como implementar junto com vocês.</p>
<h3 id="heading-referencias">Referências</h3>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/api/overview/azure/ai.agents.persistent-readme?view=azure-dotnet">Azure.AI.Agents.Persistent</a></p>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/python/api/overview/azure/ai-projects-readme?view=azure-python-preview">Azure.AI.Projects</a></p>
<p><a target="_blank" href="https://github.com/carlosmachel/azure-ai-agents-basic">Repositório do Github</a></p>
]]></content:encoded></item><item><title><![CDATA[Modularidade]]></title><description><![CDATA[💡
Série de artigos que vou escrever estudando o livro Software Architecture Fundamentals documentando meu aprendizado e minha visão dos capítulos.



Quem é desenvolvedor já deve ter trabalhado em sistemas desorganizados; em sistemas novos que começ...]]></description><link>https://carlosmachel.com/modularidade</link><guid isPermaLink="true">https://carlosmachel.com/modularidade</guid><category><![CDATA[arquitetura de software]]></category><category><![CDATA[software architecture]]></category><dc:creator><![CDATA[Carlos Machel]]></dc:creator><pubDate>Mon, 11 Dec 2023 16:31:11 GMT</pubDate><content:encoded><![CDATA[<hr />
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Série de artigos que vou escrever estudando o livro <a target="_blank" href="https://www.amazon.com.br/Fundamentals-Software-Architecture-Neal-Ford/dp/1492043451/ref=asc_df_1492043451/?tag=googleshopp00-20&amp;linkCode=df0&amp;hvadid=379795170134&amp;hvpos=&amp;hvnetw=g&amp;hvrand=14947157579504890135&amp;hvpone=&amp;hvptwo=&amp;hvqmt=&amp;hvdev=c&amp;hvdvcmdl=&amp;hvlocint=&amp;hvlocphy=1001773&amp;hvtargid=pla-751683725274&amp;psc=1">Software Architecture Fundamentals</a> documentando meu aprendizado e minha visão dos capítulos.</div>
</div>

<hr />
<p>Quem é desenvolvedor já deve ter trabalhado em sistemas desorganizados; em sistemas novos que começaram organizados e que acabou se tornando desorganizado ao longo do tempo.</p>
<p>Você já ficou na dúvida de onde colocar alguma classe, função e colocou num pacote chamado "utils".</p>
<p>Tudo isso afeta a qualidade final da arquitetura e do código como um todo.</p>
<p>Quanto mais complexo um sistema, mais esforço deve ser feito para manter uma organização. Um sistema nesse sentido é parecido com uma casa, é necessário dar manutenção senão ela desaba.</p>
<p>Nosso cérebro naturalmente organiza as coisas em blocos, na arquitetura essa organização do código se dá por módulos, e aqui cabe uma definição de módulo que abriga o mesmo conceito independente da linguagem utilizada.</p>
<p><em>Modularidade é o agrupamento lógico de códigos relacionados.</em></p>
<p>Para discussões de arquitetura, usamos modularidade como um termo geral para grupos de códigos relacionados: classes, funções, ou qualquer outro tipo de agrupamento.</p>
<p><mark>Modularidade não requer uma separação física</mark>. Por exemplo, é possível ter um monolito com suas classes no mesmo projeto, mas com uma separação lógica. Se chegar o momento que seja necessário separar o código em alguns microservicos, o esforço seria menor. O porém é que separação lógica e não física requer mais maturidade da equipe de não misturar os conceitos, quando tem essa barreira física, é mais dificil misturar.</p>
<p>Faz parte do papel do Arquiteto e do desenvolvedor de gastar "energia" para garantir a manutenção saudável do sistema. E essa organização do código, é uma característica implicita de arquitetura, nenhum cliente passa o requisito específico de manter o código organizado (manutenabilidade).</p>
<p>Um arquiteto deve estar ciente de como os desenvolvedores organizam as coisas, porque isso tem implicações importantes na arquitetura. Se os pacotes estão muito acoplados, reutilizá-los para trabalhos relacionados fica mais difícil.</p>
<p>Eu considero também que muitas vezes a organização dos pacotes pelos desenvolvedores, demonstra uma falta de conhecimento da equipe de como a arquitetura funciona e também temos os problemas com a modelagem de dados.</p>
<h2 id="heading-medindo-modularidade">Medindo Modularidade</h2>
<p>Temos três conceitos fundamentais para medir modularidade de um sistema, coesão, acoplamento e connascence (sem tradução para o português). E temos métricas para medí-los.</p>
<p>Aqui cabe um aviso, eu coloquei os exemplos nos conceitos que foram mais complicados para entender.</p>
<h3 id="heading-coesao">Coesão</h3>
<p>Coesão mede o quanto as partes são relacionadas entre si. O quanto os indivíduos daquele grupo fazem realmente parte daquele grupo.</p>
<p>Idealmente você deve agrupar partes que são inerentemente dependentes, tanto que se você quebrar em módulos menores, você ainda vai ter que relacionar eles por chamadas.</p>
<p>Cientistas da computação definiram um grupo de medidas de coesão, listado do melhor para o pior.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699876510819/dbe8de03-7025-4fd7-a56c-0e5a9fc50ccb.png" alt="Tipos de coesão." class="image--center mx-auto" /></p>
<ol>
<li><p>Coesão funcional</p>
<p> Cada parte do módulo está relacionada entre si e o módulo contém tudo que é essencial para funcionar. Esse é o mundo perfeito da coesão.</p>
</li>
<li><p>Coesão sequencial</p>
<p> Dois módulos interagem, onde a saida de um se torna a entrada do outro.</p>
<p> Pegando um exemplo simples, programação funcional tem muita coesão sequencial. No exemplo abaixo, o select só vai pegar os dados das pessoas filtradas pelo Where.</p>
<pre><code class="lang-csharp">   <span class="hljs-keyword">var</span> pessoas = <span class="hljs-keyword">new</span> List&lt;Pessoa&gt;() {<span class="hljs-keyword">new</span>(<span class="hljs-number">10</span>, <span class="hljs-string">"Jose"</span>), <span class="hljs-keyword">new</span>(<span class="hljs-number">18</span>, <span class="hljs-string">"Maria"</span>)};

   <span class="hljs-keyword">return</span> pessoas.Where(p =&gt; p.Idade &gt;= <span class="hljs-number">18</span>).Select(p =&gt; p.Nome);
</code></pre>
</li>
<li><p>Coesão comunicacional</p>
<p> Demorei um pouco para formular na minha cabeça sobre esse tipo de coesão, a chave para entender são os dados. Os dados são compartilhados entre os elementos do módulo ou entre os módulos, gerando uma relação entre eles. A fonte de informação é a mesma, porém as tarefas a serem executadas podem ser diferentes.</p>
<p> No livro eles fazem um exemplo com salvar dados no banco e enviar um email. Abaixo um exemplo em C# onde o ponto de coesão são os valores (data) utilizados nos dois métodos distintos.</p>
<pre><code class="lang-csharp"> <span class="hljs-keyword">using</span> System;
 <span class="hljs-keyword">using</span> System.Linq;

 <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
 {
     <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>)</span>
     {
         <span class="hljs-comment">// Exemplo de dados</span>
         <span class="hljs-keyword">int</span>[] data = { <span class="hljs-number">10</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">15</span>, <span class="hljs-number">7</span> };

         <span class="hljs-comment">// Chamando funções do módulo estatístico</span>
         <span class="hljs-keyword">double</span> average = CalculateAverage(data);
         <span class="hljs-keyword">int</span> sum = CalculateSum(data);

         <span class="hljs-comment">// Exibindo resultados</span>
         Console.WriteLine(<span class="hljs-string">$"Média: <span class="hljs-subst">{average}</span>"</span>);
         Console.WriteLine(<span class="hljs-string">$"Soma: <span class="hljs-subst">{sum}</span>"</span>);
     }

     <span class="hljs-comment">// Função para calcular a média</span>
     <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">double</span> <span class="hljs-title">CalculateAverage</span>(<span class="hljs-params"><span class="hljs-keyword">int</span>[] values</span>)</span>
     {
         <span class="hljs-comment">// A coesão comunicacional acontece através do parâmetro 'values'</span>
         <span class="hljs-keyword">double</span> average = values.Average();
         <span class="hljs-keyword">return</span> average;
     }

     <span class="hljs-comment">// Função para calcular a soma</span>
     <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">CalculateSum</span>(<span class="hljs-params"><span class="hljs-keyword">int</span>[] values</span>)</span>
     {
         <span class="hljs-comment">// A coesão comunicacional acontece através do parâmetro 'values'</span>
         <span class="hljs-keyword">int</span> sum = values.Sum();
         <span class="hljs-keyword">return</span> sum;
     }
 }
</code></pre>
</li>
<li><p>Coesão procedural</p>
<p> Dois módulos devem executar o código numa ordem particular. Como no exemplo abaixo a função "CalculateStatistics" funciona de maneira procedural, onde cada passo é necessario ser realizado em ordem para ter o resultado.</p>
<pre><code class="lang-csharp"> <span class="hljs-keyword">using</span> System;
 <span class="hljs-keyword">using</span> System.Linq;

 <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
 {
     <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>)</span>
     {
         <span class="hljs-comment">// Exemplo de lista de números</span>
         <span class="hljs-keyword">int</span>[] numbers = { <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">2</span>, <span class="hljs-number">10</span>, <span class="hljs-number">7</span> };

         <span class="hljs-comment">// Chamando funções do módulo de manipulação de lista</span>
         CalculateStatistics(numbers);
     }

     <span class="hljs-comment">// Função procedural para calcular média e desvio padrão</span>
     <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">CalculateStatistics</span>(<span class="hljs-params"><span class="hljs-keyword">int</span>[] values</span>)</span>
     {
         <span class="hljs-comment">// Calcula a média</span>
         <span class="hljs-keyword">double</span> average = values.Average();
         Console.WriteLine(<span class="hljs-string">$"A média dos números é: <span class="hljs-subst">{average:F2}</span>"</span>);

         <span class="hljs-comment">// Calcula o desvio padrão</span>
         <span class="hljs-keyword">double</span> sumOfSquares = values.Sum(v =&gt; 
             Math.Pow(v - average, <span class="hljs-number">2</span>));
         <span class="hljs-keyword">double</span> variance = sumOfSquares / values.Length;
         <span class="hljs-keyword">double</span> standardDeviation = Math.Sqrt(variance);
         Console.WriteLine(<span class="hljs-string">$"O desvio padrão dos números é: 
             <span class="hljs-subst">{standardDeviation:F2}</span>"</span>);
     }
 }
</code></pre>
</li>
<li><p>Coesão temporal</p>
<p> Módulos estão relacionados com base no tempo. Eles não tem relação entre eles a não ser pela característica temporal. Por exemplo, o Startup de uma aplicação .NET.</p>
<pre><code class="lang-csharp"> <span class="hljs-keyword">var</span> builder = WebApplication.CreateBuilder(args);

 <span class="hljs-comment">// Add services to the container.</span>
 builder.Services.AddControllers();

 builder.Services.AddEndpointsApiExplorer();
 builder.Services.AddSwaggerGen();

 <span class="hljs-keyword">var</span> configuration = builder.Configuration;

 <span class="hljs-keyword">var</span> connectionString = configuration.GetSection(
 <span class="hljs-string">"ConnectionStrings:DefaultConnection"</span>);

 builder.Services.AddHealthChecks(configuration);
</code></pre>
</li>
<li><p>Coesão lógica</p>
<p> Os dados dentro do módulo são relacionados lógicamente, mas não funcionalmente. O pacote StringUtils do Java, são métodos que atuam em cima da String, mas fora isso não tem relação um com outro.</p>
</li>
<li><p>Coesão coincidente.</p>
<p> Elementos do módulo não são relacionados a não ser estar na mesma pasta. (Pior cenário, o utils)</p>
</li>
</ol>
<p>Como medir coesão?</p>
<p>LCOM, que significa "Lack of Cohesion of Methods," é uma métrica usada para avaliar a coesão em uma classe em programação orientada a objetos. Coesão refere-se à medida em que os membros de uma classe (métodos e campos) estão relacionados uns com os outros. Uma coesão alta indica que os membros estão fortemente relacionados e trabalham juntos para alcançar um propósito comum. Por outro lado, uma coesão baixa sugere que os membros da classe podem não estar tão relacionados e podem ter responsabilidades divergentes.</p>
<p>A métrica LCOM é uma maneira de quantificar a coesão em uma classe, com o objetivo de avaliar a qualidade do design do software.</p>
<p>A ideia é contar quantos pares de métodos não compartilham campos em comum e, em seguida, calcular a porcentagem disso em relação ao número total de pares de métodos possíveis.</p>
<p>É importante notar que a métrica LCOM é uma ferramenta e não deve ser usada isoladamente para avaliar a qualidade de um design. Ela fornece insights sobre a coesão, mas o contexto e a compreensão mais profunda do código são essenciais para uma avaliação completa. Além disso, existem outras métricas de coesão e acoplamento que podem ser usadas em conjunto para fornecer uma visão mais abrangente da qualidade do design do software.</p>
<h3 id="heading-acoplamento">Acoplamento</h3>
<p>Acoplamento refere-se ao grau de interdependência entre módulos de um sistema.</p>
<p>Assim como coesão ele é dividido em diferentes categorias da melhor para pior.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1700402711447/92a60c95-e7af-4212-9677-2af3fb183538.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>Dados</p>
<p> Acoplamento de dados ocorre quando os módulos compartilham dados através de parametros, mas não tem conhecimento da implementação interna um do outro. Além de que um dado nao altera o dado do outro módulo.</p>
</li>
<li><p>Estrutura de Dados</p>
<p> Em inglês é Stamp, algo como carimbo, mas eu achei melhor colocar Estrutura de dados porque ele está relacionado com os módulos compartilharem estruturas completas de dados não apenas dados.</p>
</li>
<li><p>Controle</p>
<p> Um módulo exerce controle no comportamento de outro módulo. Isso indica que um módulo possui conhecimento interno do outro módulo.</p>
</li>
<li><p>Externo</p>
<p> O quanto um sistema depende de entidades externas aos módulos que estão sendo desenvolvidos. Dependencia de banco, de arquivos.</p>
</li>
<li><p>Comum</p>
<p> Os módulos compartilham de um dado global. Então alterações nesses dados podem afetar todos os módulos.</p>
</li>
<li><p>Conteúdo</p>
<p> Quando dois ou mais módulos compartilham código. Um afeta diretamente a funcionalidade do outro. Esse aqui me confundiu um pouco. Eu entendo como uma falha de implementação em modificadores de acesso no caso do C#.</p>
</li>
</ol>
<p>Como medir acoplamento?</p>
<p>Edward Yourdon e Larry Constantine definiram duas métricas de acoplamento. A Aferente e a eferente.</p>
<p><strong>Aferente</strong> mede o número de conexões chegando (incoming) para um artefato de código.</p>
<p><strong>Eferente</strong> mede o número de conexões saindo (outgoing) para outros artefatos de código.</p>
<p>No geral os tipos de conexões podem variar se são piores ou melhores. Para o quesito acoplamento, conexões aferentes (que estão chegando) são piores que as eferentes, porque significa que alterações em outros módulos podem quebrar o seu módulo e isso tem haver com métricas criadas pelo Robert Martin que explico a seguir.</p>
<h3 id="heading-abstracao-instabilidade-e-distancia-da-sequencia-principal">Abstração, instabilidade e distância da sequencia principal.</h3>
<p>Essas métricas foram criadas pelo Robert Martin para uma linguagem específica (C++), mas pode ser utilizado em outras linguagens.</p>
<h3 id="heading-abstracao">Abstração</h3>
<p>Abstração é a razão de artefatos abstratos (classes abstratas, interfaces) para artefatos concretos (implementação).</p>
<p>Existe um extremo que é sem nenhuma abstração, o código está todo no "main" e no outro extremo muita abstração pode se tornar mais complicado para o desenvolvedor de entender como as coisas se conectam.</p>
<p>A abstração é calculada pela razão da soma dos artefatos abstratos (interfaces e classes abstratas) com a soma das classes concretas.</p>
<p>$$A = \sum m^a \div \sum m^c$$</p><h3 id="heading-instabilidade">Instabilidade</h3>
<p>É a razão entre acoplamento eferente com a soma de eferente + aferente.</p>
<p>A métrica de instabilidade determina a volatilidade do código fonte. Um código fonte que possui alta porcentagem de instabilidade quebra mais facilmente quando modificado devido a alto acoplamento.</p>
<p>É só pensar essa razão leva em conta o quanto o seu sistema depende de chamadas externas. Então quanto maior for e quanto mais facilmente seu sistema é quebrado por mudanças de fora.</p>
<p>A instabilidade é calculada pela soma dos acoplamentos eferentes (conexões que saem) dividido pela soma dos acoplamentos eferentes e aferentes.</p>
<p>$$I = C^e \div C^e + C^a$$</p><h3 id="heading-distancia-da-sequencia-principal">Distância da Sequencia principal</h3>
<p>Essa distância é calculada pelo módulo da soma de "abstração" e "instabilidade" menos 1.</p>
<p>$$D = |A + I - 1 |$$</p><p>Essa sequencia principal define a relação ideal entre abstração e instabilidade. Sair dessa linha indica algum problema no seu módulo.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1700651247274/516041f3-e2e3-4768-94e3-217ee1c3b18a.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-connascence">Connascence</h3>
<blockquote>
<p>Dois componentes são connascents se uma mudança em um exige uma mudança no outro para manter o sistema correto. - Meilir Page-Jones.</p>
</blockquote>
<p>Existem dois tipos de connascence: Estática e Dinâmica.</p>
<p>Como os autores explicam, é um refinamento do acoplamento aferente e eferente. O arquiteto consegue ver pelos tipos de connascence o grau de acoplamento seja aferente ou eferente.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1700652986395/46e8d003-8180-4fb7-a5f1-63150721c61f.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>Connascence de Nome</p>
<p> Componentes devem concordar com o nome de uma entidade. É o nível mais fraco de connasnce, porque mudar é facil, não gera tanto impacto.</p>
</li>
<li><p>Connascence de Tipo</p>
<p> Componentes devem concordar com o tipo de uma entidade.</p>
</li>
<li><p>Connascence de Significado (Convenção)</p>
<p> Componentes devem concordar com o significado de certos valores particulares. Exemplo clássico é o Boolean que é tratado em muitas linguagens como TRUE = 1 e FALSE = 0.</p>
</li>
<li><p>Connascence de Posição</p>
<p> Componentes devem concordar com a ordem dos valores. Esse aqui é um exemplo muito comum quando o método tem como parâmetros por exemplo duas strings.</p>
<pre><code class="lang-csharp"> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">CreateCategory</span>(<span class="hljs-params">String name, String description</span>)</span>;
</code></pre>
<p> Se o desenvolvedor trocar a ordem dos valores não vai gerar erro, mas semanticamente está errado.</p>
</li>
<li><p>Connascence de Algortimo</p>
<p> Componentes devem concordar com um algoritmo. Um exemplo é o algoritmo de hash que deve rodar no cliente e no servidor devem ser o mesmo para autenticar um usuário, se um dos lados alterar o algoritmo, quebra o outro componente.</p>
</li>
<li><p>Connascence de Execução</p>
<p> A ordem de execução dos componentes é importante.</p>
</li>
<li><p>Connascence de Timing</p>
<p> O "timing" de execução de multiplos componentes é importante.</p>
</li>
<li><p>Connascence de Valor</p>
<p> Ocorre quando vários valores relacionam entre si e devem mudar juntos. Pensando em uma estrutura de microserviço você tem seus diferentes contextos e compartilha algumas informações, quando um valor é alterado em um microserviço tem que ser atualizado no resto.</p>
</li>
<li><p>Connascence de Identidade</p>
<p> Ocorre quando múltiplos componentes devem referencia a mesma entidade.</p>
<p> Componentes independentes compartilham uma estrutura de dados em comum.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Pensamento Arquitetural]]></title><description><![CDATA[💡
Série de artigos que vou escrever estudando o livro Software Architecture Fundamentals documentando meu aprendizado e minha visão dos capítulos.



Quatro principais aspectos:

Compreender a diferença entre arquitetura e design e saber como auxili...]]></description><link>https://carlosmachel.com/pensamento-arquitetural</link><guid isPermaLink="true">https://carlosmachel.com/pensamento-arquitetural</guid><category><![CDATA[software architecture]]></category><category><![CDATA[software development]]></category><category><![CDATA[software]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Carlos Machel]]></dc:creator><pubDate>Sat, 28 Oct 2023 19:02:13 GMT</pubDate><content:encoded><![CDATA[<hr />
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Série de artigos que vou escrever estudando o livro <a target="_blank" href="https://www.amazon.com.br/Fundamentals-Software-Architecture-Neal-Ford/dp/1492043451/ref=asc_df_1492043451/?tag=googleshopp00-20&amp;linkCode=df0&amp;hvadid=379795170134&amp;hvpos=&amp;hvnetw=g&amp;hvrand=14947157579504890135&amp;hvpone=&amp;hvptwo=&amp;hvqmt=&amp;hvdev=c&amp;hvdvcmdl=&amp;hvlocint=&amp;hvlocphy=1001773&amp;hvtargid=pla-751683725274&amp;psc=1">Software Architecture Fundamentals</a> documentando meu aprendizado e minha visão dos capítulos.</div>
</div>

<hr />
<p>Quatro principais aspectos:</p>
<ol>
<li><p>Compreender a diferença entre arquitetura e design e saber como auxiliar a equipe de desenvolvimento a implementar com sucesso a arquitetura.</p>
</li>
<li><p>Ter um amplo conhecimento em várias áreas e manter um profundo domínio em um nível específico, permitindo uma visão abrangente que o arquiteto percebe, mas os outros não.</p>
</li>
<li><p>Lidar com os <em>"dependes"</em> (ou "trade-offs", termo mais sofisticado em inglês). Nenhuma solução é perfeita; sempre haverá sacrifícios a serem feitos. A questão está em saber o que sacrificar e compreender as consequências disso.</p>
</li>
<li><p>E, o mais importante, uma visão arquitetural requer um entendimento de negócios. As decisões arquiteturais são baseadas nas necessidades do cliente e no contexto de negócios.</p>
</li>
</ol>
<hr />
<h2 id="heading-arquitetura-vs-design">Arquitetura vs Design</h2>
<p>Na abordagem tradicional, o arquiteto analisa todos os requisitos de negócio, define as características arquiteturais, seleciona os padrões arquiteturais e estilos que se adequam ao domínio do problema e cria os componentes. O resultado é então repassado à equipe de desenvolvimento, que é encarregada de criar os diagramas de classe para cada componente, desenvolver a interface do usuário, escrever o código-fonte e realizar testes.</p>
<p>É importante notar que, na abordagem tradicional, a comunicação é unidirecional, e o papel do arquiteto muitas vezes termina aí. No entanto, essa abordagem tradicional nem sempre funciona bem. O arquiteto deve fazer parte da equipe de desenvolvimento, e aqui estão algumas razões para isso:</p>
<p>Para ilustrar esse ponto, gostaria de compartilhar uma experiência recente que tive. Tive a oportunidade de projetar a arquitetura de um sistema para um cliente, mas, devido a várias razões, não pude acompanhar o desenvolvimento de perto. Algum tempo depois, fui convocado para uma reunião para discutir um possível problema no sistema. Esse problema não existia na arquitetura que propus e, no pior cenário, era mitigado. No entanto, durante o desenvolvimento, certas estruturas foram modificadas, e eu não fui informado sobre essas mudanças.</p>
<p>Outra situação que já vivenciei foi quando atuei como desenvolvedor. O arquiteto havia criado a estrutura inicial, mas não participou ativamente do desenvolvimento com a equipe. Como resultado, ele não percebeu os problemas e as dificuldades que a arquitetura apresentava para a equipe de desenvolvimento.</p>
<p>Mark Richards explica nesse <a target="_blank" href="https://www.youtube.com/watch?v=0tEBv2kAuNY">vídeo</a> de forma mais detalhada a diferença entre arquitetura e design, utilizando diversos exemplos. Recomendo assisti-lo para aprofundar sua compreensão sobre esse conceito.</p>
<h2 id="heading-technical-breadth">Technical Breadth</h2>
<p>A maioria de vocês já deve ter ouvido falar sobre o conceito de "desenvolvimento em T". Nesse modelo, você adquire conhecimento em diversas áreas, mas se especializa profundamente em um campo específico.</p>
<p>Por exemplo, um desenvolvedor de destaque terá um amplo conhecimento em um domínio específico, tornando-se um especialista nessa área. Portanto, seu perfil "T" se assemelha à imagem abaixo, com um grande conhecimento vertical em uma área específica, mas menos conhecimento horizontal em outras áreas.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698232303680/488fcbaa-3b31-41b2-a2d8-4d4172b6cf84.png" alt class="image--center mx-auto" /></p>
<p>No caso de um arquiteto de software, o conceito de "T" se assemelha mais à imagem abaixo. Enquanto ele pode ter especialização em áreas específicas, como uma forte compreensão da stack .NET ou da stack Java, ele também deve expandir seu conhecimento horizontal para avaliar e propor as melhores ferramentas, frameworks e linguagens para os projetos.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698232373486/6a4f4552-b0d4-4fe9-9385-2d192f8ad7df.png" alt class="image--center mx-auto" /></p>
<p>A pirâmide de conhecimento é uma representação conceitual que descreve a profundidade e a extensão do conhecimento de uma pessoa em relação a um determinado tópico ou campo de estudo. É comumente dividida em três partes:</p>
<ol>
<li><p><strong>O que você sabe:</strong> O topo da pirâmide é o conhecimento que você tem e reconhece ter. São informações, habilidades e conceitos que você possui e pode aplicar.</p>
</li>
<li><p><strong>O que você sabe que não sabe:</strong> Essa camada representa o conhecimento que você reconhece não possuir, mas está ciente de sua existência. Inclui tópicos, habilidades ou informações que você sabe que precisa aprender ou explorar.</p>
</li>
<li><p><strong>O que você não sabe que não sabe:</strong> está na base da pirâmide e representa o conhecimento que está além da sua consciência, coisas das quais você não tem conhecimento, e você não está ciente de sua existência.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698404839496/f94d1c9f-dceb-447b-9579-b54f7b5c8ec0.png" alt class="image--center mx-auto" /></p>
<p>Há muito tempo, eu estava envolvido no desenvolvimento de um sistema, porém, naquela época, meu conhecimento se limitava ao Asp.net WebForms. Eventualmente, deparei-me com a necessidade de uma solução mais adequada para um sistema específico, que, como descobri posteriormente, era uma Single Page Application (SPA). No entanto, naquele momento, eu nem sabia que existia algo chamado SPA. Para lidar com os desafios que o Asp.net WebForms não conseguia resolver, iniciei uma pesquisa e deparei-me com informações sobre frameworks como Ember e Angular, o que, por fim, me apresentou o conceito de SPA.</p>
<p>Como desenvolvedor, estamos constantemente em busca de aprimorar nossa base de conhecimento, visando ampliar o topo da "pirâmide de conhecimento". No entanto, é crucial reconhecer que o conhecimento que possuímos está sujeito ao avanço do tempo. Por exemplo, trabalhei com Angular há algum tempo, o que significa que ainda tenho algum entendimento sobre ele, embora meu conhecimento possa estar desatualizado devido às evoluções na área.</p>
<p>Um ponto fundamental destacado no livro é que a mudança de mentalidade de desenvolvedor para arquiteto implica, em grande parte, em equilibrar a expansão do topo da pirâmide com um foco na ampliação do "meio". Como arquiteto, é mais vantajoso possuir um conhecimento abrangente, mesmo que superficial, de diversos tipos de bancos de dados, em vez de se aprofundar em apenas um. Essa abordagem oferece uma visão mais holística e possibilita escolhas mais informadas ao projetar sistemas complexos.</p>
<p>Considero esse cenário bastante interessante. Compreendo o ponto de vista e, assim como os autores, acredito que essa seja a parte mais desafiadora durante a transição de carreira. É preciso fazer escolhas criteriosas sobre onde manter uma especialização e onde abrir mão, concentrando-se em expandir a base de conhecimento, já que nosso tempo é um recurso limitado.</p>
<p>Tive a sorte de ter trabalhado em ambientes nos quais fui exposto a uma ampla variedade de desafios. Embora meu foco de estudo seja predominantemente C# e o desenvolvimento no backend, tive a oportunidade de atuar em diversos contextos, envolvendo diferentes linguagens, bancos de dados, frameworks e áreas como frontend e desenvolvimento mobile. Essa diversidade de experiências tem enriquecido minha visão e habilidades, preparando-me para desafios mais amplos e complexos em minha carreira.</p>
<h2 id="heading-trade-offs">Trade-offs</h2>
<p>A análise de trade-offs não é uma prática exclusiva de arquitetos. Desenvolvedores também devem considerar os prós e contras ao escolher uma estrutura de dados. Por exemplo, ao trabalhar em algoritmos complexos de otimização, uma decisão errada no código pode facilmente resultar em um StackOverflow.</p>
<p>No entanto, no caso dos arquitetos, o impacto de suas decisões é amplificado. As decisões arquiteturais afetam todo o ciclo de desenvolvimento de um projeto. Portanto, os arquitetos precisam ser cautelosos ao seguir tendências, pois nem todos os sistemas devem adotar uma arquitetura de microserviços, nem todos os bancos de dados precisam ser NoSQL. Múltiplos fatores devem ser levados em consideração.</p>
<p>Uma das restrições que, em minha opinião, mais influencia as decisões arquiteturais é a maturidade da equipe. Implantar uma arquitetura complexa em uma equipe com pouca experiência pode criar gargalos significativos no ciclo de desenvolvimento. Portanto, é importante considerar o nível de conhecimento e experiência da equipe ao tomar decisões arquiteturais.</p>
<h2 id="heading-entender-do-negocio">Entender do Negócio</h2>
<p>Ao criar uma arquitetura, uma das responsabilidades centrais de um arquiteto é a análise das necessidades do cliente. Isso envolve fazer as perguntas apropriadas para extrair os requisitos fundamentais do sistema e, em seguida, desenvolver a arquitetura com base nesses requisitos.</p>
<p>Frequentemente, o cliente expressa desejos que podem não se alinhar diretamente com as necessidades essenciais do projeto. Nesse cenário, cabe ao arquiteto gerenciar essas expectativas. Acredito que as habilidades interpessoais desempenham um papel crucial nesse processo, permitindo uma comunicação eficaz com o cliente e a capacidade de traduzir desejos em requisitos práticos.</p>
<h2 id="heading-arquitetura-e-codificacao">Arquitetura e Codificação</h2>
<p>Por fim, é importante destacar um aspecto do pensamento arquitetural, que é uma crença compartilhada pelos autores e que considero fundamental: o arquiteto deve ter experiência em desenvolvimento.</p>
<p>As estratégias para alcançar isso podem variar, e uma delas envolve a atuação do arquiteto como parte da equipe de desenvolvimento, embora em áreas que não causem gargalos para o progresso da equipe. Ou seja, funcionalidades críticas que impedem o avanço da equipe não devem ser de responsabilidade do arquiteto, já que ele tem outras funções a cumprir.</p>
<p>Outra estratégia proposta são as famosas POCs (Proof of Concept). A implementação dessas POCs serve para testar frameworks, tecnologias e bancos de dados. Além disso, essas POCs funcionam como um ponto de partida para que a equipe de desenvolvimento continue a implementação da funcionalidade.</p>
<p>Só uma coisa a se tomar cuidado nessas POCs. Geralmente quando fazemos POCs, pulamos algumas boas práticas para testar exatamente se aquilo é possível ser feito. É bom a gente tomar cuidado com isso, porque os desenvolvedores veem os arquitetos também como liderança técnica, então eles podem seguir o padrão da POC sem se preocupar com as boas práticas, fora que quantas vezes você não já viu uma POC entrar no código de produção? Aqui tem outra coisa que eu vou sugerir, esses códigos podem ser colocados como seu portifolio, mais um motivo para fazer um código bem feito.</p>
<p>Um outro ponto legal proposto é o arquiteto atuar em débito técnicos ou bugs. Eu acho essa abordagem muito interessante também porque é uma outra forma de avaliar os gaps ténicos da equipe e os gaps da própria arquitetura. Saber em que nível sua equipe de desenvolvimento está e como você pode ajudar eles a melhorar é um papel importante para o arquiteto. E você atuando nesses pontos, não afeta a equipe porque se não puder atuar não afeta o desenvolvimento, a revisão do código também ajuda a identificar esses gaps, não tem desenvolvimento, mas é sempre bom gastar um tempo analisando o código dos outros.</p>
<p>Por fim eu diria que a construção de bibliotecas, de uma sdk própria e ferramentas de automação ajudam a equipe a ganhar velocidade e ajuda a manter o conhecimento em desenvolvimento afiado.</p>
<hr />
<p>Agradeço por ter chegado até este ponto!</p>
<p>Fique à espera dos próximos capítulos, nos quais, além dos resumos, compartilharei minhas experiências com as provas de conceito que venha a desenvolver e outros aprendizados relacionados ao desenvolvimento.</p>
]]></content:encoded></item></channel></rss>