VAGAS DE EMPREGO

balta.io balta.io
  • Cursos
  • Carreiras
  • Para sua Empresa
  • Livros
    • Background Services
    • Blazor com .NET 8
    • Segurança em APIs
    • Futuro do C# 12
    • Nullable Types
    • Clean Code
  • Blog

Seja Premium
balta.io

  • Cursos
  • Carreiras
  • Para sua Empresa
  • Agenda
  • Livros
    • Background Services
    • Blazor com .NET 8
    • Segurança em APIs
    • Futuro do C# 12
    • Nullable Types
    • Clean Code
  • Blog
  • Player
Seja Premium

Entre ou Cadastre-se

  • Home
  • Artigos
  • GitHub Packages e GitHub Actions - Distribuindo contextos delimitados


👉 Temos uma versão mais atualizada deste artigo no nosso novo Blog

GitHub Packages e GitHub Actions - Distribuindo contextos delimitados

Neste artigo vou abordar a estretégia que utilizei para distribuir algumas capacidades de negócio do balta em nosso projeto novo, bem como as motivações por trás de tudo.

Contextos delimitados

O primeiro entendimento que fizemos foi sobre quais contextos delimitados teríamos no novo negócio.

Basicamente analisamos as áreas que temos e separamos em capacidades de negócios, pequenas e mais simples.

Como resultado obtivemos os seguintes contextos:

  • Conteúdo
  • Alunos
  • Assinaturas

Estes são os primeiros recortes, depois teremos mais, a medida que as coisas forem expandindo, mas por enquanto, quanto mais simples melhor.

Regras de negócio e testes de unidade

O ponto principal de todo sistema são as regras de negócio e consequentemente os testes de unidade.

Se você organiza tudo, mantém a regra na aplicação ao invés do banco e não executa testes de unidade (Automatizados), está jogando 50% dos benefícios no lixo.

Desta forma, a premissa era manter os processos centralizados. Hoje temos integrações com gateways de pagamento, site e futuramente App.

Todos estes devem seguir o mesmo fluxo, o mesmo processo, as mesmas regras.

Logo concentramos todas as interfaces (Contratos) e fluxos no Core da aplicação, separados nos contextos que mencionei acima.

Se você já fez os cursos Modelando Domínios Ricos e Refatorando para Testes de unidade sabe quanto eu valorizo utilizar a OOP na modelagem.

Inclusive eu brinco que o Coretem que funcionar só com os objetos do contexto, sem depender de API, dados nem nada.

Com eles funcionando, é só persistir seu estado em um banco e expor via API. Gosto de pensar assim.

Múltiplas arquiteturas

Outro ponto importante é os contextos nem sempre seguem o mesmo padrão, eles agem independentes.

Por exemplo, o contexto de conteúdos é extremamente voltado para leitura, logo não faz sentido ter uma estrutura complexa e voltada para validação de dados nele. Neste caso, temos apenas repositórios, expondo informações.

Já o contexto de assinaturas precisa de uma validação muito mais consistente e firme, envolve pagamentos, dados sensíveis e muitos testes de unidade.

Então, One size doesn't fits all, ou no belo português, arquitetura não tem bala de prata.

Distribuição

Mas por que separar um domínio? Por que segmentar em diversos sub domínios, em contextos delimitados?

Quanto menor, melhor

Um componente pequeno é fácil de mudar ou até mesmo de se jogar fora, pode ser gerenciado por uma pessoa apenas, por exemplo.

Fácil entendimento

Componentes menores também são mais fáceis de se entender. Mesmo dentro do meu negócio, tem coisas de marketing por exemplo que me tomam tempo.

Como funciona o processo de sinalização de uma nova assinatura para ferramenta de marketing? Como funciona o processo que coloca um aluno em um funíl de vendas?

Estes processos podem ser simples se forem tratados individualmente, mas podem se tornar complexos se conviverem muito juntos.

A ideia de um componente é conseguir realizar este processo com o mínimo de esforço e informação possível, de forma que vários outros componentes e sistemas possam fazer uso do mesmo.

Fácil manutenção

Como disse Robert C. Martin (Uncle Bob), criar um software para rodar uma vez é fácil, criar um software para ser mantido por um bom tempo é difícil.

Na teoria é tudo lindo, mas na prática a teoria é outra, você sabe! Aqui não é diferente, tem horas que as coisas precisam acontecer para ontem.

Embora eu odeie esta situação e lute para que isto não aconteça, não podemos ser hipócritas e dizer que estamos livres disso.

Então, gambiarras vão acontecer, porém elas devem ser raras e se você tem uma divisão boa das coisas, pode refazer um componente com facilidade.

Por exemplo, na integração com um gateway de pagamentos, utilizamos a SDK que eles fornecem em .NET (uma péssima decisão), mas vimos que ela não atende em diversos pontos, e ainda depende de versões anteriores do .NET.

Porém como este componente é pequeno, já temos um issue aberto para refazê-lo, usando RestSharp e chamando a API do gateway diretamente.

Juntando isto com uma boa modelagem e testes de unidade, conseguimos uma boa manutenção nos componentes.

Deploy

Outra vantagem de componentes independentes é a publicação deles. Se as integrações (Jobs), o site, a API, o App, dentre outros consomem estes componentes, uma simples modificação neles pode gerar um novo deploy de tudo.

O que fazemos é separar estes contextos em projetos (repositórios) separados e distribuí-los como pacotes NuGet.

Neste cenário, trabalhamos com versionamento semântico para não quebrar nada que já está rodando e conseguimos distribuir versões novas de formas independentes.

Por exemplo, houveram funcionalidades que precisavam ser implementadas no Core mas no momento só deveriam afetar as integrações, o site deveria ser mantido como está.

Em uma referência direta aos projetos em uma solução, isto não aconteceria de forma amena.

Microsserviços

"Mas isto que você está falando são microsserviços?" Ok, tem algumas coisas parecidas, mas não são!

A ideia dos microsserviços também é serem pequenos e independentes, mas eles implicam em uma série de outras coisas que não temos hoje como IAM e múltiplos bancos de dados.

Inclusive não temos a mínima necessidade de ter algo neste nível hoje, as justificativas são inválidas.

Quanto custa? só a nível de conhecimento, hoje todo ecossistema do balta roda no Azure e gastamos menos de $ 150.00/mês.

Em relação aos custos, ter cada contexto em uma API independente com seu próprio banco de dados, um servidor de autenticação (IAM) e um API gateway nos custaria pelo menos o dobro deste valor.

GitHub Packages

A chave para distribuir os pacotes é o GitHub, na verdade estamos utilizando o GitHub para gerenciar tudo relacionado a código, inclusive os projetos, mas isto é pauta para outro assunto.

Os GitHub Packages funcionam como um NuGet privado, que só os repositórios da sua empresa tem acesso.

No nosso caso, temos uma organização dentro do GitHub e dentro dela temos os repositórios.

Estes contextos, nada mais são do que ClassLibraries que geramos o nupkg e publicamos no GitHub Packages utilizando GitHub Actions.

Iniciando um projeto

Para criar um pacote, basta gerar um novo Class Library com qualquer nome que faça sentido para seu negócio.

dotnet new classlib -o MeuContexto

Não vou me atentar a código aqui, então abra o projeto, modifique como desejar e certifique-se que o mesmo está buildando!

Gerando um pacote

Sempre que compilamos um Class Library o resultado é uma DLL, mas queremos gerar um pacote, então o primeiro passo é colocar algumas informações no arquivo .csproj do seu projeto.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>

    <PackageId>MeuContexto</PackageId>
    <Version>1.0.0</Version>
    <Authors>André Baltieri</Authors>
    <Company>balta.io</Company>
    <PackageDescription>Meu primeiro pacote</PackageDescription>
    <RepositoryUrl>https://github.com/balta-io/MeuContexto</RepositoryUrl>
  </PropertyGroup>
</Project>

Estas informações serão inclusas no seu pacote e ficarão disponíveis quando alguém for baixá-lo.

Feito isto, podemos compilar e empacotar nosso projeto, utilizando os comandos abaixo.

dotnet build --configuration Release
dotnet pack --configuration Release

O comando pack vai gerar um arquivo .nupkg como resultado, nas pasta bin/release do projeto.

Publicando o pacote no NuGet

Com o pacote .nupkg gerado, caso queira publicá-lo publicamente no NuGet e deixá-lo visível para todos que usam Visual Studio por exemplo, basta acessar NuGet.org se autenticar e fazer o upload do pacote.

Ou caso queira fazer isto via linha de comando, basta usar o seguinte.

dotnet nuget push "bin/Release/*.nupkg" -s nuget --skip-duplicate

O -s nuget significa que estamos enviando para o NuGet.org e o --skip-duplicate não causa erro caso o pacote (versão atual) já exista.

A versão do pacote está descrita no .csproj que editamos anteriormente.

Publicando no GitHub Packages

Quando criamos uma organização no GitHub, temos a possibilidade de usar pacotes privados (NPM, NuGet, Maven, etc...) e eles ficam disponíveis na página inicial da sua organização.

Toda vez que executamos um dotnet add package <PACOTE> ele busca este pacote de fontes que temos definidas em um arquivo global do NuGet na máquina ou em um arquivo NuGet.config na raiz da aplicação.

No caso, o arquivo NuGet.config na raiz sempre sobrescreve o arquivo NuGet.config global da sua máquina.

O arquivo NuGet.config global está localizado em %appdata%\NuGet\NuGet.Config no Windows e ~/.config/NuGet/NuGet.Config no Mac/Linux.

Enão tudo que o NuGet.org faz é armazenar os .nupkg e prover um JSON com todos os pacotes registrados.

No caso dos GitHub Packages a URL dos seus pacotes será esta.

https://nuget.pkg.github.com/<ORGANIZATION>/index.json

Dadas estas informações, se usamos o -s nuget para enviar um pacote para o NuGet público, só precisamos mudar o source para termos a publicação nos GitHub Packages.

O único detalhe é que os GitHub Packages requerem autenticação, afinal, só usuários da sua empresa podem publicar um pacote.

Então, caso você queira publicar seu pacote diretamente da sua máquina, vai precisar de um PAT (Public Access Token), que pode ser gerado no seu perfil no GitHub na sessão Developer Settings.

Desta forma a publicação nos GitHub Packages fica da seguinte forma.

dotnet nuget push "bin/Release/*.nupkg" -k <TOKEN> -s https://nuget.pkg.github.com/<ORGANIZATION>/index.json --skip-duplicate

Feito isto, você já deve ver seu pacote na URL https://github.com/orgs/<ORGANIZATION>/packages.

GitHub Packages e GitHub Actions

Bom, não chegamos até aqui simplesmente para ficar publicando nossos pacotes manualmente, não é? Queremos deixar tudo automático e para isto temos os GitHub Actions.

Sabemos que precisamos executar um dotnet build, dotnet pack e dotnet nuget push para ter nosso pacote publicado, então nossa tarefa já está mais fácil.

Nosso único desafio aqui é o token necessário para autenticação. Não podemos simplesmente deixá-lo no arquivo .yml, senão todos com acesso ao repositório poderão vê-lo (very bad).

Por sorte, toda execução do GitHub Action tem uma variável chamada GITHUB_TOKEN que fica armazenada nos segredos.

Esta variável é gerada automaticamente e dura apenas a execução do action, o que a torna perfeita para nós.

Sendo assim podemos substituir nosso token por ${{ secrets.GITHUB_TOKEN }} que o GitHub se encarrega do resto.

Vamos então criar a estrutura que os GitHub Actions dependem em nosso projeto, que nada mais é do que um arquivo .yml gerado na pasta .github/worflows.

.github/worflows/main.yml

name: Build and deploy MeuPacote Package

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@main

    - name: Set up .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: '5.0.x'
        source-url: https://nuget.pkg.github.com/<ORGANIZATION>/index.json
      env:
        NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}

    - name: Build with dotnet
      run: dotnet build --configuration Release

    - name: Create the Package
      run: dotnet pack --configuration Release

    - name: Publish
      run: dotnet nuget push "bin/Release/*.nupkg" -k ${{ secrets.GITHUB_TOKEN }} -s https://nuget.pkg.github.com/<ORGANIZATION>/index.json --skip-duplicate

Feito isto, basta enviar o código para a branch main do seu repositório e pronto, você tem um pacote no GitHub Packages com deployment automatizado via GitHub Actions.

Consumindo um GitHub Package

Para consumir este pacote localmente (na sua máquina) você precisará de permissão, afinal este pacote é privado.

Se você está apenas estudando por este artigo, a maneira mais fácil de fazer isto é criar um arquivo NuGet.config na raiz do projeto no qual deseja consumir o pacote criado e adicionar o Package Source da sua organização.

Você também precisará de um PAT para ter acesso a este pacote.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
    <add key="github" value="https://nuget.pkg.github.com/<ORGANIZATION>/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <github>
        <add key="Username" value="USERNAME" />
        <add key="ClearTextPassword" value="TOKEN" />
    </github>
  </packageSourceCredentials>
</configuration>

Caso esteja em um projeto utilizando vários pacotes privados no GitHub Packages recomendo que copie este arquivo NuGet.config acima para e sobrescreva seu arquivo de configuração global do NuGet.

Este arquivo está localizado em:

Windows:%appdata%\NuGet\NuGet.Config
Mac/Linux: ~/.config/NuGet/NuGet.Config

GitHub Actions que dependem de GitHub Packages

Por último, o desafio é utilizar pacotes privados do GitHub Packages com em outras aplicações que são publicadas via GitHub Actions.

Como você pode notar durante a publicação com GitHub Actions, executamos um dotnet build e este por sua vez executa um dotnet restore que fará o download dos pacotes que sua aplicação depende.

O problema aqui é que como os pacotes são privados, você se deparará com um erro de permissão, afinal a máquina gerada pelos GitHub Actions não tem acesso a estes pacotes privados.

Então, assim como fizemos localmente, temos que adicionar um Package Source as configurações do NuGet da máquina antes do comando dotnet build, porém não vamos editar ou enviar um arquivo NuGet.config e sim utilizar o comando dotnet nuget add source.

dotnet nuget add source https://nuget.pkg.github.com/<ORGANIZATION>/index.json -n github -u USERNAME -p PAT 

Novamente precisamos informar o usuário e token, porém, neste cenárion o ${{ secrets.GITHUB_TOKEN }} não vai funcionar.

A maneira mais segura de armazenar seu usuário/token é utilizando os Secrets do GitHub em https://github.com/<ORGANIZATION>/<REPOSITORIO>/settings/secrets/actions.

Os secrets funcionam como chave/valor e uma vez criados você não pode mais visualizá-los, sua chave será exibida uma única vez na tela e depois já era! Você pode remover e criar outra, mas não pode visualizar ou alterar ela.

Desta forma, vamos adicionamos um passo a mais no arquivo de publicação do action, que eu chamei de Add GitHub Packages Source.

- name: Set up .NET Core
    uses: actions/setup-dotnet@v1
    with:
    dotnet-version: '5.0.x'

- name: Add GitHub Packages Source
    run: dotnet nuget add source https://nuget.pkg.github.com/<ORGANIZATION>/index.json -n github -u ${{secrets.MEU_USER}}  -p ${{secrets.MEU_TOKEN}} 

- name: Build with dotnet
    run: dotnet build --configuration Release

Pronto, você tem um deployment automatizado com GitHub Actions que consome um pacote privado do GitHub Packages que também foi automatizado com GitHub Actions! Mind blow!

Conclusão

Podemos criar e distribuir aplicações grandes sem a necessidade de criar microsserviços. Podemos criar pequenos e independentes pacotes privados e inclusive automatizar sua distribuição.

Mantenham as coisas o mais simples possível!

Populares

Priority Queue

Priority Queue ou fila prioritária é um tipo de lista inclusa no C# 10 que permite que seus itens...


Implicit Operators no C#

Implicit Operators permitem adicionar comportamentos de conversão a objetos Built In ou complexos...


ASP.NET 5 – Autenticação e Autorização com Bearer e JWT

Este artigo atualmente utiliza a versão 5.0.0-rc.1 do ASP.NET/.NET, o que significa que ainda não...


Clean Code - Guia e Exemplos

Saiba como manter seu código limpo (Clean Code) seguindo algumas práticas sugeridas pelo Robert C...


Git e GitHub - Instalação, Configuração e Primeiros Passos

Git é um sistema de controle de versões distribuídas, enquanto GitHub é uma plataforma que tem o ...


Compartilhe este artigo



Conheça o autor

André Baltieri

André Baltieri

Microsoft MVP

Me dedico ao desenvolvimento de software desde 2003, sendo minha maior especialidade o Desenvolvimento Web. Durante esta jornada pude trabalhar presencialmente aqui no Brasil e Estados Unidos, atender remotamente times da ?ndia, Inglaterra e Holanda, receber 8x Microsoft MVP e realizar diversas consultorias em empresas e projetos de todos os tamanhos.





3.133

Aulas disponíveis

292

horas de conteúdo

76.461

Alunos matriculados

53.012

Certificados emitidos





Comece de graça agora mesmo!

Temos mais de 21 cursos totalmente de graça e todos com certificado de conclusão.

Começar


Prefere algo mais Premium?

Conheça nossos planos



Premium anual

Compra única, parcelada em até
12x no cartão de crédito


12x R$

99

,79

=R$ 1.197,44
  • 1 ano de acesso
  • Acesso à todo conteúdo
  • Emissão de Certificado
  • Tira Dúvidas Online
  • 67 cursos disponíveis
  • 10 carreiras disponíveis
  • 161 temas de tecnologia
  • Conteúdo novo todo mês
  • Encontros Premium

Começar agora

Política de privacidade



Precisa de ajuda?

Dúvidas frequentes



  • Posso começar de graça?

    Sim! Basta criar sua conta gratuita no balta.io e começar seus estudos. Nós contamos com diversos cursos TOTALMENTE gratuitos e com certificado de conclusão.

  • Vou ter que pagar algo?

    Nós temos cursos gratuitos e pagos, porém você não precisa informar nenhum dado de pagamento para começar seus estudos gratuitamente conosco. Os cursos gratuitos são completos e com certificado de conclusão, você não paga nada por eles.

    Porém, caso queira algo mais Premium , você terá acesso à diversos benefícios que vão te ajudar ainda mais em sua carreira.

  • Por onde devo começar?

    Siga SEMPRE as nossas Carreiras , elas vão te orientar em todos os sentidos. Os cursos já estão organizados em categorias e carreiras para facilitar seu aprendizado.
    Nossa sugestão para aprendizado é começar pelo Backend e seguindo para Frontend e Mobile.

    • Backend
    • Frontend
    • Mobile

  • Os cursos ensinam tudo que preciso?

    Nenhum curso no mundo vai te ensinar tudo, desculpa ser sincero! Os cursos são uma base, eles fornecem por volta de 30% do que você precisa aprender, o resto é com você, com dedicação e MUITA prática.

  • O que eu devo estudar?

    Java ou .NET? Angular ou React? Xamarin ou Flutter? A resposta é simples e direta: "Você já sabe o básico?"

    Se você ainda não sabe BEM o básico, ou seja, os fundamentos, OOP, SOLID, Clean Code, está perdendo tempo estudando Frameworks ou até coisas mais avançadas como Docker. Foque nos seus objetivos primeiro.
    Agora se você está indeciso sobre qual Framework estudar, a boa notícia é que o mercado neste momento está bem aquecido e você tem várias oportunidade. Desta forma o que levaríamos em conta para tomar esta decisão seria:

    • Já sei o básico
    • O Framework/Tecnologia tem mercado onde eu estou (região)
    • O Framework/Tecnologia é utilizado em uma empresa onde quero atual
    • O Framework/Tecnologia resolve meu problema
    • Eu gosto de utilizar o Framework/Tecnologia

  • Estou pronto para estudar no balta.io?

    Com certeza! O primeiro passo é começar e você pode fazer isto agora mesmo!

    Começar de graça

Ainda tem dúvidas?





Assine nosso Newsletter

Receba em primeira mão todas as nossas novidades.

Cadastrar


balta.io

Sobre

  • Como funciona?
  • Seja Premium
  • Agenda
  • Blog
  • Todos os cursos

Cursos

  • Frontend
  • Backend
  • Mobile
  • Fullstack

Suporte

  • Termos de uso
  • Privacidade
  • Cancelamento
  • Central de ajuda

Redes Sociais

  • Telegram
  • Facebook
  • Instagram
  • YouTube
  • Twitch
  • LinkedIn
  • Discord