Executando verificação de segurança...
9

[PARTE 1] O Poder desconhecido do .yaml 🚀

Post – Parte 1 de 2

Nota: Este post foi dividido em duas partes para facilitar a leitura. Continue na Parte 2 para ver o conteúdo completo.


O Poder desconhecido do .yaml 🚀

Índice


Salve tabnews, nesse artigo eu venho compartilhar um pouco de um projeto que estou desenvolvendo que envolve uma abordagem na programação talvez um pouco obscura e desconhecida mas que se usada de maneira eficiente pode fazer com que um sistema que você entregaria em 1 ano seja entregue em 6 meses.

Se ficou curioso com como raios um .yaml pode trazer tanta vantagem, se quer entender melhor oque o maluco aqui está falando, se quer me refutar, ou apontar algum erro no que estou fazendo por favor continue lendo o artigo e deixe um comentário para eu ler.


Oque é o .yaml?

Vamos começar pelo simples, oque seria um arquivo .yaml? arquivos .yaml ou .yml são arquivos de marcação que funcionam com base na identação do texto, ou seja, eles não funcionam com palavras reservadas como os arquivos de programação.

Normalmente esses arquivos são usados para gestão de dependências ou para automatizações, como no Flutter onde o pubspec.yaml controla dados como os pacotes que você importa do pub.dev e outras informações do projeto, ou como nas Github Actions onde você constroi pipelines CI/CD com a indentação do arquivo. Mas essas não são as únicas maneiras de se tirar proveito disso.


Porque comecei a seguir essa abordagem de desenvolvimento

Não posso ser mesquinho a ponto de dizer que fui eu que tive essa ideia de abordagem e acredito que isso consolida a eficiência dessa abordagem de desenvolvimento. Atualmente atuo como Desenvolvedor Fullstack Junior numa empresa do Sudoeste Paranaense chamada Imaxis.

E foi na Imaxis que eu descobri essa abordagem. Aqui na empresa temos diversas soluções para diferentes problemas, mas uma coisa em comum com todas seria o Ipanel. O Ipanel é uma invenção do meu chefe que vem sendo aprimorada desde 2011, desenvolvida em PHP puro com jQuery – inclusive, atualmente faço o sustain de um sistema que usa uma das primeiras versões do Ipanel e ele segue firme e forte (fora os problemas do legadão de sempre, klkkkk).

Atualmente, o Ipanel já está bem mais avançado do que era lá em 2011 e usamos uma versão com o CodeIgniter ainda com jQuery, o que trouxe uma organização para o projeto e fez com que a abordagem ainda seja eficiente. Basicamente, essa é a história e a maneira que eu descobri o poder do .yaml. Não irei entrar em muitos detalhes a nível de código do Ipanel, o foco é o meu CMS – mas posso responder qualquer pergunta.


Como isso funciona no Ipanel?

Chegando na parte mais legal e de fato o que preparei para esse artigo, o Ipanel funciona com base na estruturação das pastas e nomeação dos arquivos .yaml. Ou seja, depois de modelado o banco e com as entidades do Doctrine criadas, são feitos um arquivo .yaml para cada entidade com o mesmo nome do arquivo da Entidade – ou seja, para o usuário teríamos User.php (modelo Doctrine) e User.yaml (arquivo com as configurações da tela).

Através de diversos arquivos PHP, os .yaml são interpretados e processados de modo que o HTML, jQuery e CSS sejam construídos de maneira dinâmica, evitando a necessidade de criar um arquivo para cada entidade. Isso economiza muito tempo de desenvolvimento, principalmente para entidades simples onde só seria necessário um CRUD.

Destaque: Um caso atual que mostra a força do Ipanel foi uma das soluções da Imaxis, onde eu sozinho consegui subir o core de um sistema de vendas com carrinho, gestão de produtos, gestão das entregas e relatório de produtos em apenas 1 semana.


A minha versão do Ipanel o LaravelCMS

Depois de 1 ano trabalhando na empresa e sempre pensando: “Po, eu preciso fazer a minha versão do Ipanel”, decidi por jogar meu Laravel e desenvolver uma versão com as minhas decisões e com uma abordagem um pouco diferente – já que, mesmo parecidos, o CodeIgniter e o Laravel são bem diferentes. O que se manteve foi a organização em uma pasta core/ e a lógica de nomeação da entidade com o arquivo .yaml.

Como todo dev atarefado e universitário (5° Período na UTFPR-FB), não podia simplesmente gastar meu tempo refazendo o código sem ter de fato um retorno, nem que fosse de conhecimento. Então, decidi que iria usar a TALL Stack (TailwindCSS, AlpineJs, Livewire e Laravel). Mesmo que eu não conseguisse usar meu sistema num futuro freelance, pelo menos aprendi novas tecnologias.

Web nunca foi o meu forte e nunca foi o que me interessou, mas sempre gostei muito de PHP. O Ipanel facilitou muito minha vida nesse último ano, além de me lapidar como dev – afinal, o código é muito complexo, e tive que ler bastante para entender como funciona de fato. Nesse último mês, estive aprendendo e desenvolvendo essa solução. Algo interessante de se falar é que nunca toquei em nada da TALL Stack; meu dia a dia se resume ao pão com ovo do PHP, CodeIgniter, Bootstrap 4.5 e jQuery – o básico e que, de fato, funciona.


Mostre código doidão

Esse é o arquivo Person.yaml, para minha entidade de Person do Eloquent que vai armazenar as pessoas no sistema. A ideia é ter uma base sólida que exista em todo sistema – então estou me concentrando nisso atualmente: usuários, permissões, logs, o básico de todo sistema.

Person:
  startsOn: 'grid'
  identifier: 'pes_id'
  listingConfig:
    grid:
      pes_id:
        name: Id
        tagStyle: 'mb-4'
        labelStyle: 'font-semibold text-primary-400 text-xl pb-2'
        fieldStyle: 'text-xl'
        html: 'p'
      pes_name:
        name: Nome
        labelStyle: 'font-semibold text-primary-400 text-md pb-2'
        fieldStyle: 'truncate'
        html: 'p'
      pes_cpf:
        name: Cpf
        labelStyle: 'font-semibold text-primary-400 text-md pb-2'
        fieldStyle: 'truncate'
        html: 'p'
      pes_created_at:
        name: Criado Em
        labelStyle: 'font-semibold text-primary-400 text-md pb-2'
        fieldStyle: 'truncate'
        html: 'p'
        listingFunction: getDate
    table:
      pes_id:
        name: Id
        style: 'font-semibold'
      pes_name:
        name: Nome
        style: 'font-semibold'
      pes_cpf:
        name: Cpf
        style: 'font-semibold'
      pes_created_at:
        name: Criado Em
        style: 'font-semibold'
        listingFunction: getDate
  formConfig:
    view: "form.component"
    pes_name:
      type: string
      label: "Nome"
      edit: false
      placeholder: "Informe o nome"
      helper: "Nome do Usuário"
      sizing: "mb-4 w-full max-w-2lg"
      groupIn: "Dados Pessoais"
      identifier: "name"
      validationRules:
        - "required"
        - "min:3"
      line: 1
    pes_email:
      type: string
      label: "Email"
      edit: true
      placeholder: "Informe o Email"
      helper: "Email do Usuário"
      groupIn: "Dados Pessoais"
      sizing: "mb-4 w-full max-w-md"
      identifier: "email"
      validationRules:
        - "required"
      line: 2
    pes_phone:
      type: string
      label: "Telefone"
      edit: true
      placeholder: "Informe o telefone"
      helper: "Telefone do Usuário"
      sizing: "mb-4 w-full max-w-md"
      groupIn: "Dados Pessoais"
      identifier: "phone"
      mask: "(99) 99999-9999"
      line: 2
    pes_cpf:
      type: string
      label: "CPF"
      edit: false
      placeholder: "Informe o cpf"
      helper: "Documento do Usuário"
      sizing: "mb-4 w-full max-w-md"
      groupIn: "Dados Pessoais"
      identifier: "cpf"
      customValidation: ValidateCPF
      mask: "999.999.999-99"
      validationRules:
        - "required"
      line: 2
    city_cit_id:
      type: relation
      label: "Cidade"
      edit: true
      placeholder: "Selecione a Cidade"
      helper: "Cidade do Endereço"
      sizing: "mb-4 w-4/4"
      groupIn: "Endereço"
      identifier: "city"
      validationRules:
        - "required"
      values:
      line: 1
    pes_uf:
      type: select
      edit: true
      updateRemoteField:
        remoteIdentifier: city
        remoteEntity: City
        remoteAtrr: cit_uf
        key: cit_id
        value: cit_name
      values:
        AC: "Acre"
        AL: "Alagoas"
        AP: "Amapá"
        AM: "Amazonas"
        BA: "Bahia"
        CE: "Ceará"
        DF: "Distrito Federal"
        ES: "Espírito Santo"
        GO: "Goiás"
        MA: "Maranhão"
        MT: "Mato Grosso"
        MS: "Mato Grosso do Sul"
        MG: "Minas Gerais"
        PA: "Pará"
        PB: "Paraíba"
        PR: "Paraná"
        PE: "Pernambuco"
        PI: "Piauí"
        RJ: "Rio de Janeiro"
        RN: "Rio Grande do Norte"
        RS: "Rio Grande do Sul"
        RO: "Rondônia"
        RR: "Roraima"
        SC: "Santa Catarina"
        SP: "São Paulo"
        SE: "Sergipe"
        TO: "Tocantins"
      label: "Estado"
      placeholder: "Selecione o Estado"
      helper: "Estado do Endereço"
      sizing: "mb-4 w-1/6"
      groupIn: "Endereço"
      identifier: "uf"
      validationRules:
        - "required"
      line: 1
    pes_neighborhood:
      type: string
      label: "Bairro"
      edit: true
      placeholder: "Informe o bairro"
      helper: "Bairro do Endereço"
      sizing: "mb-4 w-2/4"
      groupIn: "Endereço"
      identifier: "neighborhood"
      validationRules:
        - "required"
      line: 2
    pes_street:
      type: string
      label: "Logradouro"
      edit: true
      placeholder: "Informe o Logradouro"
      helper: "Logradouro do Endereço"
      sizing: "mb-4 w-2/4"
      groupIn: "Endereço"
      identifier: "street"
      validationRules:
        - "required"
      line: 2
    pes_number:
      type: string
      label: "Número"
      edit: true
      placeholder: "Informe o Número"
      helper: "Número do Endereço"
      sizing: "mb-4 w-1/4"
      groupIn: "Endereço"
      identifier: "number"
      validationRules:
        - "required"
      line: 2
    pes_complement:
      type: string
      label: "Complemento"
      edit: true
      placeholder: "Informe o complemento"
      helper: "Complemento do Endereço"
      sizing: "mb-4 w-4/4"
      groupIn: "Endereço"
      identifier: "complement"
      line: 3
    pes_postal_code:
      type: string
      label: "CEP"
      edit: true
      placeholder: "Informe o cep"
      helper: "CEP do Endereço"
      sizing: "mb-4 w-1/6"
      groupIn: "Endereço"
      identifier: "postal_code"
      mask: "99999-999"
      validationRules:
        - "required"
      line: 3
  buttonsConfig:
    showDeleteButton: true
    showEditButton: true
    showDetailsButton: true
    showInsertButton: true
    showSearchButton: true
  getConfig:
    controller: ListingCtrl
    method: getAll
    params:
      model: Person

Detalhe importante

As chaves principais deste arquivo são:

  • StartsOn → modo de listagem da view
  • identifier → pk da minha entidade
  • listingConfig → configurações da listagem (colunas, labels, estilos)
  • formConfig → configurações dos formulários (view, tamanho, validações, etc.)
  • buttonsConfig → quais botões serão apresentados
  • getConfig → controlador e método para listagem customizada

Em um primeiro momento pode parecer confuso, mas está feito da maneira que faz sentido pra minha cabeça. Algo que levei muito em conta e que resolve o principal problema dessa abordagem é: Telas Complexas.
Caso você já tenha desenvolvido um sistema, sabe que essa abordagem não vai funcionar para alguma entidade que tenha muitas peculiaridades ou que o cliente traga algum requisito muito louco/específico que fuja do escopo disso. Por isso, em todos os momentos tento dar a possibilidade de se tratar essas coisas com um arquivo customizado – um exemplo seria no formConfig→view, onde eu posso passar uma view customizada para o form, podendo assim tratar qualquer peculiaridade.

Mas isso não faz com que o resto seja descartado também. Segue o exemplo da User.yaml, que renderiza uma view custom:

formConfig:
  view: "user-form"
  usr_email:
    type: string
    label: "Email"
    edit: true
    placeholder: "Informe o Email"
    helper: "Email do Usuário"
    groupIn: "Dados Usuário"
    sizing: "mb-4 w-1/2"
    identifier: "email"
    validationRules:
      - "required"
    line: 1
  usr_password:
    type: string
    label: "Senha"
    edit: true
    placeholder: "Informe a Senha"
    helper: "Senha do Usuário"
    groupIn: "Dados Usuário"
    sizing: "mb-4 w-1/2"
    identifier: "password"
    validationRules:
      - "required"
    line: 1
  confirm_password:
    type: string
    edit: false
    label: "Confirmar Senha"
    placeholder: "Confirme a senha"
    helper: "Confirme a senha"
    sizing: "mb-4 w-1/2"
    groupIn: "Dados Usuário"
    identifier: "confirm_password"
    validationRules:
      - "required"
    line: 1
  persons_pes_id:
    type: relation
    label: "Operador"
    edit: false
    placeholder: "Selecione o Operador"
    helper: "Pessoa vinculada a esse usuário"
    sizing: "mb-4 w-2/3"
    groupIn: "Dados Usuário"
    identifier: "persons"
    validationRules:
      - "required"
    fillOnStart:
      controller: GenericCtrl
      params:
        model: "Person"
      method: getAll
      pluck:
        - pes_name
        - pes_id
    line: 2
  profiles_prf_id:
    type: relation
    label: "Perfil de Acesso"
    edit: false
    placeholder: "Selecione o Perfil de Acesso"
    helper: "Perfil de Acesso desse usuário"
    sizing: "mb-4 w-1/3"
    groupIn: "Dados Usuário"
    identifier: "profile"
    validationRules:
      - "required"
    updateRemoteField:
      customRemote: getRepresentedAgents
    fillOnStart:
      controller: GenericCtrl
      params:
        model: "Profile"
      method: getAll
      pluck:
        - prf_name
        - prf_id
    line: 2

Mesmo tendo suas peculiaridades, eu ainda aproveito as coisas que podem ser "matadas" com .yaml na view personalizada, usando meu componente Blade que consome os dados processados do .yaml e constrói o HTML diretamente na view personalizada.

Carregando publicação patrocinada...
1

Caramba, isso é mais um caso de a tecnologia é somente meio não o fim, mesmo com abordagens totalmente desconexas com os hypes de desenvolvimento atual o importante é que funciona e que a empresa entrega, maaaas vamos rever esse lance de fullstack Jr kkkkk outros devs podem te considerar meme por isso pois mesmo um dev experiente que se denomina fullstack as vezes recebe piadinhas como "dev que promete tudo e entrega mais ou menos".

Outro ponto, por mais que as abordagens da sua empresa resolva os problemas dos clientes no dia a dia, como um amigo, aconselho se manter estudando ferramentas que são mais populares no mercado de tech, pois se um dia você precisar se recolocar, talvez essa experiência super específica não seja tão considerada.

2

Dei essa nomenclatura por ser oque eu faço, querendo ou não eu entrego nas minhas tarefas tanto no backend como também no frontend, então não tem porque eu omitir isso se é oque de fato eu faço.

E de fato não é legal ficar engessado na tecnologia de uma empresa, por isso mesmo estou aprendendo o laravel, mas qualquer tempo se aprofundando em qualquer coisa nunca é perdido, no mundo da tecnologia para sair da x e começar a y não é algo complexo, ainda mais com as LLMs agora que podem ajudar a nivel de sintaxe.

Afinal oque fica é a lógica de programação e é oque eu me forço a me aprimorar subindo esse projeto usando uma arquitetura que exige bastante pensamento antes de implementar as coisas.

E essa tecnologia não seria "antiga" não entendi oque você quis dizer com se aprimorar em techs populares mas minha visão o elefantinho por ser uma tecnologia robusta e bem alocada no mercado funciona melhor que muita framework js, nesse projeto inclusive estou usando várias técnologias populares no mercado tech, Tailwindcss, AlpineJS, Laravel e Livewire.

1

Calma jovem kkkk em nenhum momento invalidei nem reduzi nenhuma tool, so disse que tecnologia é meio não fim, tanto faz qual a tecnologia esta sendo usada o importante é que soluções estão sendo entregues. Sobre a nomenclatura do seu cargo, ok entendi o ponto e faz sentido então.