<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michel Versiani</title>
    <description>The latest articles on DEV Community by Michel Versiani (@forgivenbr).</description>
    <link>https://dev.to/forgivenbr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F556760%2F1c0bce93-6ffa-40b7-b37e-9c86cc0a7ada.jpeg</url>
      <title>DEV Community: Michel Versiani</title>
      <link>https://dev.to/forgivenbr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/forgivenbr"/>
    <language>en</language>
    <item>
      <title>Melhorando a rastreabilidade do sistema com um histórico de ações do usuário/sistema.</title>
      <dc:creator>Michel Versiani</dc:creator>
      <pubDate>Mon, 06 Feb 2023 17:15:37 +0000</pubDate>
      <link>https://dev.to/forgivenbr/melhorando-a-rastreabilidade-do-sistema-com-um-historico-de-acoes-do-usuariosistema-49kf</link>
      <guid>https://dev.to/forgivenbr/melhorando-a-rastreabilidade-do-sistema-com-um-historico-de-acoes-do-usuariosistema-49kf</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Fala pessoal como andam as coisas?, espero que estejam todos bem, por aqui estou recuperando de um burnout mas a cada dia melhor.&lt;/p&gt;

&lt;p&gt;Faz uns meses que eu queria criar esse artigo de hoje sobre um assunto que é muito importante no trabalho como pessoa desenvolvedora, e esse assunto é a criação de um histórico de ações do usuário e do sistema visando trazer mais rastreabilidade e segurança para nós e nossa aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendendo a importância de se ter uma boa rastreabilidade nas aplicações
&lt;/h2&gt;

&lt;p&gt;Imagine o seguinte cenário, você trabalha em uma empresa que possui um SaaS focado na venda de produtos, na aplicação tem uma feature de desconto onde o usuário pode escolher um determinado produto de uma categoria e adicionar um desconto sobre ele, então em um belo dia na Black Friday o gerente de uma loja cliente pede para o funcionário responsável por administrar o sistema para adicionar um desconto de 5% sobre um determinado produto, mas , por falta de atenção infelizmente, o funcionário adiciona um desconto de 50%.&lt;/p&gt;

&lt;p&gt;Algumas horas se passam e então ele percebe a falha que cometeu quando as vendas daquele produto começam a disparar de forma inacreditável, então quando ele vai checar os detalhes do produto no sistema acaba percebendo que adicionou a porcentagem de desconto errada, tenso não?&lt;/p&gt;

&lt;p&gt;Sendo assim, o funcionário com medo da bronca que iria levar do chefe resolveu colocar a culpa no sistema, e adivinha ?, o gerente acreditou e ligou para o suporte da sua empresa.&lt;/p&gt;

&lt;p&gt;Agora imagine só, como provaríamos que o erro foi do funcionário ao aplicar o desconto e não da aplicação que adicionou o desconto com valor errado?, precisaríamos de provas correto?, e é aí que entra a importância de se ter um bom sistema de histórico (&lt;strong&gt;por isso eu disse no inicio que é para nossa segurança também , porque tendo um sistema de histórico conseguimos comprovar quem foi o autor da falha&lt;/strong&gt;).&lt;br&gt;
Sei que para muitos esse assunto soa como sendo bem básico, e realmente é, mas acredite, muitos sistemas hoje em dia não contemplam essa feature.&lt;/p&gt;

&lt;p&gt;Nesse artigo irei utilizar de exemplo a linguagem &lt;strong&gt;PHP&lt;/strong&gt; juntamente com o &lt;strong&gt;Laravel&lt;/strong&gt;, e mais especificamente, um projeto que estou desenvolvendo. Mas por se tratar de orientação a objetos conseguimos replicar em outras linguagens que suportem o paradigma.&lt;br&gt;
Sem mais delongas bora botar a mão na massa!&lt;/p&gt;
&lt;h2&gt;
  
  
  Primeiramente, onde salvar o histórico?
&lt;/h2&gt;

&lt;p&gt;Bom , nesse exemplo irei utilizar um banco de dados relacional, mas nada nos impede de salvar essas informações em um banco não relacional, ou até mesmo em um arquivo de log, o importante é de alguma forma conseguir salvar todas as ações necessárias para se ter uma boa rastreabilidade. Sendo assim fique a vontade para escolher onde irá salvar essas informações.&lt;/p&gt;
&lt;h2&gt;
  
  
  Quais ações mapear no histórico?
&lt;/h2&gt;

&lt;p&gt;Vamos então começar partindo da seguinte pergunta, quais ações serão salvas em nosso histórico?&lt;/p&gt;

&lt;p&gt;Pois bem, o mais importante de fato é salvar ações que são criticas no sistema e que também sejam passíveis de erros que possam acarretar em grandes problemas.&lt;/p&gt;

&lt;p&gt;Seguindo esse nosso exemplo poderia ser a ação de adicionar um desconto, mas poderíamos ter outras ações, como por exemplo a ação de se realizar uma venda, alterar algum dado do produto, deletar um produto, e assim por diante.&lt;/p&gt;

&lt;p&gt;Mas também nada impede de salvar todas as ações do usuário/sistema, eu particularmente prefiro salvar as ações criticas e que podem trazer alguma consequência (&lt;strong&gt;mas pense bem, pois a tabela de históricos ficaria lotada bem rapidinho, dificultando o “debug”&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;É bom lembrar que quando eu digo usuário estou me referindo também ao sistema (&lt;strong&gt;talvez atores soaria melhor que usuários? lembrou daquela aula chatinha de UML da facul nééé?&lt;/strong&gt;).&lt;/p&gt;
&lt;h2&gt;
  
  
  Montando nosso sistema de histórico
&lt;/h2&gt;

&lt;p&gt;Bom , agora que enxergamos a importância de se ter um sistema de histórico, e quais ações devemos mapear, está na hora de botar a mão na massa e criar o nosso módulo de histórico.&lt;/p&gt;
&lt;h3&gt;
  
  
  Entidade/Modelo
&lt;/h3&gt;

&lt;p&gt;Para a nossa entidade/modelo é importante termos o tipo da entidade (enum), que nesse caso seria o módulo em que foi registrado aquele histórico (product, sales, financial), uma coluna para salvar quem foi o responsável por aquela mudança (eu gosto de chamar de changed_by), e a ação que foi feita (enum). Também é indispensável ter colunas timestamp como created_at e updated_at para fins de rastreabilidade.&lt;/p&gt;
&lt;h3&gt;
  
  
  Estrutura do modelo
&lt;/h3&gt;

&lt;p&gt;Seguindo a entidade da aplicação que irei utilizar como exemplo, temos as seguintes colunas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpx16tnjxz7tx2228fv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpx16tnjxz7tx2228fv2.png" alt="A table showing the entities columns" width="608" height="605"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Podemos utilizar as informações acima para criar uma migration para a tabela histories.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Ações que serão rastreadas
&lt;/h3&gt;

&lt;p&gt;Dentro de nosso modelo teremos as seguintes ações que serão rastreadas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpm1a4s32mfaam1x7l95s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpm1a4s32mfaam1x7l95s.png" alt="A table containing information about the actions that are going to be mapped" width="667" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nessa aplicação que estou utilizando como exemplo até então as ações que criam histórico são relacionadas a produtos, pois são as mais críticas do sistema, por isso só tenho elas dentro do model de History. Mas no futuro irei adicionar ações relacionadas a autenticação, edição de permissões e pagamentos.&lt;/p&gt;

&lt;p&gt;No PHP, antes da versão 8 onde foi introduzido o tipo &lt;strong&gt;enum&lt;/strong&gt; nós costumávamos utilizar constantes como uma forma de representar um &lt;strong&gt;enum&lt;/strong&gt;, mas na versão 8 &amp;gt; já é possível criar os enums como criamos em C#, por exemplo. &lt;br&gt;
Então &lt;strong&gt;se você estiver utilizando o PHP 8 &amp;gt;, pode substituir as constantes por enums&lt;/strong&gt; 🙂 , não substituí ainda por falta de tempo (ou vergonha na cara, ainda não me decidi rs…).&lt;/p&gt;

&lt;p&gt;Segue o código da Model:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi44yk2f8ljnp2j3rt3o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi44yk2f8ljnp2j3rt3o.png" alt="The History model code" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
Acima podemos observar também os enums mapeando as entidades que utilizarão o histórico (Dê uma olhada nas constantes terminadas em &lt;strong&gt;ENTITY&lt;/strong&gt;), para podermos saber qual módulo é dono de qual registro.&lt;/p&gt;
&lt;h3&gt;
  
  
  Relacionamentos e função para criação do histórico
&lt;/h3&gt;

&lt;p&gt;Por fim, temos os relacionamentos de &lt;strong&gt;History&lt;/strong&gt; com &lt;strong&gt;User&lt;/strong&gt; e &lt;strong&gt;Company&lt;/strong&gt;, também temos uma função para criação do histórico chamada &lt;strong&gt;createChange&lt;/strong&gt;, onde recebemos como parâmetro o id da ação ocorrida, e um array com os dados para serem salvos na tabela.&lt;br&gt;
Mais para frente utilizaremos essa função para criar os registros de histórico através da nossa classe de Serviços.&lt;/p&gt;

&lt;p&gt;Código da entidade History:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Scopes\FilterTenant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Traits\UsesLoggedEntityId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Factories\HasFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Relations\BelongsTo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Collection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * @property int $entity_id
 * @property string $entity_type
 * @property string $metadata
 * @property int $changed_by_id
 * @property int $company_id
 * @property int $action_id
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;History&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* Para esse exemplo , ignore essas 2 traits.*/&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;UsesLoggedEntityId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;PRODUCT_ENTITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'product'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;BRAND_ENTITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'brand'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;CATEGORY_ENTITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'category'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;PRODUCT_CREATED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;PRODUCT_UPDATED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;PRODUCT_DELETED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;PRODUCT_SOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;ADDED_QUANTITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'entity_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'entity_type'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'company_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'action_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'metadata'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'changed_by_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="cm"&gt;/* Um scope global que eu utilizo para sempre filtrar as queries por tenantId, pode ignorar tb... */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;booted&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;addGlobalScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FilterTenant&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Company&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$actionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;entity_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'entityId'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;entity_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'entityType'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'metadata'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;changed_by_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'changedById'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;company_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getLoggedCompanyId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;action_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$actionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora que temos nosso Modelo criado está na hora de criarmos a classe de serviço para podermos utilizá-la nos módulos que salvarão no histórico.&lt;/p&gt;

&lt;h2&gt;
  
  
  Classe de serviço e sua utilização nos módulos
&lt;/h2&gt;

&lt;p&gt;Para quem nunca ouviu falar de classes de serviços vou explicar bem brevemente, trata de uma classe que &lt;strong&gt;é utilizada para fazer a separação da regra de negócio na nossa aplicação&lt;/strong&gt;, assim ao invés de termos as regras de negócio em um controller por exemplo, delegamos essa função para a classe de serviços, e o controlador fica com o papel de ser um orquestrador, lidando somente com requisições HTTP e delegando processamento relacionado a regras de negócios ao serviço. Caso tenha interesse fiz um &lt;a href="https://dev.to/forgivenbr/classe-de-servicos-centralizada-vs-separada-por-acoes-4go6"&gt;artigo explicando um pouco sobre&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando o HistoryService
&lt;/h3&gt;

&lt;p&gt;Muito bem, para começarmos podemos criar uma pasta chamada &lt;strong&gt;Services&lt;/strong&gt; dentro de App, e logo após criamos uma pasta para a entidade History.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlprt4wkzw7vmeyot0lb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlprt4wkzw7vmeyot0lb.png" alt="Image showing the code structure for the services folder" width="542" height="532"&gt;&lt;/a&gt;&lt;br&gt;
Dentro da pasta &lt;strong&gt;Services/History&lt;/strong&gt; criamos uma classe PHP chamada &lt;strong&gt;HistoryService&lt;/strong&gt;, e então criamos uma função chamada &lt;strong&gt;createHistory&lt;/strong&gt;, recebendo como parâmetro o id da ação e um array de parâmetros.&lt;/p&gt;

&lt;p&gt;Dentro da função &lt;strong&gt;createHistory&lt;/strong&gt; faremos o seguinte:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Criaremos uma nova instância da model History.&lt;/li&gt;
&lt;li&gt;Na instância criada chamaremos a função &lt;strong&gt;createChange&lt;/strong&gt; passando como parâmetro o actionId e o array de parâmetros.&lt;/li&gt;
&lt;li&gt;Chamaremos o método &lt;strong&gt;save&lt;/strong&gt; do History para persistir no banco.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O código ficará assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Services\History&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\History&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HistoryService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$actionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;History&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$history&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$actionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$history&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Utilizando o HistoryService nos módulos da nossa aplicação
&lt;/h3&gt;

&lt;p&gt;Enfim chegamos a parte final desse processo, utilizar a classe de serviço dentro dos módulos da nossa aplicação, criando assim os registros no nosso histórico. Nesse exemplo utilizarei a classe de serviço responsável por importar produtos de uma planilha para o nosso sistema. &lt;/p&gt;

&lt;p&gt;Deixei somente as funções que mostrarão o uso do History , e o resto só comentei oque cada uma faz para vocês não ficarem voando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
   * @throws FailedToImportProducts|AttachmentInvalid
 */&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;importProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UploadedFile&lt;/span&gt; &lt;span class="nv"&gt;$importedFile&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setImportedProductsFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$importedFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validateFile&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;beginTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createProductsBasedOnImport&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; 
        &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CustomException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;rollBack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Throwable&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;rollBack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FailedToImportProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Nessa função fazemos o seguinte:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Seto o arquivo importado em uma propriedade, para ser utilizado em outras funções de processamento.&lt;/li&gt;
&lt;li&gt;Valido se o tipo do arquivo enviado é uma planilha CSV ou XLSX, e também o tamanho do arquivo.&lt;/li&gt;
&lt;li&gt;Chamamos a função &lt;strong&gt;createProductsBasedOnImport&lt;/strong&gt; para começar o processo de criar os produtos da planilha importada.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createProductsBasedOnImport&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;convertSpreadsheetToCollection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$importedProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$products&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ImportedProduct&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fromArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;storeImportedProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$importedProducts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Agora, na função de criar os produtos fazemos o seguinte:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Criamos uma Collection apartir da planilha, facilitando as operações que serão feitas mais pra frente.&lt;/li&gt;
&lt;li&gt;Pegamos cada produto da Collection e criamos um objeto usando Factory, para facilitar mais uma vez as operações a seguir.&lt;/li&gt;
&lt;li&gt;Chamamos a função &lt;strong&gt;storeImportedProducts&lt;/strong&gt;, que é responsável por salvar no banco de dados os produtos importados passando como parâmetro uma coleção dos objetos que criamos com a Factory.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Então finalmente chegamos na cereja do bolo, onde faremos o seguinte:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;storeImportedProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Collection&lt;/span&gt; &lt;span class="nv"&gt;$products&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$products&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ImportedProduct&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$createdProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fromRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toCollection&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createImportedProductHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$createdProduct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Para cada produto da coleção, criamos um produto na nossa tabela Products.&lt;/li&gt;
&lt;li&gt;Para cada produto salvo na tabela products, chamamos a função que criará o histórico de produto importado.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createImportedProductHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$historyService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HistoryService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'entityId'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s1"&gt;'entityType'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;History&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PRODUCT_ENTITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'changedById'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getChangedBy&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s1"&gt;'metadata'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createHistoryMetaData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$historyService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;History&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PRODUCT_UPDATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Criamos uma instância do &lt;strong&gt;HistoryService&lt;/strong&gt; para ser utilizado.&lt;/li&gt;
&lt;li&gt;Criamos um Array com os parâmetros necessários no histórico, e no metadata chamamos uma função que retornara todos os dados que precisamos do produto para ser utilizado futuramente se necessário.&lt;/li&gt;
&lt;li&gt;Chamamos a função &lt;strong&gt;createHistory&lt;/strong&gt;, passando como parâmetro a ação e o array de parâmetros.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createHistoryMetaData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'entityId'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'productName'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'initialQuantity'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'categoryId'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'brandId'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;brand_id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'minimumQuantity'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;minimum_quantity&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E dessa forma acabamos de utilizar o nosso sistema de histórico 🙂.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto3550zen9oer1hz513p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto3550zen9oer1hz513p.png" alt="Image showing the history registration on the database." width="800" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bom, espero que de alguma forma esse artigo tenha sido útil para você, se gostou compartilhe com aquele amigo que ainda não sabe da importância de se ter um histórico, e críticas construtivas são bem vindas!&lt;/p&gt;

&lt;p&gt;Tem um jeito melhor de resolver tal problema?, comenta aí embaixo, vamos compartilhar conhecimento! 😀, é isso aí pessoal uma ótima semana a todos, e até a proxima 👋🏿.&lt;/p&gt;

</description>
      <category>announcement</category>
      <category>devto</category>
      <category>opensource</category>
      <category>royalties</category>
    </item>
    <item>
      <title>Classe de serviços Centralizada VS Separada por Ações</title>
      <dc:creator>Michel Versiani</dc:creator>
      <pubDate>Sat, 07 May 2022 22:06:34 +0000</pubDate>
      <link>https://dev.to/forgivenbr/classe-de-servicos-centralizada-vs-separada-por-acoes-4go6</link>
      <guid>https://dev.to/forgivenbr/classe-de-servicos-centralizada-vs-separada-por-acoes-4go6</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Fala pessoal , espero que esteja tudo bem com vocês. Estou fazendo esse post para tentar mostrar um pouco sobre duas abordagens quando utilizando Serviços como uma forma de separar a lógica de negócios do controller , assim , deixando o controller somente com a responsabilidade de orquestrar todo fluxo , como deveria ser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Porque eu decidi abordar tal assunto?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/2xwWl4iiaR0UKIiiRQ/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/2xwWl4iiaR0UKIiiRQ/giphy.gif" width="300" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Você pode estar se perguntando , porque diabos você vai abordar esse assunto? , e bom , tudo surgiu quando eu aprendi sobre as classes de Serviço, e como elas nos ajudam a separar a responsabilidade de aplicar uma lógica de negócios fora do nosso Controller.&lt;/p&gt;

&lt;p&gt;Da forma que aprendi , uma classe de serviço tinha centralizado todos os métodos e funções que condiziam com aquela entidade. &lt;/p&gt;

&lt;p&gt;Ex : o método &lt;strong&gt;&lt;em&gt;searchAvailableHotels&lt;/em&gt;&lt;/strong&gt; estar na mesma classe de Serviço que o método &lt;strong&gt;&lt;em&gt;getHotelRoomDetails&lt;/em&gt;&lt;/strong&gt; , pois ambos fazem parte da regra de negócios da entidade &lt;strong&gt;Hotel , que utiliza um &lt;em&gt;HotelService&lt;/em&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Porém , a medida em que a aplicação for crescendo , mais e mais métodos serão criados dentro desse serviço. E assim tornando difícil se localizar no workflow, no meio de tantas funções.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Yq2SKEsscV85lPRJdu/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Yq2SKEsscV85lPRJdu/giphy.gif" width="400" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sendo assim , lembrei que em algumas linguagens como TypeScript, os devs costumam criar várias classes de serviços, separando seus módulos por pastas , e sendo &lt;strong&gt;cada classe de serviço de um módulo específico performando sua principal ação&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Também me lembrei que no Framework Laravel existe algo semelhante ,uma abordagem  chamada &lt;strong&gt;Actions&lt;/strong&gt;, que se assemelha a uma classe Job , onde a classe tem um construtor para inicializar as dependências  necessárias , e um método &lt;strong&gt;execute&lt;/strong&gt;, que executa a ação imposta sobre aquela action.&lt;/p&gt;

&lt;p&gt;Para saber mais sobre actions acesse &lt;a href="https://freek.dev/1371-refactoring-to-actions"&gt;esse post&lt;/a&gt; do Freek , demonstrando como se utiliza as tão famosas actions.&lt;/p&gt;

&lt;p&gt;Sendo assim, decidi abordar esse assunto, por conta dessa “dor” que tive com as classes de Serviço. Então sem mais delongas irei falar um pouco sobre cada abordagem , e quando utiliza-las (na minha visão :p).&lt;/p&gt;

&lt;h2&gt;
  
  
  Centralizada
&lt;/h2&gt;

&lt;p&gt;Uma classe de serviço centralizada geralmente é criada com uma separação por entidades. Então se tenho as entidades Hotel e Guest na minha aplicação , eu deveria ter uma classe &lt;strong&gt;HotelService&lt;/strong&gt; , e &lt;strong&gt;GuestService&lt;/strong&gt; para lidar com a aplicação das regras de negócios.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prós
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Todas as funções relacionadas as regras de negócio de uma determinada entidade ficam centralizadas em um local específico , tornando assim mais fácil de debugar o código , e de seguir um workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Contras
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ao mesmo tempo , as funções estando centralizadas em uma só classe, dependendo da aplicação pode se tornar maçante , por conter inúmeras linhas de código, tornando assim fácil de se perder , e correndo um maior risco de ter efeitos colaterais ( um exemplo prático seria a mudança em uma variavel declarada no escopo da classe , e que é modificada por uma ou duas funções em um determinado workflow, podendo gerar erros).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Separadas por ações
&lt;/h2&gt;

&lt;p&gt;Uma classe de Serviços separada por ações , geralmente é separada por módulos assim como na centralizada , porém , a grande diferença é que agora , para cada fluxo ou ação , criamos uma classe de serviço especifico.&lt;/p&gt;

&lt;p&gt;Então agora , se antes tinhamos uma classe &lt;strong&gt;HotelService&lt;/strong&gt; , contendo várias funções relacionadas a entidade Hotel , utilizando essa abordagem teremos &lt;strong&gt;uma classe de serviço para cada ação da entidade Hotel&lt;/strong&gt;. &lt;br&gt;
Um exemplo seriam os serviços &lt;strong&gt;SearchAvailableHotels&lt;/strong&gt; e &lt;strong&gt;GetHotelRoomDetails&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prós
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Maior abstração/separação de responsabilidades.&lt;/li&gt;
&lt;li&gt;Classes menores e com maior facilidade de se localizar.&lt;/li&gt;
&lt;li&gt;Uncle Bob ficaria feliz por saber que estaríamos seguindo a letra S do Solid , o princípio da responsabilidade única.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Contras
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Dependendo do sistema e da complexidade teríamos muitas classes , tornando  não o código , mas a estruturação um pouco mais complexa , tendo em vista que teremos várias classes de serviço em um módulo. Mas mesmo assim , em sistemas complexos eu ainda prefiro essa abordagem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Não vale a pena utilizar em sistemas menores que não possuem tantas funções relacionadas a regra de negócio da entidade.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusão
&lt;/h1&gt;

&lt;p&gt;Então quando devemos utilizar uma abordagem ou outra? , e a resposta é bem comum na nossa área... &lt;strong&gt;DEPENDE&lt;/strong&gt;!.&lt;br&gt;
Se na sua visão , a sua aplicação não irá precisar de implementar mais métodos e verificações no futuro , e que não há a chance de se tornar um projeto complexo , utilize a abordagem de Serviço centralizado. Senão , se você sabe que a aplicação irá crescer junto com o número de features requisitadas por seus clientes , se tornando algo complexo , recomendo utilizar a abordagem de serviços separados por ações/fluxos.&lt;/p&gt;

&lt;p&gt;Espero que tenham gostado desse post , e principalmente espero que ele seja útil para alguém. Falei algo errado? , pensa de um modo diferente ou conhece uma abordagem melhor? , comenta aí :) , Um grande abrç a todos , e até a proxima!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Hw5QkgfUPxgjWaSnWA/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Hw5QkgfUPxgjWaSnWA/giphy.gif" width="480" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>patterns</category>
      <category>poo</category>
      <category>structuralpattern</category>
    </item>
  </channel>
</rss>
