Criando uma Aplicação .NET Completa com o Lino: Guia Passo a Passo

Este tΓ³pico apresenta um guia prΓ‘tico e orientado por passos para usar o Lino CLI como ferramenta principal na construΓ§Γ£o de um projeto: desde a instalaΓ§Γ£o e configuraΓ§Γ£o inicial, passando pela geraΓ§Γ£o de serviΓ§os, mΓ³dulos e entidades, atΓ© recursos avanΓ§ados como eventos, jobs em background, migraΓ§Γ΅es, build de imagens Docker e versionamento.

O objetivo Γ© mostrar, de forma integrada, como os comandos do CLI se encaixam no fluxo de desenvolvimento real β€” nΓ£o apenas listΓ‘-los, mas explicar o porquΓͺ de cada escolha, o que Γ© gerado automaticamente e quais sΓ£o as implicaΓ§Γ΅es arquiteturais.

Mesmo que cada comando jΓ‘ tenha documentaΓ§Γ£o especΓ­fica, aqui vocΓͺ verΓ‘ o processo fim-a-fim β€” um roteiro reproduzΓ­vel que economiza horas de trabalho repetitivo e ajuda a manter um cΓ³digo consistente e testΓ‘vel.

Ao longo do guia, explicaremos conceitos tΓ©cnicos aplicados pelo Lino (ex.: CQRS, TypedResults, Source Generators, Outbox Pattern), mostraremos exemplos de comandos e indicaremos boas prΓ‘ticas para versionamento, deploy e integraΓ§Γ£o contΓ­nua.

Use este roteiro como uma sequΓͺncia de aprendizado: primeiro entenda a estrutura, depois modele o domΓ­nio, sΓ³ entΓ£o gere APIs, pΓ‘ginas, eventos e artefatos de deploy. Isso reduz retrabalho e mantΓ©m o cΓ³digo gerado alinhado ao desenho do sistema.

Instalando e configurando o Lino CLI

O primeiro passo para comeΓ§ar a trabalhar com o Lino CLI Γ© instalar a ferramenta em seu ambiente de desenvolvimento. Ela Γ© distribuΓ­da como uma dotnet global tool, o que significa que estarΓ‘ disponΓ­vel para qualquer projeto .NET no seu computador. Antes de instalar, confirme que o SDK do .NET exigido pelos templates atuais estΓ‘ disponΓ­vel, que o terminal consegue executar dotnet e que o diretΓ³rio de ferramentas globais do .NET estΓ‘ no PATH.

Passo 1: InstalaΓ§Γ£o

Para instalar (ou atualizar) o Lino CLI, execute o seguinte comando no terminal:

dotnet tool install --global Tolitech.Lino

ObservaΓ§Γ΅es importantes:

  • Se jΓ‘ houver uma versΓ£o instalada, vocΓͺ pode usar dotnet tool update --global Tolitech.Lino para atualizar.
  • Verifique se o diretΓ³rio de ferramentas globais do .NET estΓ‘ no PATH do sistema para que o comando lino funcione corretamente.

Passo 2: Configurar idioma

ApΓ³s a instalaΓ§Γ£o, Γ© recomendado configurar o idioma (ou cultura) que o CLI irΓ‘ utilizar em mensagens, prompts e logs:

lino preferences culture set

VocΓͺ serΓ‘ solicitado a escolher entre os idiomas disponΓ­veis. Essa configuraΓ§Γ£o garante que todas as instruΓ§Γ΅es e prompts apareΓ§am de forma consistente no idioma desejado. Essa preferΓͺncia altera mensagens, prompts e orientaΓ§Γ΅es localizadas da CLI; ela nΓ£o renomeia entidades, serviΓ§os, mΓ³dulos ou termos de negΓ³cio gerados no projeto.

Passo 3: AutenticaΓ§Γ£o e registro

Para acessar todos os recursos do Lino, incluindo templates avanΓ§ados, publicaΓ§Γ£o de imagens Docker e integraΓ§Γ΅es com serviΓ§os externos, Γ© necessΓ‘rio estar autenticado.

- Caso ainda nΓ£o possua cadastro, registre-se com o comando:

lino user register

- Caso jΓ‘ tenha cadastro, faΓ§a login com:

lino auth login

O que acontece: o CLI armazena um token de autenticaΓ§Γ£o localmente, permitindo que vocΓͺ execute comandos que requerem acesso a recursos protegidos sem precisar se logar a cada uso. Mantenha esse token privado e evite compartilhar o mesmo perfil de usuΓ‘rio entre desenvolvedores, agentes de CI ou mΓ‘quinas diferentes.

Passo 4: VerificaΓ§Γ£o

Para confirmar que a instalaΓ§Γ£o e autenticaΓ§Γ£o foram realizadas com sucesso, execute:

lino --version

Se o comando retornar a versΓ£o instalada, vocΓͺ estΓ‘ pronto para comeΓ§ar a utilizar o Lino CLI em seus projetos.

Criando o projeto MyApp

Neste passo, vamos criar a estrutura inicial do projeto utilizando o Lino CLI. Este projeto servirΓ‘ como base para demonstrar a criaΓ§Γ£o de serviΓ§os, mΓ³dulos, front-end e toda a integraΓ§Γ£o de eventos. Um projeto Lino nΓ£o Γ© apenas uma pasta com uma solution: ele define convenΓ§Γ΅es para fronteiras de serviΓ§o, bibliotecas compartilhadas, host Aspire, estrutura de frontend, testes, gerenciamento de pacotes, analyzers e configuraΓ§Γ΅es que os prΓ³ximos comandos reutilizam.

Passo 1: Executando o comando de criaΓ§Γ£o

Para criar um novo projeto, execute o comando abaixo no terminal:

lino project new

O CLI irΓ‘ guiΓ‘-lo passo a passo, solicitando informaΓ§Γ΅es como:

  • Nome do projeto: usaremos MyApp, mas vocΓͺ pode escolher qualquer nome que desejar;
  • Recursos adicionais: analisadores de cΓ³digo, cache distribuΓ­do, suporte a eventos assΓ­ncronos, etc.

Passo 2: Configurando recursos essenciais

Para este projeto, recomendamos habilitar os seguintes recursos desde o inΓ­cio:

  • Analisadores de cΓ³digo: para garantir que o cΓ³digo esteja seguindo boas prΓ‘ticas e padrΓ΅es consistentes, prevenindo erros comuns de implementaΓ§Γ£o;
  • Cache distribuΓ­do: melhora a performance da aplicaΓ§Γ£o em cenΓ‘rios com mΓΊltiplos serviΓ§os, evitando consultas desnecessΓ‘rias ao banco de dados;
  • ComunicaΓ§Γ£o assΓ­ncrona: habilita o uso de eventos e filas para integraΓ§Γ£o entre serviΓ§os, garantindo escalabilidade e desacoplamento.

Γ‰ importante habilitar todas essas opΓ§Γ΅es neste projeto, pois criaremos mΓΊltiplos serviΓ§os que irΓ£o se comunicar atravΓ©s de eventos de integraΓ§Γ£o. Isso permitirΓ‘ que vocΓͺ compreenda como estruturar sistemas modulares e distribuΓ­dos usando o Lino.

Passo 3: Estrutura gerada

ApΓ³s a execuΓ§Γ£o do comando e configuraΓ§Γ£o dos recursos, o CLI irΓ‘ gerar a estrutura inicial do projeto. Ela incluirΓ‘:

  • Pastas de serviΓ§os e mΓ³dulos;
  • Templates para front-end (se aplicΓ‘vel);
  • ConfiguraΓ§Γ΅es iniciais de cache, eventos e integraΓ§Γ΅es;
  • Arquivos de soluΓ§Γ£o (.slnx) e projeto (.csproj) prontos para compilaΓ§Γ£o.

Agora seu projeto MyApp estΓ‘ pronto para receber os serviΓ§os, mΓ³dulos, entidades e front-end que configuraremos nos prΓ³ximos passos. Antes de continuar, abra a soluΓ§Γ£o gerada e execute dotnet build para confirmar que referΓͺncias, source generators, templates, arquivos de projeto e configuraΓ§Γ£o inicial estΓ£o coerentes.

Adicionando a aplicaΓ§Γ£o web Backoffice

Um sistema completo normalmente precisa de pelo menos uma aplicaΓ§Γ£o web para operar o domΓ­nio. Neste guia, a aplicaΓ§Γ£o se chama Backoffice e representa uma interface interna para administradores, gestores ou usuΓ‘rios operacionais acompanharem produtos, categorias, estoques, vendas e demais informaΓ§Γ΅es do sistema.

A aplicaΓ§Γ£o web nΓ£o substitui os serviΓ§os de domΓ­nio. Ela atua como ponto de entrada visual para consumir APIs, acionar Commands, consultar Queries e expor telas coerentes com as regras jΓ‘ modeladas nos serviΓ§os.

Passo 1: Executando o comando de criaΓ§Γ£o

Para adicionar uma nova aplicaΓ§Γ£o web ao projeto, utilize:

lino web-app new

O alias lino webapp new tambΓ©m estΓ‘ disponΓ­vel para facilitar a digitaΓ§Γ£o. Durante a execuΓ§Γ£o, informe um nome claro para a aplicaΓ§Γ£o web; neste exemplo, usaremos Backoffice.

lino web-app new --name Backoffice

Passo 2: Entendendo a estrutura gerada

Ao final do processo, o Lino cria a estrutura inicial da Web App em src/WebApps/<WebAppName>. Para uma aplicaΓ§Γ£o Blazor, a estrutura pode incluir projetos server/client, recursos compartilhados, arquivos de localizaΓ§Γ£o, clients para consumo das APIs e convenΓ§Γ΅es que serΓ£o usadas posteriormente por lino page new.

  • Pastas para pΓ‘ginas, componentes, layouts, services e recursos da aplicaΓ§Γ£o;
  • Clientes HTTP e contratos necessΓ‘rios para consumir APIs expostas pelos serviΓ§os do projeto;
  • Resources de localizaΓ§Γ£o e templates iniciais usados pela experiΓͺncia web;
  • Projetos client/server quando o tipo de aplicaΓ§Γ£o exigir essa separaΓ§Γ£o;
  • ConvenΓ§Γ΅es de rotas, navegaΓ§Γ£o e integraΓ§Γ£o que serΓ£o reutilizadas na geraΓ§Γ£o de pΓ‘ginas;
  • Pontos de integraΓ§Γ£o com autenticaΓ§Γ£o e autorizaΓ§Γ£o quando a feature de auth for adicionada ao projeto.

Passo 3: Quando criar a Web App no fluxo

Se vocΓͺ jΓ‘ sabe que o sistema terΓ‘ uma interface Blazor, crie a Web App logo no inΓ­cio, depois da criaΓ§Γ£o do projeto. Assim, os serviΓ§os, mΓ³dulos, entidades, APIs e pΓ‘ginas gerados depois jΓ‘ ficam alinhados com a aplicaΓ§Γ£o web que irΓ‘ consumi-los.

Esse fluxo Γ© especialmente ΓΊtil porque os serviΓ§os criados posteriormente tambΓ©m geram projetos tipados de Api.Contracts e Api.Client, consumidos pelo projeto Blazor. Com a Web App presente desde cedo, fica mais simples validar o caminho completo: domΓ­nio, API, contratos, HttpClient e tela.

ObservaΓ§Γ΅es importantes

  • O Backoffice deve consumir os dados por meio das APIs geradas, mantendo a lΓ³gica de negΓ³cio nos serviΓ§os e mΓ³dulos corretos.
  • Antes de expor pΓ‘ginas administrativas, revise autenticaΓ§Γ£o, autorizaΓ§Γ£o, roles, permissΓ΅es e polΓ­ticas de acesso.
  • VocΓͺ pode criar mΓΊltiplas aplicaΓ§Γ΅es web, por exemplo um Backoffice interno e um Site pΓΊblico, quando pΓΊblicos, permissΓ΅es, deploys ou responsabilidades forem diferentes.
  • Evite misturar fluxos pΓΊblicos e administrativos na mesma Web App quando isso dificultar seguranΓ§a, navegaΓ§Γ£o, deploy ou ownership da equipe.

Com a aplicaΓ§Γ£o web criada, o guia pode avanΓ§ar para os serviΓ§os e mΓ³dulos que fornecerΓ£o os dados e regras de negΓ³cio consumidos por essa interface.

Criando serviΓ§os e mΓ³dulos

Nesta etapa, vamos construir os serviΓ§os e mΓ³dulos que irΓ£o compor a aplicaΓ§Γ£o. O objetivo Γ© criar uma arquitetura modular e escalΓ‘vel, permitindo que diferentes Γ‘reas do sistema, como produtos, categorias, estoque, vendas e mΓ­dia, evoluam de forma independente, mantendo coesΓ£o e facilitando a manutenΓ§Γ£o. ServiΓ§os definem fronteiras de deploy e persistΓͺncia; mΓ³dulos organizam Γ‘reas de negΓ³cio dentro de um serviΓ§o modular. Antes de criar arquivos, avalie quais partes do domΓ­nio precisam de banco prΓ³prio, release independente, contrato de integraΓ§Γ£o ou ownership separado.

Passo 1: Definindo os serviΓ§os

Inicialmente, criaremos os seguintes serviΓ§os, cada um com responsabilidades bem definidas:

  • Catalog (modular) – responsΓ‘vel por gerenciar produtos, categorias e preΓ§os;
  • Sales – responsΓ‘vel pelo processamento de vendas e pedidos;
  • Stock – responsΓ‘vel pelo gerenciamento de estoques e movimentaΓ§Γ΅es;
  • Security – responsΓ‘vel por autenticaΓ§Γ£o, autorizaΓ§Γ£o e gerenciamento de usuΓ‘rios.

Para criar os serviΓ§os do exemplo, execute um comando para cada serviΓ§o. No wizard do Catalog, escolha arquitetura modular; nos demais, escolha a arquitetura adequada para uma fronteira simples.

lino service new --name Catalog
lino service new --name Sales
lino service new --name Stock
lino service new --name Security

Durante a execuΓ§Γ£o do comando, o CLI irΓ‘ solicitar:

  • Nome do serviΓ§o: por exemplo, Catalog;
  • Nome de exibiΓ§Γ£o e estilo de arquitetura: escolha uma arquitetura simples para fronteiras compactas ou modular quando o serviΓ§o tiver Γ‘reas coesas independentes;
  • Banco de dados: escolha a tecnologia que se adequa melhor ao seu projeto (SQL Server, PostgreSQL, etc.);

Passo 2: Criando mΓ³dulos dentro de serviΓ§os

Nem todos os serviΓ§os precisam ser modulares. No nosso projeto, apenas o serviΓ§o Catalog terΓ‘ mΓ³dulos, para separar responsabilidades como merchandising e precificaΓ§Γ£o.

Definimos para o serviΓ§o Catalog os seguintes mΓ³dulos:

  • Merchandising – gerenciamento de produtos e categorias;
  • Pricing – gerenciamento de preΓ§os, promoΓ§Γ΅es e histΓ³rico de alteraΓ§Γ΅es.

Para criar os mΓ³dulos do Catalog, execute:

lino module new --service Catalog --name Merchandising
lino module new --service Catalog --name Pricing

VocΓͺ pode criar quantos mΓ³dulos desejar dentro do serviΓ§o modular Catalog. AlΓ©m disso, dependendo da complexidade, alguns mΓ³dulos poderiam se tornar serviΓ§os independentes futuramente. A separaΓ§Γ£o apresentada aqui Γ© apenas didΓ‘tica e serve como exemplo de organizaΓ§Γ£o modular. NΓ£o use mΓ³dulos apenas para criar pastas; use-os quando eles protegem uma Γ‘rea de negΓ³cio com entidades, casos de uso, APIs, migrations e eventos prΓ³prios.

Estrutura final do projeto

ApΓ³s a criaΓ§Γ£o dos serviΓ§os e mΓ³dulos, sua soluΓ§Γ£o deverΓ‘ ter uma estrutura semelhante Γ  seguinte:

MyApp/
└── src/
    β”œβ”€β”€ Aspire/
    β”œβ”€β”€ Integrations/
    β”œβ”€β”€ Services/
    β”‚   β”œβ”€β”€ Catalog/
    β”‚   β”‚   β”œβ”€β”€ Modules/
    β”‚   β”‚   β”‚   β”œβ”€β”€ Merchandising/
    β”‚   β”‚   β”‚   └── Pricing/
    β”‚   β”‚   β”œβ”€β”€ MyApp.Catalog.Host
    β”‚   β”‚   └── MyApp.Catalog.Infrastructure
    β”‚   β”œβ”€β”€ Sales/
    β”‚   β”œβ”€β”€ Security/
    β”‚   β”œβ”€β”€ Shared/
    β”‚   └── Stock/
    └── WebApps/
        β”œβ”€β”€ Backoffice/
        β”‚   β”œβ”€β”€ Services/
        β”‚   β”‚   β”œβ”€β”€ Catalog/
        β”‚   β”‚   β”œβ”€β”€ Sales/
        β”‚   β”‚   β”œβ”€β”€ Security/
        β”‚   β”‚   └── Stock/
        β”‚   β”œβ”€β”€ MyApp.WebApp.Backoffice
        β”‚   └── MyApp.WebApp.Backoffice.Client
        └── Shared/
            └── MyApp.WebApp.Shared
└── tests/
    └── Services/
        β”œβ”€β”€ Catalog/
        β”‚   β”œβ”€β”€ Merchandising/
        β”‚   └── Pricing/
        β”œβ”€β”€ Sales/
        β”œβ”€β”€ Security/
        β”œβ”€β”€ Shared/
        └── Stock/

ExplicaΓ§Γ£o da estrutura:

  • Services/: contΓ©m todos os serviΓ§os do sistema, cada um isolado com sua prΓ³pria lΓ³gica de negΓ³cio, infraestrutura e hospedagem;
  • Modules/: pastas dentro de serviΓ§os modulares, permitindo organizar funcionalidades especΓ­ficas e manter cΓ³digo coeso;
  • WebApps/: frontends associados ao sistema, jΓ‘ integrados aos serviΓ§os;
  • Shared/: bibliotecas e recursos compartilhados entre serviΓ§os e frontends;
  • tests/: testes unitΓ‘rios e de integraΓ§Γ£o organizados por serviΓ§o e mΓ³dulo.

Com essa estrutura modular, cada equipe ou desenvolvedor pode trabalhar de forma independente em diferentes partes do sistema, facilitando escalabilidade, manutenΓ§Γ£o e testes. Depois de criar serviΓ§os e mΓ³dulos, execute dotnet build para confirmar que os projetos foram anexados corretamente Γ  soluΓ§Γ£o, ao host Aspire, aos projetos compartilhados e Γ  estrutura de testes.

Adicionando autenticaΓ§Γ£o e autorizaΓ§Γ£o

A autenticaΓ§Γ£o e autorizaΓ§Γ£o sΓ£o elementos essenciais de qualquer sistema moderno. AutenticaΓ§Γ£o comprova quem Γ© o usuΓ‘rio; autorizaΓ§Γ£o define o que esse usuΓ‘rio pode executar. No Lino CLI, a feature de auth gera a base necessΓ‘ria para usuΓ‘rios, papΓ©is, permissΓ΅es, tokens, polΓ­ticas de acesso e integraΓ§Γ£o com APIs.

Passo 1: Executando o comando de autenticaΓ§Γ£o

Para adicionar os recursos de autenticaΓ§Γ£o e autorizaΓ§Γ£o, utilize o comando:

lino feature auth add

O CLI irΓ‘ guiΓ‘-lo pelas seguintes etapas:

  • Escolha do serviΓ§o ou mΓ³dulo: vocΓͺ precisa indicar onde os artefatos de autenticaΓ§Γ£o serΓ£o instalados. No nosso projeto de exemplo, utilizamos o serviΓ§o Security, que centraliza toda a lΓ³gica de seguranΓ§a do sistema;
  • ConfiguraΓ§Γ΅es adicionais: criaΓ§Γ£o de tabelas de usuΓ‘rios, papΓ©is, permissΓ΅es, tokens e configuraΓ§Γ£o de polΓ­ticas de acesso;
  • Tempo de vida dos tokens: definiΓ§Γ£o de expiraΓ§Γ£o para access token e refresh token de acordo com a polΓ­tica de seguranΓ§a do produto;
  • Tipo do identificador do usuΓ‘rio: escolha do tipo usado pelo modelo de usuΓ‘rio e pelos contratos gerados.

Passo 2: Estrutura gerada

ApΓ³s a execuΓ§Γ£o do comando, o serviΓ§o Security conterΓ‘ arquivos e pastas como:

  • Domain/Entities: agregados, entidades e regras para usuΓ‘rios, papΓ©is, permissΓ΅es e tokens;
  • Infrastructure/Persistence: configuraΓ§Γ΅es do banco de dados, mapeamentos do Entity Framework e migrations para tabelas de seguranΓ§a;
  • Application: Commands, Queries, Handlers, serviΓ§os de autenticaΓ§Γ£o, geraΓ§Γ£o de tokens, validaΓ§Γ£o de credenciais e checagens de permissΓ£o;
  • API/Host: endpoints para login, logout, cadastro, refresh token e operaΓ§Γ΅es protegidas;
  • IntegraΓ§Γ£o com Web App: suporte para fluxos autenticados quando uma aplicaΓ§Γ£o web estiver presente.

Com isso, sua aplicaΓ§Γ£o terΓ‘ autenticaΓ§Γ£o robusta e controle de acesso granular, pronta para suportar mΓΊltiplos usuΓ‘rios e diferentes nΓ­veis de permissΓ΅es. Ainda assim, cΓ³digo gerado deve ser tratado como uma base forte, nΓ£o como a revisΓ£o final de seguranΓ§a. Antes de produΓ§Γ£o, revise tempo de vida de tokens, desenho de permissΓ΅es, polΓ­tica de senha, HTTPS, secrets, rate limiting, logs e configuraΓ§Γ΅es de deploy.

Adicionando Background Jobs

Em sistemas distribuΓ­dos e modulares, como o que estamos construindo com o Lino CLI, nem todos os serviΓ§os se comunicam diretamente entre si. Para garantir consistΓͺncia e confiabilidade na troca de informaΓ§Γ΅es, utilizamos eventos de integraΓ§Γ£o. PorΓ©m, para processar esses eventos de forma eficiente e assΓ­ncrona, precisamos de Background Jobs.

O Lino utiliza o padrΓ£o Outbox Pattern para garantir que todas as mensagens geradas pelos serviΓ§os sejam registradas de forma confiΓ‘vel antes de serem enviadas. Com isso, conseguimos:

  • Evitar a perda de eventos em caso de falhas ou reinicializaΓ§Γ΅es do serviΓ§o;
  • Garantir que a mesma mensagem seja enviada apenas uma vez;
  • Permitir o reprocessamento de mensagens em caso de falha na entrega;
  • Separar o processamento de eventos da lΓ³gica principal da aplicaΓ§Γ£o, melhorando performance e escalabilidade.

Passo 1: Executando o comando

Para adicionar suporte a Background Jobs no seu projeto, execute o comando:

lino feature background-job add

O CLI irΓ‘ solicitar que vocΓͺ selecione o serviΓ§o onde o Background Job serΓ‘ instalado. Geralmente, vocΓͺ escolherΓ‘ o serviΓ§o que centraliza a produΓ§Γ£o de eventos, como Catalog ou Sales. Nas opΓ§Γ΅es atuais, o wizard tambΓ©m pode solicitar o mΓ³dulo, a biblioteca de jobs, se eventos de Outbox devem ser processados, o agendamento e o tamanho do lote. O fluxo dos templates atuais usa Hangfire para execuΓ§Γ£o recorrente dos jobs.

Passo 2: Configurando a execuΓ§Γ£o

Durante a configuraΓ§Γ£o, vocΓͺ poderΓ‘ definir:

  • Intervalo de verificaΓ§Γ£o: determina com que frequΓͺncia o Background Job irΓ‘ verificar a tabela Outbox para novas mensagens. Um intervalo muito curto pode aumentar o uso de recursos, enquanto um intervalo longo pode atrasar a entrega de eventos;
  • Lote de registros processados por vez: controla quantos eventos serΓ£o lidos e enviados por execuΓ§Γ£o. Lotes maiores podem aumentar a performance, mas exigem mais memΓ³ria e processamento;
  • PolΓ­tica de retries: em caso de falha no envio de mensagens, vocΓͺ poderΓ‘ configurar quantas vezes o job tentarΓ‘ reenviar.

Esses parΓ’metros dependem do tamanho do seu sistema, da capacidade da mΓ‘quina e do volume esperado de eventos.

Passo 3: Estrutura gerada

ApΓ³s a configuraΓ§Γ£o, o projeto terΓ‘ um Background Job pronto para processar mensagens das tabelas Outbox em cada serviΓ§o.

  1. O caso de uso altera o domΓ­nio e registra um evento de domΓ­nio ou integraΓ§Γ£o.
  2. A unidade de trabalho salva os dados de negΓ³cio e as mensagens de Outbox na mesma transaΓ§Γ£o.
  3. O Hangfire executa jobs recorrentes que leem mensagens pendentes em lotes.
  4. As mensagens sΓ£o publicadas no mecanismo de integraΓ§Γ£o configurado, como RabbitMQ quando comunicaΓ§Γ£o assΓ­ncrona estiver habilitada.
  5. Mensagens concluΓ­das, falhas, antigas ou presas podem ser tratadas pela lΓ³gica e configuraΓ§Γ£o geradas para o job.

Com isso, vocΓͺ garante que todos os eventos de integraΓ§Γ£o sejam processados de forma confiΓ‘vel e eficiente, permitindo que mΓΊltiplos serviΓ§os e mΓ³dulos se comuniquem de forma assΓ­ncrona, sem impactar a performance do sistema principal. A regra operacional mais importante Γ© manter a fronteira transacional clara: eventos que precisam ser enviados via Outbox devem ser criados no mesmo fluxo transacional da alteraΓ§Γ£o de negΓ³cio que representam.

Criando entidades e enumeraΓ§Γ΅es

Nesta seΓ§Γ£o, vamos detalhar o design das entidades, enumeraΓ§Γ΅es e Value Objects da aplicaΓ§Γ£o, mostrando em quais serviΓ§os e mΓ³dulos cada item serΓ‘ criado.

1. Criando a entidade Category

Para criar a entidade Category no serviΓ§o Catalog e no mΓ³dulo Merchandising, execute:

lino entity new --service Catalog --module Merchandising --name Category

A entidade serΓ‘ criada no serviΓ§o Catalog e no mΓ³dulo Merchandising com a seguinte estrutura:

β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PK β”‚ FK β”‚ Property name β”‚ Type   β”‚ Length β”‚ Required β”‚ Auto-increment β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ x  β”‚    β”‚ Id            β”‚ Guid   β”‚        β”‚    x     β”‚       x        β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Name          β”‚ string β”‚   50   β”‚    x     β”‚                β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Criando a entidade Product

Em seguida, criamos a entidade Product no mesmo serviΓ§o e mΓ³dulo. Neste fluxo, o Value Object ProductDimension e a enum ProductStatus sΓ£o configurados dentro do prΓ³prio wizard de criaΓ§Γ£o da entidade, como parte do agregado Product:

lino entity new --service Catalog --module Merchandising --name Product

Durante a execuΓ§Γ£o, adicione as propriedades simples, o relacionamento com Category, a propriedade Dimensions do tipo Value Object e a propriedade Status do tipo Enum.

β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PK β”‚ FK β”‚ Property name β”‚ Type        β”‚ Length β”‚ Required β”‚ Auto-increment β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ x  β”‚    β”‚ Id            β”‚ Guid        β”‚        β”‚    x     β”‚       x        β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Name          β”‚ string      β”‚  100   β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Description   β”‚ string      β”‚  500   β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Price         β”‚ decimal     β”‚        β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚ x  β”‚ CategoryId    β”‚ Category    β”‚        β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Dimensions    β”‚ ValueObject β”‚        β”‚          β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Status        β”‚ Enum        β”‚        β”‚    x     β”‚                β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2.1 Configurando o Value Object ProductDimension dentro de Product

No cenΓ‘rio deste guia, ProductDimension nΓ£o Γ© criado por um comando separado. Ele Γ© adicionado durante o lino entity new de Product, como a propriedade Dimensions, e representa as dimensΓ΅es do produto:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Property name β”‚ Type    β”‚ Length β”‚ Required β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Width         β”‚ decimal β”‚        β”‚    x     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Height        β”‚ decimal β”‚        β”‚    x     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Depth         β”‚ decimal β”‚        β”‚    x     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

ObservaΓ§Γ£o: o comando lino value-object new existe para cenΓ‘rios em que o Value Object precisa ser criado separadamente. Nesses casos, use os argumentos do serviΓ§o e do mΓ³dulo de destino:

lino value-object new --service <ServiceName> --module <ModuleName> --name <ValueObjectName>

2.2 Configurando a Enum ProductStatus dentro de Product

Da mesma forma, ProductStatus Γ© configurada dentro do lino entity new de Product, como a propriedade Status. Ela define o status do produto:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Value β”‚ Name         β”‚ Display Name β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1     β”‚ Active       β”‚ Active       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 2     β”‚ Inactive     β”‚ Inactive     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 3     β”‚ Discontinued β”‚ Discontinued β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

ObservaΓ§Γ£o: o comando lino enumeration new tambΓ©m pode ser usado quando uma enum precisa ser criada separadamente em outro cenΓ‘rio:

lino enumeration new --service <ServiceName> --module <ModuleName> --name <EnumerationName>

3. Adicionando novas propriedades

Com a evoluΓ§Γ£o do projeto, podemos editar entidades existentes para adicionar novas propriedades. Por exemplo, adicionaremos uma lista de imagens Γ  entidade Product:

lino entity edit --service Catalog --module Merchandising --entity Product

Dentro desse mesmo fluxo, criamos a propriedade Images do tipo List<ProductImage>. Como ProductImage pertence ao agregado Product, sua estrutura tambΓ©m Γ© configurada durante o lino entity edit de Product:

β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PK β”‚ FK β”‚ Property name β”‚ Type               β”‚ Length β”‚ Required β”‚ Auto-increment β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ x  β”‚    β”‚ Id            β”‚ Guid               β”‚        β”‚    x     β”‚       x        β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Name          β”‚ string             β”‚  100   β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Description   β”‚ string             β”‚  500   β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Price         β”‚ decimal            β”‚        β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚ x  β”‚ CategoryId    β”‚ EntityId           β”‚        β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Dimensions    β”‚ ValueObject        β”‚        β”‚          β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Status        β”‚ Enum               β”‚        β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚ x  β”‚ Images        β”‚ List<ProductImage> β”‚        β”‚          β”‚                β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

3.1 Configurando ProductImage dentro de Product

ProductImage nΓ£o Γ© criado por um comando separado neste cenΓ‘rio. Ele Γ© configurado como item da coleΓ§Γ£o Images durante a ediΓ§Γ£o de Product e possui a seguinte estrutura:

β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PK β”‚ FK β”‚ Property name β”‚ Type           β”‚ Length β”‚ Required β”‚ Auto-increment β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ x  β”‚    β”‚ Id            β”‚ Guid           β”‚        β”‚    x     β”‚       x        β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚ x  β”‚ ProductId     β”‚ EntityId       β”‚        β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ UploadDate    β”‚ DateTimeOffset β”‚        β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Image         β”‚ File           β”‚        β”‚    x     β”‚                β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

ObservaΓ§Γ£o: o comando lino entity new existe para criar entidades independentes em outros cenΓ‘rios. Quando a entidade nΓ£o faz parte da ediΓ§Γ£o de um agregado existente, use:

lino entity new --service <ServiceName> --module <ModuleName> --name <EntityName>

4. Criando entidades para outros serviΓ§os

No serviΓ§o Sales, criamos a entidade ProductSnapshot, que serΓ‘ alimentada pelos eventos de integraΓ§Γ£o. Como o Id original da entidade Product vem do serviΓ§o Catalog, ele nΓ£o pode ser auto-incremental aqui.

lino entity new --service Sales --name ProductSnapshot
β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PK β”‚ FK β”‚ Property name β”‚ Type    β”‚ Length β”‚ Required β”‚ Auto-increment β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ x  β”‚    β”‚ Id            β”‚ Guid    β”‚        β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Name          β”‚ string  β”‚  100   β”‚    x     β”‚                β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Price         β”‚ decimal β”‚        β”‚    x     β”‚                β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

ObservaΓ§Γ£o: apenas os campos essenciais foram replicados no ProductSnapshot para o serviΓ§o Sales. Entidades complementares, como Customer, Order e StockItem, nΓ£o serΓ£o detalhadas aqui para simplificar a documentaΓ§Γ£o. O ProductSnapshot funciona como uma shadow entity: uma cΓ³pia local, mΓ­nima e controlada de dados cujo dono estΓ‘ em outro serviΓ§o. Isso permite que Sales consulte os dados necessΓ‘rios de produto sem depender diretamente da entidade ou do banco do Catalog.

Esse tipo de estrutura tambΓ©m poderia ser criado pelo comando lino shadow new, alias de lino shadow-entity new. Nesse fluxo, o Lino copia a estrutura de uma entidade de outro serviΓ§o ou mΓ³dulo e permite selecionar apenas as propriedades que fazem sentido para o contexto consumidor.

lino shadow new --service <ServiceName> --module <ModuleName> --name <ShadowEntityName>

No exemplo, a entidade de origem Γ© Catalog.Merchandising.Product e o destino Γ© o serviΓ§o Sales, mantendo somente os campos necessΓ‘rios para vendas.

Criando eventos e seus manipuladores

Recapitulando, no tΓ³pico anterior criamos no serviΓ§o modular Catalog.Merchandising as entidades Product, Category e ProductImage, enquanto no serviΓ§o Sales criamos a entidade ProductSnapshot.

Agora, vamos criar eventos de domΓ­nio e eventos de integraΓ§Γ£o. O objetivo Γ© que, ao criar ou atualizar produtos no serviΓ§o Catalog, essas alteraΓ§Γ΅es sejam replicadas para os serviΓ§os consumidores, como Sales e Stock.

1. Criando eventos de domΓ­nio

O primeiro passo Γ© criar os eventos de domΓ­nio ProductCreated e ProductUpdated com o comando:

lino event new

Durante a criaΓ§Γ£o, podemos associar o evento a um manipulador e, simultaneamente, configurar o disparo de um evento de integraΓ§Γ£o. Isso centraliza a criaΓ§Γ£o de todo o fluxo necessΓ‘rio.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Question                                             β”‚ Answer                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a service:                                    β”‚ Catalog                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a module:                                     β”‚ Merchandising                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a entity:                                     β”‚ Product                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event type:                               β”‚ Domain Event                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Enter the name of the event:                         β”‚ ProductCreated                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Do you want to create an associated event handler?   β”‚ Yes                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Trigger a integration event?                         β”‚ Yes                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Choose the integration event to be triggered:        β”‚ (Create new integration event) β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Enter the name of the event:                         β”‚ ProductCreated                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Which model will be used for this integration event? β”‚ Creation model                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Da mesma forma, criamos o evento ProductUpdated:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Question                                             β”‚ Answer                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a service:                                    β”‚ Catalog                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a module:                                     β”‚ Merchandising                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a entity:                                     β”‚ Product                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event type:                               β”‚ Domain Event                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Enter the name of the event:                         β”‚ ProductUpdated                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Do you want to create an associated event handler?   β”‚ Yes                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Trigger a integration event?                         β”‚ Yes                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Choose the integration event to be triggered:        β”‚ (Create new integration event) β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Enter the name of the event:                         β”‚ ProductUpdated                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Which model will be used for this integration event? β”‚ Update model                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Com isso, temos:

  • Eventos de domΓ­nio criados (ProductCreated e ProductUpdated);
  • Manipuladores de eventos de domΓ­nio correspondentes;
  • Eventos de integraΓ§Γ£o registrados na Outbox automaticamente pelo manipulador de eventos de domΓ­nio.

2. Criando manipuladores de eventos de integraΓ§Γ£o

O prΓ³ximo passo Γ© definir quais serviΓ§os irΓ£o consumir os eventos de integraΓ§Γ£o. Para isso, usamos:

lino event-handler new

O fluxo de criaΓ§Γ£o envolve:

  • Selecionar o serviΓ§o, mΓ³dulo e entidade que conterΓ‘ o manipulador;
  • Selecionar o evento de integraΓ§Γ£o que serΓ‘ consumido e de qual serviΓ§o/mΓ³dulo/entidade ele virΓ‘.

Por exemplo, no serviΓ§o Sales criamos os manipuladores para ProductCreated e ProductUpdated que irΓ£o consumir os eventos disparados pelo Catalog.Merchandising.Product:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Question                                   β”‚ Answer            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a service:                          β”‚ Sales             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a entity:                           β”‚ ProductSnapshot   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event type:                     β”‚ Integration Event β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event's service to be consumed: β”‚ Catalog           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event's module to be consumed:  β”‚ Merchandising     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event's entity to be consumed:  β”‚ Product           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Choose the event to be consumed:           β”‚ ProductCreated    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Enter the name of the event handler:       β”‚ ProductCreated    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Question                                   β”‚ Answer            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a service:                          β”‚ Sales             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a entity:                           β”‚ ProductSnapshot   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event type:                     β”‚ Integration Event β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event's service to be consumed: β”‚ Catalog           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event's module to be consumed:  β”‚ Merchandising     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select the event's entity to be consumed:  β”‚ Product           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Choose the event to be consumed:           β”‚ ProductUpdated    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Enter the name of the event handler:       β”‚ ProductUpdated    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Com isso, temos dois manipuladores de eventos de integraΓ§Γ£o no serviΓ§o Sales, consumindo apenas os campos necessΓ‘rios dos eventos de integraΓ§Γ£o do Catalog.Merchandising. Isso garante que as tabelas replicadas mantenham apenas os dados essenciais, otimizando armazenamento e desempenho. Em termos de arquitetura, o fluxo esperado Γ©: o Command Handler altera o agregado, o evento de domΓ­nio Γ© levantado, o manipulador prepara o evento de integraΓ§Γ£o, a Outbox armazena a mensagem, o Background Job processa a pendΓͺncia e o mecanismo de mensageria entrega a mensagem aos consumidores. Isso evita acoplamento sΓ­ncrono entre serviΓ§os e reduz o risco de o banco ser alterado sem que o evento correspondente seja publicado.

Gerando pΓ‘ginas web, APIs, Commands e Queries

Uma das grandes vantagens do Lino CLI Γ© permitir a criaΓ§Γ£o integrada de pΓ‘ginas web, APIs, Commands e Queries de forma automatizada, simplificando todo o fluxo de desenvolvimento. Quando o modelo de domΓ­nio jΓ‘ estΓ‘ estΓ‘vel o suficiente, esse comando cria um caminho completo da tela atΓ© a persistΓͺncia, tornando visΓ­vel para o usuΓ‘rio final o trabalho de modelagem feito nas etapas anteriores. Para iniciar, basta executar o comando:

lino page new

Durante o processo, vocΓͺ irΓ‘:

  • Selecionar o serviΓ§o, mΓ³dulo e entidade que deseja expor;
  • Escolher o tipo e o nome da pΓ‘gina que serΓ‘ gerada;
  • Escolher os campos que farΓ£o parte da listagem;
  • Gerar automaticamente pΓ‘ginas de listagem (grid paginado) e formulΓ‘rio de criaΓ§Γ£o/ediΓ§Γ£o;
  • Gerar classes de HttpClient para consumo pelo frontend;
  • Criar contratos de request/response e todas as APIs REST necessΓ‘rias (POST, PUT, PATCH, DELETE e GET);
  • Criar Commands, Queries, Handlers e validators, que vΓ£o atΓ© o banco de dados garantindo o fluxo completo de CRUD.

Com esse comando, vocΓͺ obtΓ©m uma aplicaΓ§Γ£o funcional sem precisar escrever manualmente a camada de interface, APIs e lΓ³gica de negΓ³cio, mantendo o padrΓ£o e a consistΓͺncia entre serviΓ§os. Mesmo assim, revise regras de negΓ³cio e validaΓ§Γ΅es depois da geraΓ§Γ£o, porque nem toda regra pode ser inferida apenas pelos metadados das propriedades.

Para este projeto, podemos gerar as pΓ‘ginas integradas para as seguintes entidades:

  • Catalog.Merchandising.Category
  • Catalog.Merchandising.Product
  • Sales.ProductSnapshot

ApΓ³s gerar as pΓ‘ginas, APIs e Commands/Queries, a aplicaΓ§Γ£o estarΓ‘ pronta para interagir de forma completa entre frontend e backend, com validaΓ§Γ΅es, rotas e persistΓͺncia jΓ‘ configuradas automaticamente pelo Lino CLI. Execute dotnet build apΓ³s a geraΓ§Γ£o para identificar cedo referΓͺncias quebradas, contratos inconsistentes ou impactos de mudanΓ§as recentes no modelo.

Criando e aplicando migrations

Depois de criar ou alterar entidades, Value Objects, enumeraΓ§Γ΅es, relacionamentos, autenticaΓ§Γ£o, suporte a tenant ou persistΓͺncia de Background Jobs, gere migrations para manter banco de dados e cΓ³digo alinhados. A migration transforma a mudanΓ§a de modelo em um artefato explΓ­cito, versionΓ‘vel e revisΓ‘vel.

O Lino CLI coordena esse processo com Entity Framework, selecionando o serviΓ§o/mΓ³dulo correto, usando a versΓ£o atual do serviΓ§o e organizando os scripts gerados para facilitar rastreabilidade.

Passo 1: Criando uma migration

Para criar uma nova migration, execute:

lino database migrations add

O comando tambΓ©m pode aceitar aliases como lino database migrations new e lino database migrations create, mas a forma preferida na documentaΓ§Γ£o Γ© add.

Durante a execuΓ§Γ£o, vocΓͺ deverΓ‘ informar:

  • O serviΓ§o que receberΓ‘ a migration, por exemplo Catalog;
  • O mΓ³dulo afetado, quando o serviΓ§o for modular, por exemplo Merchandising;
  • A versΓ£o atual do serviΓ§o, lida a partir de src/Services/<ServiceName>/version.txt;
  • Uma descriΓ§Γ£o objetiva para a migration, como Initial migration ou Add product images.
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Question                                  β”‚ Answer            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a service:                         β”‚ Catalog           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Select a module:                          β”‚ Merchandising     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Current version of the service:           β”‚ 0.1.0             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Provide a description for this migration: β”‚ Initial migration β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Quando vocΓͺ jΓ‘ sabe o serviΓ§o e o mΓ³dulo, pode informar esses dados diretamente:

lino database migrations add --service <ServiceName> --module <ModuleName>

Passo 2: O que Γ© gerado

Ao confirmar a criaΓ§Γ£o, o Lino prepara os comandos corretos do Entity Framework para o projeto de persistΓͺncia do serviΓ§o ou mΓ³dulo selecionado. Em um serviΓ§o modular, a migration permanece isolada no mΓ³dulo correspondente, evitando misturar alteraΓ§Γ΅es de contextos diferentes.

  • Uma nova migration de banco de dados associada Γ  versΓ£o atual do serviΓ§o;
  • O script SQL correspondente, organizado na camada de persistΓͺncia, em um caminho versionado como /scripts/<version>/<nome do arquivo>;
  • Artefatos compatΓ­veis com a estrutura de Infrastructure/Persistence do serviΓ§o ou mΓ³dulo selecionado;
  • HistΓ³rico rastreΓ‘vel para relacionar versΓ£o de serviΓ§o, mudanΓ§as de schema e release da aplicaΓ§Γ£o.

Passo 3: Listando e aplicando migrations

Antes de aplicar alteraΓ§Γ΅es em um banco, liste as migrations conhecidas e confirme se a migration esperada estΓ‘ presente:

lino database migrations list --service <ServiceName> --module <ModuleName>

Para aplicar migrations no ambiente configurado:

lino database migrations apply --service <ServiceName> --module <ModuleName>

Em desenvolvimento local, esse fluxo acelera a validaΓ§Γ£o do modelo. Em ambientes compartilhados ou produΓ§Γ£o, aplique migrations pelo processo de deploy definido pela equipe, com revisΓ£o do script, aprovaΓ§Γ£o e backup quando necessΓ‘rio.

Passo 4: Revertendo ou removendo em ambiente controlado

Use revert quando precisar voltar uma migration aplicada em um ambiente controlado, entendendo que a operaΓ§Γ£o pode executar comandos destrutivos dependendo do conteΓΊdo da migration:

lino database migrations revert --service <ServiceName> --module <ModuleName>

Use remove para descartar a ΓΊltima migration ainda nΓ£o consolidada, normalmente antes de commitar ou publicar a alteraΓ§Γ£o:

lino database migrations remove --service <ServiceName> --module <ModuleName>

Boas prΓ‘ticas

  • Crie migrations sempre que houver alteraΓ§Γ΅es no modelo persistido: novas entidades, propriedades, relaΓ§Γ΅es, Γ­ndices, constraints, tabelas de auth ou tabelas de Outbox.
  • Revise o cΓ³digo e o SQL gerados antes de aplicar em ambientes compartilhados.
  • Tenha atenΓ§Γ£o especial com renames, mudanΓ§as de tipo, drops de coluna, alteraΓ§Γ΅es de chave, mudanΓ§as de schema e operaΓ§Γ΅es que podem causar perda de dados.
  • Mantenha a versΓ£o do serviΓ§o alinhada aos scripts gerados para facilitar auditoria, rollback planejado e comunicaΓ§Γ£o de release.
  • Depois de criar a migration, execute build e testes relevantes para confirmar que domain, persistence, APIs e pΓ‘ginas geradas continuam coerentes.

Seguindo esse fluxo, o banco de dados permanece consistente com o modelo de domΓ­nio definido no Lino, e cada mudanΓ§a de schema fica documentada, rastreΓ‘vel e pronta para ser revisada antes do deploy.

Passo 5: Validando a aplicaΓ§Γ£o localmente

Com projeto, Web App, serviΓ§os, mΓ³dulos, entidades, migrations, APIs, Commands e Queries prontos, valide a aplicaΓ§Γ£o antes de gerar imagens Docker. Primeiro, compile a soluΓ§Γ£o para confirmar que todos os projetos, contratos e clients gerados continuam coerentes:

dotnet build

Em seguida, execute a aplicaΓ§Γ£o pelo AppHost do Aspire:

dotnet run --project src/Aspire/AppHost/<ProjectName>.AppHost.csproj

Use o dashboard do Aspire para conferir se APIs, Web App, banco de dados, cache, mensageria e Background Jobs subiram corretamente. Depois, teste os fluxos principais no Backoffice: pΓ‘ginas geradas, chamadas do Blazor para os projetos Api.Client, migrations aplicadas, autenticaΓ§Γ£o quando habilitada, e eventos ou jobs quando fizerem parte do cenΓ‘rio.

Quando a aplicaΓ§Γ£o compilar, executar localmente e os fluxos principais estiverem validados, o projeto estarΓ‘ pronto para a etapa de empacotamento.

Gerando imagens Docker

Depois que a aplicaΓ§Γ£o compila, roda localmente pelo AppHost e os fluxos principais foram testados, vocΓͺ pode gerar imagens Docker dos serviΓ§os e aplicaΓ§Γ΅es web para posterior publicaΓ§Γ£o em um registro de contΓͺineres. Use lino build quando os itens selecionados estiverem prontos para empacotamento como imagens.

O Lino CLI simplifica esse processo com o comando:

lino build

Ao executar, vocΓͺ verΓ‘ uma lista de todos os serviΓ§os e aplicaΓ§Γ΅es web disponΓ­veis no projeto, juntamente com suas versΓ΅es atuais:

Select the services or web applications you want to include in the build:

> [ ] Services
    [ ] Catalog |0.1.0|
    [ ] Sales |0.1.0|
    [ ] Security |0.1.0|
    [ ] Stock |0.1.0|
  [ ] Web applications
    [ ] Backoffice |0.1.0|

VocΓͺ pode selecionar um ou mais serviΓ§os e aplicaΓ§Γ΅es web para gerar imagens simultaneamente. Basta marcar os itens desejados.

Em seguida, serΓ‘ solicitado escolher como deseja atualizar a versΓ£o das imagens geradas. As opΓ§Γ΅es disponΓ­veis sΓ£o:

  • Manter a versΓ£o atual – nΓ£o altera a versΓ£o existente;
  • Patch – incrementa a versΓ£o de patch (ex.: 0.1.0 β†’ 0.1.1);
  • Minor – incrementa a versΓ£o menor (ex.: 0.1.0 β†’ 0.2.0);
  • Major – incrementa a versΓ£o principal (ex.: 0.1.0 β†’ 1.0.0).

ApΓ³s selecionar os serviΓ§os e definir o incremento de versΓ£o, o Lino CLI realiza:

  • Build do cΓ³digo de cada serviΓ§o e aplicaΓ§Γ£o web;
  • GeraΓ§Γ£o da imagem Docker correspondente;
  • AplicaΓ§Γ£o da tag com a versΓ£o definida;
  • DisponibilizaΓ§Γ£o das imagens para publicaΓ§Γ£o em seu registro de contΓͺineres;
  • Uso de parΓ’metros de publish como -c Release, -p:PublishProfile=DefaultContainer, -p:ContainerRepository, -p:ContainerImageTag e -p:ContainerLabelVersion, conforme o tipo de projeto gerado.

Ao final do processo, considerando que todos os serviΓ§os e aplicaΓ§Γ΅es web foram selecionados, as imagens geradas terΓ£o a seguinte estrutura:

  • my-app/services/catalog-host - tag: 0.1.0
  • my-app/services/sales-api - tag: 0.1.0
  • my-app/services/security-api - tag: 0.1.0
  • my-app/services/stock-api - tag: 0.1.0
  • my-app/webapps/backoffice - tag: 0.1.0

Em termos gerais, serviΓ§os simples tendem a gerar repositΓ³rios como project-name/services/service-name-api:1.2.3, serviΓ§os modulares usam hosts como project-name/services/service-name-host:1.2.3, e aplicaΓ§Γ΅es Blazor usam caminhos como project-name/webapps/webapp-name:1.2.3.

ObservaΓ§Γ£o: Esse processo garante consistΓͺncia entre o cΓ³digo e a versΓ£o das imagens Docker, facilitando o deploy e a manutenΓ§Γ£o de mΓΊltiplos ambientes, alΓ©m de permitir que cada serviΓ§o seja isolado em contΓͺineres independentes. Depois que a imagem local for gerada, publique-a no registro usado pela sua plataforma de deploy, como Docker Hub, GitHub Container Registry, AWS ECR, Azure Container Registry ou outro registro compatΓ­vel com OCI. NΓ£o inclua secrets, connection strings de produΓ§Γ£o ou credenciais dentro da imagem.

Criando versΓ΅es na aplicaΓ§Γ£o

O Lino mantΓ©m a versΓ£o operacional de cada serviΓ§o em src/Services/<ServiceName>/version.txt e de cada aplicaΓ§Γ£o web em src/WebApps/<WebAppName>/version.txt. Isso permite planejar releases independentes para cada item implantΓ‘vel.

Antes de alterar versΓ΅es, inspecione o estado atual:

lino version list

Use lino version show quando precisar consultar um serviΓ§o ou aplicaΓ§Γ£o web especΓ­fica.

Incrementar novas versΓ΅es aos serviΓ§os ou aplicaΓ§Γ΅es web Γ© um processo simples e centralizado no Lino CLI. Basta executar o comando:

lino version bump

Assim como na geraΓ§Γ£o de imagens Docker, ao executar esse comando vocΓͺ verΓ‘ uma lista completa de todos os serviΓ§os e aplicaΓ§Γ΅es web do seu projeto. Apenas os itens que vocΓͺ selecionar terΓ£o sua versΓ£o incrementada, enquanto os demais permanecerΓ£o inalterados.

Select the services or web applications that will have version changes:

> [ ] Services
    [ ] Catalog |0.1.0|
    [ ] Sales |0.1.0|
    [ ] Security |0.1.0|
    [ ] Stock |0.1.0|
  [ ] Web applications
    [ ] Backoffice |0.1.0|

ApΓ³s selecionar os itens desejados, vocΓͺ serΓ‘ solicitado a escolher o tipo de incremento de versΓ£o. As opΓ§Γ΅es disponΓ­veis sΓ£o:

  • Patch – pequenas correΓ§Γ΅es sem impacto funcional;
  • Minor – adiΓ§Γ£o de novas funcionalidades compatΓ­veis com versΓ΅es anteriores;
  • Major – alteraΓ§Γ΅es que podem quebrar compatibilidade com versΓ΅es anteriores.

Γ‰ importante destacar que as versΓ΅es dos serviΓ§os e aplicaΓ§Γ΅es web tΓͺm impacto direto em:

  • As tags das imagens Docker;
  • As pastas utilizadas para armazenar scripts gerados pelas migraΓ§Γ΅es de banco de dados;
  • O controle de releases e histΓ³rico do projeto;
  • Notas de release, manifests de deploy e comunicaΓ§Γ£o com consumidores de API.

Antes de aplicar um bump, revise mudanΓ§as de cΓ³digo, migrations, eventos de integraΓ§Γ£o, contratos de API e alteraΓ§Γ΅es no frontend que fazem parte do release. Uma versΓ£o Patch deve representar correΓ§Γ΅es compatΓ­veis, Minor deve representar adiΓ§Γ΅es compatΓ­veis, e Major deve ser reservado para mudanΓ§as que exigem adaptaΓ§Γ£o de consumidores.

Com isso, concluΓ­mos o guia passo a passo de todos os comandos essenciais para a construΓ§Γ£o de um projeto web utilizando o Lino CLI, desde a instalaΓ§Γ£o, criaΓ§Γ£o de serviΓ§os, entidades, eventos e pΓ‘ginas, atΓ© a geraΓ§Γ£o de imagens Docker e versionamento. O fluxo completo fica rastreΓ‘vel: modelar domΓ­nio, gerar casos de uso e telas, validar com builds e testes, criar migrations, publicar imagens com tags de versΓ£o e liberar cada serviΓ§o ou aplicaΓ§Γ£o web com um valor SemVer explΓ­cito.

NΓ£o deixe de acompanhar nosso canal no YouTube para acompanhar tutoriais detalhados, demonstraΓ§Γ΅es prΓ‘ticas e dicas de uso da ferramenta, desde operaΓ§Γ΅es simples atΓ© recursos avanΓ§ados.

Ocorreu um erro não tratado. Recarregar πŸ—™