Criando templates para dotnet new

Desde o .Net core, temos disponível uma CLI extremamente poderosa, que nos facilita muito a vida durante o desenvolvimento e entrega das aplicações. Uma dessas facilidades está na possibilidade de criar templates que funcionam na linha de comando e também nas IDE’s. Hoje vamos aprender como criar templates para dotnet new e também para os assistentes de template do Visual Studio.

Como esse post rendeu, acabei criando um novo repositório para ele. Lá você encontrará as templates organizadas e seguindo um fluxo de desenvolvimento. Em breve estará aberto para colaboração!

ftathiago/dotnet-templates (github.com)

O que é um template?

Não pule esta seção! Eu sei que você já sabe o que eles são. O caso é que as vezes esquecemos que templates são apenas “modelos”, cuja finalidade é trazer pronto para nós aquele código que está presente em todas as aplicações: O chamado boilerplate. Padronização de logs, middlewares para armazenamento de header, manipulação de exceptions, configuração de acesso a dados, características de design da aplicação… Acho que você pegou a ideia.

Uma interpretação comum, e que particularmente considero um erro, é de que aspectos arquiteturais da aplicação podem ser considerados boilerplate. A natureza da arquitetura de uma aplicação é mutável, dependendo de aspectos particulares de cada caso. Por isso a palavra mais dita nesse meio é “depende”. O erro de uma má interpretação, neste caso, é de você incorrer em overengineering; construir tantas camadas de abstrações que, além de degradação de performance, aumentam complexidade desnecessariamente; ter mais trabalho para tirar “módulos” desnecessários do que se fizesse a aplicação do zero.

Se você ainda está em dúvidas do que colocar dentro do seu pacote de templates, pesquise no google imagens pela palavra scaffoding. E se não tiver ficado claro, pergunte a um mestre de obras sobre a importância e uso dos andaimes em uma construção. É exatamente essa que deve ser a função dos templates em relação ao seu resultado esperado.

Essa discussão, por si só, daria um post. Por isso quero resumir dizendo: não espere que o seu template seja algo além de um simples modelo.

Então para quê serve o template?

Partindo apenas da minha percepção pessoal, acredito que o tempo gasto escrevendo boilerplate equivale a algo em torno de 40% do tempo de desenvolvimento. Você precisa escrever, testar, analisar a performance… Seria um sonho você poder escrever apenas o código relativo ao negócio, sem se preocupar (no início) com todo o código não-funcional da aplicação (o boilerplate que falamos antes). As templates te deixam bem mais próximas desse sonho. Mas essa é apenas uma das vantagens.

Outra grande vantagem é: pense em um cenário em que pessoas desenvolvedoras estão mudando de tecnologia; ou ainda, um time composto majoritariamente de pessoas juniores. Talvez essas pessoas não saibam ainda quais são as boas práticas de rastreabilidade. Ou não conheçam muito bem o potencial do ferramental que o framework e a linguagem disponibilizam. Neste ponto, além do template oferecer um código pronto, seguro, dotado de boas práticas, também serve de fonte de conhecimento para quem está chegando. Isso, claro, se houver quem ensine e interesse da empresa para tal.

Como fazer templates para .Net Core?

Você pode visualizar o conteúdo de exemplo desse artigo, visitando meu GitHub

Em .Net é possível também criar template para códigos mais simples, como um snippet que cria um DBContext, por exemplo. Mas não é isso que vamos abordar hoje. Queremos algo mais complexo! E por isso vamos fazer um template de uma api completa! Acompanhe o passo-à-passo:

Passo 1: tenha um projeto com o boilerplate

Uma das coisas que eu acho mais maravilhosas no .Net, no que diz respeito a construir templates, é que elas compilam! Isso abre diversas portas, como por exemplo, testes de unidade! Você pode, portanto, configurar Github Actions que testem sua aplicação e façam o deploy no nuget.org ou em qualquer outro centralizador de pacotes da sua preferência. Imagine entregar uma template já com testes de unidade?

Por isso o primeiro passo é ter um projeto. Comece um projeto se preocupando principalmente com o boilerplate e com a performance dele até ali. Por isso, pensando uma webapi, crie um controller sample, que faça um processamento bem simples, e afira o seu tempo de resposta. Assim você consegue ter ideia do impacto que o seu boilerplate sozinho terá no código final. Por isso adicione apenas o necessário. E se for o caso, crie parâmetros que permitam selecionar quais componentes o seu template deve incluir ou não.

Passo 2: crie um diretório .template.config

O diretório .template.config deve ficar na raiz do projeto. O conteúdo dele é, basicamente, as definições do que realmente é o seu template e como ele vai se comportar nas diversas IDE disponíveis no mercado. Essa configuração está contida em arquivos JSON. Vamos entender primeiro o arquivo que configura o template para rodar na dotnet CLI: template.json

{
     "$schema": "http://json.schemastore.org/template",
     "author": "Francisco Thiago de Almeida",
     "description": "A 'toy-code' to demonstrate how to use templates for boilerplate delivery.",
     "classifications": [
         "Web",
         "ASP.NET"
     ],
     "name": "Blog do FT - WebAPI",
     "shortName": "bftWebApi",
     "defaultName": "WebApi",
     "identity": "BlogDoFt.Web.CSharp",
     "tags": {
         "language": "C#",
         "type": "project"
     },
     "sourceName": "WebApi",
     "preferNameDirectory": true,
     "symbols": {
         "Framework": {
             "type": "parameter",
             "description": "The target framework for the project.",
             "datatype": "choice",
             "choices": [
                 {
                     "choice": "net5.0",
                     "description": "Target net5.0"
                 }
             ],
             "replaces": "net5.0",
             "defaultValue": "net5.0"
         },
         "excludeSamples": {
             "type": "parameter",
             "datatype": "bool",
             "description": "Create sample code",
             "defaultValue": "false"
         }
     },
     "placeholderFilename": "-.-",
     "sources": [
         {
             "modifiers": [
                 {
                     "condition": "(excludeSamples)",
                     "exclude": [
                         "**/*Sample*.cs"
                     ]
                 }
             ]
         }
     ]
 } 

Algumas linhas são autoexplicativas, outras possuem code hint. Por isso não falarei de todas.

  • “schema”: é a linha que permite a adição de code hints e code completation ao seu ambiente de desenvolvimento. Apenas copie.
  • “classification”: tem relação ao que aparece quando você digita dotnet new -l na linha de comando.
  • “shortname”: é o nome que deve ser digitado para que o scaffolding aconteça com o comando dotnet new [shortname];
  • “symbols”: Define parâmetros para o comando dotnet new. É importante ressaltar – principalmente porque perdi um tempão com isso – que o parâmetro “Framework” é praticamente obrigatório. Caso você não inclua esse parâmetro, o Visual Studio não consegue identificar os demais parâmetros que você incluir;
    • “type”: Define o tipo de parâmetro. Você pode ter “computed”, que ele vai ser calculado, e “parameter” que é um valor de entrada do usuário;
  • “placeholderFilename”: o scaffolding não gera diretórios vazios. No código de exemplo, nós definimos um parâmetro que permite que o código de exemplo não seja copiado. Neste caso, a pasta “Controllers”, por exemplo, não seria criada já que ficaria vazia. Contudo, se eu adicionar um arquivo com o mesmo nome definido nesta propriedade, o diretório será criado mesmo estando vazio. Eu tentei algo mais elegante, como .gitkeep, mas arquivos que começam com “.” são ignorados;
  • “sources”:  é outra propriedade autoexplicável. Nela eu consigo controlar a cópia e geração de arquivos de acordo com algumas condições. No caso do exemplo, caso a variável excludeSamples esteja definida como true, todos os arquivos contendo Sample no seu nome, em todos os diretórios do modelo, serão excluídos da geração;

Caso você queira o seu template compatível com IDE, então você precisa adicionar o arquivo ide.host.json.

{
     "$schema": "http://json.schemastore.org/vs-2017.3.host",
     "icon": "icon.png",
     "symbolInfo": [
         {
             "id": "excludeSamples",
             "name": {
                 "text": "Exclude Samples"
             },
             "isVisible": "true",
             "defaultValue": "false"
         }
     ]
 } 

Como você pode perceber, é um arquivo bem mais simples que o primeiro. O seu destaque são apenas dois:

  • “icon”: Com essa propriedade eu posso indicar o caminho para um arquivo que contenha a imagem do ícone do template. É a mesma imagem que aparece na lista de templates disponíveis no VSCode;
  • “symbols”: Parecido com o que você já fez antes, essa propriedade também contém os parâmetros da aplicação. A única diferença é que aqui eles são apenas mapeados para a IDE.

Com apenas isso, já seria possível você digitar o comando dotnet new -i .\ na raiz do projeto e ele já estaria instalado localmente na sua máquina. Mas nós queremos que outras pessoas utilizem nosso projeto, não é mesmo? O que nos leva ao passo três:

Passo 3: Empacote o seu template

Antes de continuar, eu preciso agradecer duas pessoas que me ajudaram na empreitada de adquirir o conhecimento necessário para esse artigo – e para o meu uso pessoal também. Elas são: Tim Heuer (@timheuer) e Sayed Hashimi (@sayedihashimi). Tim falou para mim sobre o Sayedi e o Sayedi fez esse conteúdo maravilhoso (em inglês) para quem deseja criar seus primeiros templates. Muito “thank you” para vocês!

O Sayed escreveu um script que remove e re-instala os seus templates. Eu fiz uma ligeira modificação nele para que faça a limpeza de outras pastas também. Vale dizer que todos os arquivos que você deixar dentro do seu template, farão parte da cópia. Então é bom rodar um dotnet clean antes de empacotar, apagar algumas pastas e arquivos (como os codecoverage, por exemplo), para não gerar arquivos desnecessários. Para executar o script, basta digitar o comando no powershell:

.\InstallPackage.ps1

E você verá o seu template disponível na linha de comando:

Na imagem, entre os templates listados há o template “Blog do FT – WebAPI” como resultado da instalação do pacote.

Atenção! Caso você encontre algum erro reclamando a inexistência do comando nuget.exe, faça o download da aplicação e deixe-a disponível no path. Ah! não se esqueça de reiniciar o seu terminal.

Passo 4: Faça com que o Visual Studio veja todos os templates

Eu perdi algumas horas tentando descobrir por que o Visual Studio não mostrava o meu pacote de templates. Foi aí que o Sayed salvou minha vida: Eu preciso ligar uma opção no Visual Studio para ver todos os templates. Veja:

Uma vez na IDE do Visual Studio, vá até o menu Tools e escolha o submenu Options.

Na tela de Opões, abre o item “Environment” e selecione “Preview Features”. Verifique se a opção “Show all .Net Core templates in the New Project dialog (requires restart)” está marcada. Se não estiver, marque-a. E então reinicie o Visual Studio. Quando você for criar um novo projeto, vai ver seu template. Com ícone e tudo!

E sem esquecer dos parâmetros que você mapeou:

Passo 5: Publique no Nuget.Org

Uma vez que você tem o seu template (ou os vários templates) empacotados, você pode publicá-los no nuget.org – ou em qualquer outro repositório de bibliotecas. Mas esse artigo está ficando muito grande. E eu quero abordar os detalhes desse processo. Quem sabe a gente já não faz um github actions sobre? Que tal?

Um ponto de atenção apenas:

Talvez por uma falha de configuração minha, ainda não consegui fazer com que o Visual Studio respeite o arquivo de solução que eu deixei dentro do meu template. Ele simplesmente sobrescreve o conteúdo. Caso eu descubra o que está acontecendo, eu falo com vocês e altero esse post, ok? E caso você saiba… poxa, fala para mim 😊

Bom, pessoal. Eu espero que vocês se divirtam construindo seus templates e ganhem algum tempo do desenvolvimento do projeto com eles.

Um abraço e até a próxima!

Veja também:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.