Estruturando o Projeto

O Lino estrutura soluções .NET para que decisões arquiteturais fiquem explícitas desde o primeiro commit. Um projeto gerado já nasce com orquestração Aspire, blocos compartilhados, testes, configuração de qualidade de código e um lugar claro para cada serviço, módulo, contrato de API, preocupação de persistência e limite de integração.


Esta seção explica como lino project new, lino service new e lino module new moldam a solução. O objetivo não é apenas criar pastas, mas definir limites de runtime, ownership de banco de dados, isolamento de módulos e um caminho de evolução de um serviço simples para monólito modular ou sistema distribuído.

Criando a Fundação da Solução

O comando lino project new cria a fundação técnica de uma nova solução .NET. Execute-o em um diretório vazio depois de instalar e autenticar a CLI.

lino project new --name <ProjectName>

O argumento <ProjectName> representa o nome real da solução. Esse nome passa a compor namespaces, assemblies, caminhos, configurações, artefatos e referências entre componentes; por isso, escolha um nome curto, estável e representativo.

O assistente interativo solicita decisões que afetam a estrutura e o comportamento de runtime da solução inteira:

  • Namespace do projeto: identidade técnica raiz usada pelos projetos gerados.
  • Nome de exibição: nome amigável utilizado em metadados gerados e pontos visíveis ao usuário.
  • Linguagem e stack: atualmente C# com .NET 10 e Aspire.
  • Analisadores de código: habilitam pacotes e regras compartilhadas para manter consistência e qualidade desde o bootstrap.
  • CQRS: prepara a camada de aplicação para separar commands e queries, com orquestração pela biblioteca de mediator selecionada.
  • Classes base na solução: controla se abstrações comuns serão geradas localmente dentro da solução.
  • Cache distribuído: define se o Microsoft.Extensions.Caching.Hybrid usará apenas memória local da instância ou também uma camada distribuída com Redis configurada pelo Aspire.
  • Comunicação assíncrona: habilita RabbitMQ com MassTransit e os blocos de mensageria/outbox usados por eventos de integração.
  • Idioma dos dados: idioma usado para descrever metadados de domínio durante a geração.
  • Culturas suportadas pela aplicação: recursos de localização gerados para textos de UI, validações, erros e respostas de API.
  • Cultura padrão: idioma principal usado quando a aplicação precisa de um fallback.

Depois da confirmação, o Lino gera uma solução organizada para crescimento. Um projeto mínimo nasce com Aspire, camadas compartilhadas e testes da área Shared:

<ProjectName>/
├── <ProjectName>.slnx
├── Directory.Build.props
├── Directory.Packages.props
├── src/
│   ├── Aspire/
│   │   ├── AppHost/
│   │   │   └── <ProjectName>.AppHost.csproj
│   │   └── ServiceDefaults/
│   │       └── <ProjectName>.ServiceDefaults.csproj
│   └── Services/
│       └── Shared/
│           ├── Api/
│           │   └── <ProjectName>.Shared.Api.csproj
│           ├── Application/
│           │   └── <ProjectName>.Shared.Application.csproj
│           ├── Domain/
│           │   └── <ProjectName>.Shared.Domain.csproj
│           ├── Infrastructure/
│           │   └── <ProjectName>.Shared.Infrastructure.csproj
│           ├── Infrastructure.Persistence/
│           │   └── <ProjectName>.Shared.Infrastructure.Persistence.csproj
│           └── Integration.Events/          (quando houver mensageria)
│               └── <ProjectName>.Shared.Integration.Events.csproj
└── tests/
    └── Services/
        └── Shared/
            └── UnitTests/
                ├── Domain/
                │   └── <ProjectName>.Shared.Domain.UnitTests.csproj
                └── Application/
                    └── <ProjectName>.Shared.Application.UnitTests.csproj

O papel dos projetos Shared

Shared contém código de plataforma compartilhado pela solução. Ele não é um serviço de negócio. Use esse espaço para abstrações transversais, erros comuns, infraestrutura de localização, contratos base de aplicação, helpers de persistência, extensões de API, integração com host, observabilidade e utilidades técnicas reutilizáveis.

Evite colocar regras de negócio em Shared. Se uma regra pertence a uma capacidade específica da aplicação, ela deve ficar no serviço ou módulo que possui aquela responsabilidade. O objetivo de Shared é reduzir repetição técnica, não virar um atalho para acoplar domínios diferentes.

Aspire e decisões de infraestrutura

AppHost compõe a solução e seus recursos de runtime. Quando Redis, RabbitMQ, SQL Server, PostgreSQL, Redis Insight, serviços, WebApps ou workers são adicionados, o Aspire se torna o ponto de orquestração local. Isso facilita execução, descoberta de serviços, logs, métricas, traces e visualização dos recursos durante o desenvolvimento.

ServiceDefaults centraliza padrões de hosting, como service discovery, health checks, resiliência, logging, métricas, tracing e integração com OpenTelemetry. Em vez de cada serviço configurar isso de forma isolada, a solução nasce com um ponto comum de composição.

Analisadores de código

Os analisadores de código estático inspecionam o código durante o desenvolvimento e tornam problemas visíveis antes da execução: inconsistências de estilo, padrões frágeis, oportunidades de refatoração, possíveis bugs e alertas de segurança.

Quando você habilita analisadores em lino project new, a solução já nasce com pacotes como StyleCop.Analyzers, SonarAnalyzer.CSharp e Roslynator.Analyzers configurados de forma centralizada. Isso evita que cada projeto precise decidir sozinho quais regras seguir.

  • Melhoria de qualidade: mantém o código legível, consistente e alinhado aos padrões da solução.
  • Prevenção de erros: aponta problemas cedo, antes que eles cheguem a testes manuais ou produção.
  • Padronização: reduz diferenças de estilo entre serviços, módulos e equipes.
  • Refatoração assistida: destaca simplificações e modernizações possíveis no código C#.

Cache distribuído, cache híbrido e Redis

O Lino prepara a solução para usar Microsoft.Extensions.Caching.Hybrid, a biblioteca da Microsoft que centraliza operações de cache através do HybridCache. Essa abstração permite que handlers, serviços de aplicação e componentes de infraestrutura armazenem resultados de consultas, permissões, configurações ou dados de apoio sem espalhar detalhes de implementação de cache pelo código.

Quando você não habilita cache distribuído na criação do projeto, o HybridCache continua disponível, mas trabalha restrito à memória local da instância em execução. Esse modo é simples e suficiente para cenários locais, ambientes pequenos ou aplicações com uma única réplica, mas cada processo mantém seu próprio cache e os dados não são compartilhados entre instâncias.

Quando você habilita cache distribuído, o Lino adiciona Redis aos recursos do Aspire e configura a infraestrutura para usar uma camada de cache compartilhada. Com Redis, múltiplas instâncias do mesmo serviço podem consultar a mesma camada, reduzindo leituras repetidas em banco de dados, APIs internas ou integrações e preparando o projeto para escala horizontal.

  • Performance: reduz tempo de resposta em leituras repetidas e operações de apoio.
  • Escalabilidade: permite que instâncias diferentes compartilhem dados em cache.
  • Disponibilidade: desacopla parte da carga de leitura do banco de dados principal.
  • Custo operacional: diminui processamento repetitivo em consultas e integrações de alto volume.

Essa decisão é feita em lino project new porque altera a fundação do ambiente: recursos do AppHost, secrets locais, pacotes, configuração de infraestrutura e a topologia de execução exibida no dashboard do Aspire.

Comunicação assíncrona

A comunicação assíncrona permite que serviços, módulos e componentes reajam a fatos do sistema sem bloquear o fluxo principal da operação. Ela é especialmente útil quando o produtor não deve depender da disponibilidade imediata do consumidor ou quando uma ação precisa disparar efeitos posteriores, como notificações, projeções, integrações externas ou sincronização entre contextos.

Quando você habilita comunicação assíncrona, o Lino adiciona RabbitMQ ao Aspire e configura MassTransit junto dos blocos de mensageria e outbox usados por eventos de integração. Assim, eventos publicados por serviços ou módulos ficam representados por contratos explícitos em Integration.Events e podem ser processados com mais resiliência.

  • Desempenho: permite continuar o caso de uso principal sem esperar todos os consumidores terminarem.
  • Escalabilidade: distribui processamento por consumidores e filas, absorvendo picos com mais controle.
  • Resiliência: permite reprocessamento e reduz perda de mensagens quando combinado com outbox.
  • Desacoplamento: evita dependência direta entre produtor e consumidor quando a resposta imediata não é necessária.

Use eventos de integração quando uma falha do consumidor não deve desfazer a transação do produtor. Quando o consumidor precisa responder imediatamente para completar o caso de uso, prefira uma integração síncrona explícita e trate disponibilidade, timeout e fallback como parte do contrato.

Localização e culturas

Quando culturas são selecionadas, o Lino gera recursos para que mensagens, validações, labels, erros e textos de UI possam ser localizados desde o início. Isso evita tratar internacionalização como remendo tardio, quando strings já estão espalhadas por endpoints, handlers e componentes.

Valide a fundação e avance para serviços

Após a geração, restaure dependências, compile a solução e execute o host do Aspire:

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

Nesse estágio, a solução ainda é uma fundação. Abra o projeto no editor, revise os recursos criados pelo Aspire, confirme as decisões de cache, mensageria, localização e qualidade de código, e então avance para os serviços que representarão as capacidades de negócio da aplicação.

Criando e Gerenciando Serviços

Um serviço é um limite de runtime e ownership. No Lino, serviços podem representar APIs independentes em um sistema distribuído ou áreas maiores de negócio dentro de uma solução que pode evoluir gradualmente.

Depois de criar a fundação do projeto, adicione serviços com:

lino service new

O assistente de serviço solicita:

  • Namespace do serviço: nome técnico usado em pastas, projetos e namespaces.
  • Nome de exibição: nome amigável do serviço.
  • Tipo de serviço: escolha entre simples e modular.
  • Provedor de banco: escolha SQL Server ou PostgreSQL para o banco de dados do serviço.
  • Estilo de arquitetura: atualmente Clean Architecture para serviços simples.
  • Strongly Typed IDs: em serviços simples, define se identificadores serão gerados como tipos dedicados.

Tipos de serviço

Serviço simples: estrutura mais direta para uma capacidade de negócio com fronteira clara. É uma boa escolha para uma API focada, um microsserviço ou uma área pequena o suficiente para evoluir como uma unidade.

Serviço modular: estrutura indicada para sistemas maiores, monólitos modulares ou runtimes que precisam hospedar várias capacidades independentes. Os módulos aumentam a organização e a escalabilidade do modelo, mas exigem mais disciplina de dependência.

Independentemente do tipo, cada serviço é dono do próprio banco. Em serviços simples, a decisão sobre Strongly Typed IDs é feita no serviço; em serviços modulares, ela é feita por módulo.

Serviço simples

Um serviço simples coloca suas camadas diretamente em src/Services/<ServiceName>. Use essa estrutura quando a capacidade de negócio possui um limite claro e não precisa de vários módulos isolados dentro do mesmo runtime. Ela funciona bem para uma API focada, um microsserviço ou uma área de domínio pequena o suficiente para evoluir como uma unidade.

src/Services/<ServiceName>/
├── Domain/
│   └── <ProjectName>.<ServiceName>.Domain.csproj
├── Application/
│   └── <ProjectName>.<ServiceName>.Application.csproj
├── Infrastructure.Persistence/
│   └── <ProjectName>.<ServiceName>.Infrastructure.Persistence.csproj
├── Infrastructure/
│   └── <ProjectName>.<ServiceName>.Infrastructure.csproj
├── Api/
│   └── <ProjectName>.<ServiceName>.Api.csproj
├── Integration.Events/              (quando houver mensageria)
│   └── <ProjectName>.<ServiceName>.Integration.Events.csproj
├── Api.Contracts/                   (quando houver consumo HTTP tipado)
│   └── <ProjectName>.<ServiceName>.Api.Contracts.csproj
└── Api.Client/                      (quando houver consumo HTTP tipado)
    └── <ProjectName>.<ServiceName>.Api.Client.csproj
tests/Services/<ServiceName>/
├── UnitTests/
│   ├── Domain/
│   │   └── <ProjectName>.<ServiceName>.Domain.UnitTests.csproj
│   └── Application/
│       └── <ProjectName>.<ServiceName>.Application.UnitTests.csproj
└── IntegrationTests/
    └── <ProjectName>.<ServiceName>.IntegrationTests.csproj

As camadas têm responsabilidades distintas: Domain protege regras de negócio e invariantes; Application orquestra casos de uso; Infrastructure.Persistence contém Entity Framework Core, repositórios, Unit of Work e migrations; Infrastructure contém integrações técnicas; e Api adapta HTTP para casos de uso.

Os projetos Api.Contracts e Api.Client aparecem quando há consumo HTTP tipado, especialmente em soluções com Web App Blazor. O primeiro concentra requests, responses, DTOs, tipos públicos e interfaces de client compartilhadas; o segundo fornece a implementação HTTP dessas interfaces, usando HttpClient, para o Blazor consumir as APIs geradas sem duplicar contratos.

O projeto Integration.Events aparece quando a solução possui mensageria habilitada. Ele contém eventos de integração publicados e consumidos por outros módulos, serviços ou sistemas, normalmente em conjunto com a infraestrutura de mensageria e outbox.

Serviço modular

Um serviço modular é indicado para monólitos modulares ou para um runtime que hospeda múltiplos bounded contexts. O serviço em si possui o host, infraestrutura comum e provedor de banco; as regras de negócio vivem dentro dos módulos.

src/Services/<ServiceName>/
├── Host/
│   └── <ProjectName>.<ServiceName>.Host.csproj
├── Infrastructure/
│   └── <ProjectName>.<ServiceName>.Infrastructure.csproj
└── Modules/
tests/Services/<ServiceName>/
└── Modules/

O Host compõe módulos, configurações, endpoints e infraestrutura compartilhada daquele serviço. Ele não deve conter regras de negócio. O projeto Infrastructure no nível do serviço fornece suporte técnico de composição compartilhado pelos módulos.

Quando um módulo é adicionado, os projetos de Domain, Application, Infrastructure.Persistence, Infrastructure e Api aparecem dentro de Modules/<ModuleName>, preservando a fronteira interna. Os projetos Api.Contracts, Api.Client e Integration.Events seguem as mesmas condições: consumo HTTP tipado para Blazor/API clients e mensageria habilitada para eventos de integração.

Ownership de banco de dados

Cada serviço é dono do próprio banco. Em uma solução com múltiplos serviços, um serviço pode usar PostgreSQL enquanto outro usa SQL Server. Essa decisão pode variar por necessidade de domínio, performance, operação, maturidade da equipe ou integração com infraestrutura existente.

Em um serviço modular, o banco pertence ao serviço como runtime, mas os módulos ficam isolados por schema, projetos de persistência e migrations próprias. Um módulo não deve consultar diretamente tabelas de outro módulo, mesmo quando as tabelas estão no mesmo banco físico.

Escolhendo entre simples e modular

Escolha a menor estrutura que protege o limite. Um serviço simples é suficiente quando existe uma única fronteira de domínio. Um serviço modular faz sentido quando várias capacidades precisam compartilhar runtime, deploy ou transação, mas ainda devem manter modelos, persistência, APIs e testes separados.

EscolhaQuando usarCusto assumido
Serviço simplesUm domínio focado, poucas fronteiras internas, API isolada ou microsserviço direto.Menos estrutura inicial, mas menos isolamento interno se o domínio crescer muito.
Serviço modularMúltiplos subdomínios no mesmo runtime, monólito modular, SaaS com áreas independentes ou necessidade de schemas por módulo.Mais projetos, mais disciplina de dependência e maior atenção a contratos internos.

Estilo de arquitetura

Os serviços gerados pelo Lino seguem Clean Architecture para separar regras de negócio de detalhes técnicos. A camada de domínio não precisa conhecer HTTP, Entity Framework Core, mensageria, UI ou provedores externos; essas preocupações ficam nas bordas da aplicação.

  • Desacoplamento: regras centrais não dependem de frameworks ou mecanismos de entrega.
  • Manutenibilidade: mudanças em infraestrutura tendem a ficar isoladas das regras de negócio.
  • Testabilidade: casos de uso e domínio podem ser testados com menos dependências externas.
  • Evolução: detalhes técnicos podem ser substituídos com menor impacto no núcleo do serviço.

Clean Architecture e Strongly Typed IDs

Serviços gerados seguem Clean Architecture para que código de negócio não dependa de HTTP, EF Core, mensageria, UI ou detalhes de infraestrutura. Essa separação torna testes mais diretos, reduz acoplamento e permite substituir detalhes técnicos sem reescrever regras centrais.

Strongly Typed IDs aumentam segurança ao impedir misturas acidentais de identificadores. Em vez de aceitar qualquer Guid, long ou int, o domínio pode trabalhar com um tipo próprio para cada entidade ou agregado, normalmente no formato <EntityName>Id. Isso evita passar o identificador de uma entidade onde outra era esperada e deixa assinaturas mais expressivas em commands, queries, entidades, handlers e mapeamentos.

  • Segurança de tipo: impede misturar identificadores de entidades diferentes por acidente.
  • Clareza: deixa assinaturas e contratos mais expressivos do que tipos primitivos soltos.
  • Refatoração: concentra mudanças de formato ou serialização no tipo de ID correspondente.
  • Redução de erros: torna usos incorretos visíveis em compilação sempre que possível.

Integração entre serviços

Quando um serviço precisa reagir a outro, prefira eventos de integração, integrações HTTP explícitas ou dados replicados conscientemente. Acesso direto ao banco de outro serviço cria acoplamento estrutural, dificulta migrações e torna deploys independentes mais arriscados.

Próximos passos para serviços modulares

Depois de criar um serviço modular, o próximo passo é adicionar módulos com lino module new. Cada módulo deve representar uma capacidade de negócio própria, com domínio, aplicação, persistência, API, testes e integrações preservando o limite interno do serviço.

Criando e Gerenciando Módulos

Módulos existem apenas dentro de serviços modulares. Um módulo representa um limite de negócio dentro do mesmo runtime: ele possui seu próprio modelo de domínio, casos de uso, persistência, superfície de API, eventos de integração, testes e schema de banco de dados.

Use módulos quando o serviço precisa hospedar mais de uma capacidade de negócio sem misturar entidades, regras, migrations e contratos. Em um monólito modular, tudo pode rodar no mesmo processo, mas os limites internos continuam importantes: isolamento real é controle de dependências, não apenas separação de pastas.

lino module new --service <ServiceName>

Durante a criação, o assistente solicita:

  • Serviço: o serviço modular que hospedará o módulo. O Lino não permite criar módulos dentro de serviços simples, porque eles não possuem a estrutura necessária para isolamento interno.
  • Namespace do módulo: o nome técnico usado em pastas, namespaces, assemblies e projetos.
  • Nome de exibição: o nome amigável do módulo em locais voltados para leitura humana.
  • Strongly Typed IDs: define se os identificadores gerados dentro do módulo usarão tipos dedicados, normalmente no formato <EntityName>Id.

Após a confirmação, o Lino adiciona o módulo dentro do serviço sem colapsar o limite arquitetural:

src/Services/<ServiceName>/
├── Host/
├── Infrastructure/
└── Modules/
    └── <ModuleName>/
        ├── Domain/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Domain.csproj
        ├── Application/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Application.csproj
        ├── Infrastructure.Persistence/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Infrastructure.Persistence.csproj
        ├── Infrastructure/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Infrastructure.csproj
        ├── Api/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Api.csproj
        ├── Integration.Events/              (quando houver mensageria)
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Integration.Events.csproj
        ├── Api.Contracts/                   (quando houver consumo HTTP tipado)
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Api.Contracts.csproj
        └── Api.Client/                      (quando houver consumo HTTP tipado)
            └── <ProjectName>.<ServiceName>.<ModuleName>.Api.Client.csproj
tests/Services/<ServiceName>/Modules/<ModuleName>/
├── UnitTests/
│   ├── Domain/
│   │   └── <ProjectName>.<ServiceName>.<ModuleName>.Domain.UnitTests.csproj
│   └── Application/
│       └── <ProjectName>.<ServiceName>.<ModuleName>.Application.UnitTests.csproj
└── IntegrationTests/
    └── <ProjectName>.<ServiceName>.<ModuleName>.IntegrationTests.csproj

Responsabilidades dos artefatos gerados

ArtefatoResponsabilidade dentro do módulo
DomainEntidades, agregados, Value Objects, enumerations, eventos de domínio, contratos de repositório e invariantes do módulo.
ApplicationCasos de uso, comandos, queries, handlers, validações, contratos internos de entrada e saída e orquestração das regras do módulo.
Infrastructure.PersistenceDbContext, configurações do Entity Framework Core, repositórios concretos, Unit of Work e migrations do módulo.
InfrastructureImplementações técnicas específicas do módulo, adaptadores, provedores e composição de dependências.
ApiEndpoints HTTP, versionamento, filtros, autorização e adaptação entre requisições externas e casos de uso.
Api.ContractsGerado quando há consumo HTTP tipado, normalmente por Web Apps Blazor. Contém requests, responses, DTOs, tipos públicos e interfaces de client compartilhadas entre a API e o consumidor.
Api.ClientGerado junto dos contratos quando há consumo HTTP tipado. Contém a implementação HTTP das interfaces, usando HttpClient, providers, options e helpers para que projetos Blazor consumam as APIs geradas de forma consistente e fortemente tipada.
Integration.EventsGerado quando o projeto possui mensageria. Contém eventos de integração disparados e consumidos por outros módulos, serviços ou sistemas, mantendo o payload como contrato explícito.

Estrutura de banco de dados

O banco de dados continua vinculado ao serviço, não ao módulo isoladamente. Dentro de um serviço modular, cada módulo é representado por seu próprio schema no banco associado, com projeto de persistência e migrations próprias. Isso oferece isolamento e organização sem exigir múltiplos bancos físicos para cada módulo.

Isolamento e independência entre módulos

Um módulo não deve acessar diretamente o DbContext, as entidades, os repositórios ou os serviços internos de outro módulo. Cada módulo possui seu próprio modelo e sua própria persistência. Quando outro módulo precisa de dados, use uma integração explícita em vez de atravessar a fronteira por detalhes internos de implementação.

Isso evita que uma mudança aparentemente local quebre outra área do sistema. Se um módulo consumidor precisa consultar dados que pertencem a outro módulo, ele não deve depender da entidade completa do módulo de origem. Ele pode manter uma shadow entity com os dados mínimos necessários para seu próprio caso de uso, alimentada por integração ou evento.

Vantagens desse desacoplamento:

  • Isolamento: cada módulo pode evoluir regras, persistência e testes sem atravessar detalhes internos de outro módulo.
  • Organização: a aplicação respeita bounded contexts e deixa ownership explícito.
  • Flexibilidade: módulos podem ser adicionados, removidos ou refatorados com menor impacto no restante do serviço.
  • Facilidade de testes: cada módulo pode ser validado de forma mais independente, aumentando confiança nas mudanças.

Comunicação entre módulos

Use contratos explícitos para comunicação. Para chamadas dentro do mesmo runtime, uma integração interna pode expor contratos em Integration.Contracts e implementação in-process quando gerada. Para chamadas entre runtimes, use integração HTTP. Para propagação assíncrona, publique eventos de integração em Integration.Events e use a infraestrutura de mensageria/outbox quando a publicação fizer parte de uma operação transacional.

Schemas, migrations e banco de dados

Em um serviço modular, os módulos compartilham o provedor de banco do serviço, mas cada módulo é representado por seu próprio schema e por seu próprio projeto Infrastructure.Persistence. As migrations são geradas por módulo, mantendo a evolução do banco alinhada ao limite de negócio.

Essa separação permite que cada módulo evolua suas tabelas, seeds, índices e foreign keys sem transformar o banco do serviço em um modelo único compartilhado por todos. O schema é um limite técnico que reforça o limite de negócio, mesmo quando os módulos rodam no mesmo processo e usam o mesmo banco físico.

Bons limites de módulo

Crie módulos ao redor de capacidades de negócio, não ao redor de camadas técnicas. Um bom nome de módulo deve descrever uma responsabilidade reconhecível pelo domínio, como uma área, processo ou capacidade que possui regras próprias. Evite nomes genéricos como Common, Core ou Utilities para regras de negócio, porque eles escondem ownership e tendem a virar dependências compartilhadas demais.

  • Coesão: o módulo deve ter linguagem, regras e dados que mudam juntos.
  • Autonomia: deve ser possível testar e evoluir o módulo sem acessar entidades internas de outro módulo.
  • Contratos claros: dados necessários fora do módulo devem ser expostos por API, integração, evento ou shadow entity, não por acesso direto ao banco.
  • Baixo acoplamento: se dois módulos precisam alterar as mesmas entidades o tempo todo, o limite provavelmente precisa ser revisto.

Com os módulos criados, os próximos tópicos da documentação mostram como modelar entidades, Value Objects, enumerations, commands, queries, APIs, eventos, integrações e migrations dentro desses limites.

Ocorreu um erro não tratado. Recarregar 🗙