Modelando o domínio
No coração de qualquer aplicação orientada a domínio está o modelo que representa o conhecimento central e as regras de negócio do sistema. Modelar bem o domínio significa traduzir conceitos do mundo real em estruturas de software expressivas, coesas e consistentes.
Entidades
Uma entidade é um objeto definido principalmente pela sua identidade e não apenas pelos seus atributos. Mesmo que os atributos mudem ao longo do tempo, a identidade de uma entidade permanece a mesma.
Características principais:
- Possui uma identidade única (geralmente um
Id). - Aquilo que importa é quem a entidade é, não apenas o que ela contém.
- Seus atributos podem mudar ao longo do tempo.
Criando uma entidade com o Lino
Para criar uma nova entidade utilizando o Lino, execute:
lino entity new
O assistente do CLI solicitará:
- Serviço – Serviço no qual a entidade será criada.
- Módulo – Módulo em que a entidade será criada (apenas em serviços modulares).
- Nome da entidade – Nome utilizado no domínio e na tabela do banco de dados.
Em seguida, você definirá os campos que compõem a entidade, configurando cada um deles.
Tipos de campos disponíveis
| Tipo | Descrição | Faixa / Observações |
|---|---|---|
short | Inteiro de 16 bits | -32 768 → 32 767 |
int | Inteiro de 32 bits | -2 147 483 648 → 2 147 483 647 |
long | Inteiro de 64 bits | -9 223 372 036 854 775 808 → 9 223 372 036 854 775 807 |
string | Texto | Até ~2 bilhões de caracteres |
bool | Valor booleano | true ou false |
Guid | Identificador global único | Unicidade distribuída |
decimal | Número decimal de alta precisão | Ideal p/ valores monetários |
float | Ponto flutuante (32 bits) | ≈ 6–9 dígitos de precisão |
double | Ponto flutuante (64 bits) | ≈ 15–17 dígitos de precisão |
DateTime | Data e hora | Inclui fuso horário |
DateOnly | Só data (C# 10+) | – |
TimeOnly | Só hora (C# 10+) | – |
Entity | Referência a outra entidade | 1 : 1 ou 1 : N |
Value Object | Value Object imutável | Ex.: Endereço, CPF |
Enum | Enumeração | Conjunto fixo de valores |
List<Entity> | Lista de entidades | 1 : N |
ManyToMany | Muitos‑para‑muitos | Requer tabela de junção |
Exemplo
Criando a entidade Person:
┌────┬────┬───────────────┬────────┬────────┬───────────┬────────────────┐ │ PK │ FK │ Property name │ Type │ Length │ Required │ Auto-increment │ ├────┼────┼───────────────┼────────┼────────┼───────────┼────────────────┤ │ x │ │ Id │ int │ │ x │ x │ ├────┼────┼───────────────┼────────┼────────┼───────────┼────────────────┤ │ │ │ Name │ string │ 100 │ x │ │ └────┴────┴───────────────┴────────┴────────┴───────────┴────────────────┘
Estrutura gerada pelo Lino:
<ProjectName>/
└── src/
└── Services/
└── <ServiceName>/
└── Domain/
├── <ProjectName>.<ServiceName>.Domain.csproj
└── Aggregates/
└── People/
├── Person.cs
├── Errors/
│ └── PersonErrors.cs
├── Repositories/
│ └── IPersonRepository.cs
└── Resources/
└── Person/
├── PersonResources.resx
├── PersonResources.en.resx
└── PersonResources.pt-BR.resx
Depois de definir suas entidades, use o próprio Lino para gerenciar as Migrations e manter o banco de dados sincronizado. Abordaremos esse processo em detalhes na seção Camada de Persistência.
Fluxo atual de evolução
Além do fluxo interativo com lino entity new, a CLI também permite deixar a intenção explícita no comando quando o serviço, módulo e nome já são conhecidos:
lino entity new --name <EntityName> --service <ServiceName> --module <ModuleName> lino entity edit --service <ServiceName> --module <ModuleName> --entity <EntityName> lino entity list --service <ServiceName> --module <ModuleName>
Use entity list antes de criar novos conceitos em módulos grandes. Durante a edição, revise identificador, propriedades, obrigatoriedade, tamanho, relacionamentos, índices, ownership, tenant e impacto em migrations.
Strongly Typed IDs, ownership e invariantes
Quando Strongly Typed IDs estiverem habilitados, o identificador passa a ser um tipo dedicado, como ProductId, reduzindo trocas acidentais entre IDs de entidades diferentes. Relacionamentos devem refletir ownership real do domínio: nem toda referência precisa virar navegação direta; em muitos casos, um identificador, shadow entity, evento de integração ou consulta explícita protege melhor o limite entre módulos.
Coloque invariantes no domínio, não apenas na interface, no banco ou em validators. Depois de alterar entidades, rode build, revise o diff e gere ou atualize migrations.
Value Objects
Um Value Object representa um conceito do domínio definido apenas pelos seus atributos – ele não possui identidade própria. Dois Value Objects são considerados iguais se todos os seus valores forem iguais.
Características principais:
- Imutáveis após a criação.
- Não possuem
Id.
Criando um Value Object com o Lino
Execute:
lino value-object new
O CLI solicitará:
- Serviço – Serviço no qual o objeto será criado.
- Módulo – Módulo em que o objeto será criado (apenas em serviços modulares).
- Localização – Raiz do domínio ou agregado específico.
- Nome do Value Object.
Em seguida, defina os campos que compõem o objeto.
Tipos de campos disponíveis
| Tipo | Descrição | Observações |
|---|---|---|
short | Inteiro de 16 bits | -32 768 → 32 767 |
int | Inteiro de 32 bits | -2 147 483 648 → 2 147 483 647 |
long | Inteiro de 64 bits | -9 223 372 036 854 775 808 → 9 223 372 036 854 775 807 |
string | Texto | Até ~2 bilhões de caracteres |
bool | Booleano | true/false |
decimal | Decimal preciso | Valores monetários |
float | Ponto flutuante (32 bits) | ≈ 6–9 dígitos |
double | Ponto flutuante (64 bits) | ≈ 15–17 dígitos |
DateTime | Data/hora | Inclui fuso |
DateOnly | Somente data | C# 10+ |
TimeOnly | Somente hora | C# 10+ |
Exemplo
Value Object Address:
┌───────────────┬────────┬────────┬───────────┐ │ Property name │ Type │ Length │ Required │ ├───────────────┼────────┼────────┼───────────┤ │ Street │ string │ 100 │ x │ ├───────────────┼────────┼────────┼───────────┤ │ Number │ string │ 10 │ x │ ├───────────────┼────────┼────────┼───────────┤ │ Neighborhood │ string │ 50 │ │ ├───────────────┼────────┼────────┼───────────┤ │ City │ string │ 100 │ x │ ├───────────────┼────────┼────────┼───────────┤ │ State │ string │ 2 │ x │ ├───────────────┼────────┼────────┼───────────┤ │ PostalCode │ string │ 20 │ x │ ├───────────────┼────────┼────────┼───────────┤ │ Country │ string │ 100 │ x │ └───────────────┴────────┴────────┴───────────┘
Estrutura de arquivos gerada (agregado Person):
<ProjectName>/
└── src/
└── Services/
└── <ServiceName>/
└── Domain/
├── <ProjectName>.<ServiceName>.Domain.csproj
└── Aggregates/
└── People/
├── Person.cs
├── ValueObjects/
│ └── Address.cs
├── Errors/
│ ├── AddressErrors.cs
│ └── PersonErrors.cs
├── Repositories/
│ └── IPersonRepository.cs
└── Resources/
├── Address/
│ ├── AddressResources.resx
│ ├── AddressResources.en.resx
│ └── AddressResources.pt-BR.resx
└── Person/
├── PersonResources.resx
├── PersonResources.en.resx
└── PersonResources.pt-BR.resx
Assim como nas entidades, as Migrations podem ser gerenciadas pelo Lino para manter o modelo de dados em sincronia.
Fluxo atual de evolução
Além do comando interativo, use parâmetros quando já souber onde o conceito deve nascer:
lino value-object new --name <ValueObjectName> --service <ServiceName> --module <ModuleName> lino value-object edit --service <ServiceName> --module <ModuleName> --value-object <ValueObjectName> lino value-object list --service <ServiceName> --module <ModuleName>
Quando uma propriedade de entidade é declarada como ValueObject, o Lino pode reutilizar um Value Object existente ou criar um novo durante a modelagem da entidade. Prefira essa opção quando o conjunto de campos representar um conceito único, como dinheiro, endereço, dimensões, período ou documento.
Persistência, UI e localização
O Lino mapeia propriedades de Value Objects pela camada de persistência do módulo e carrega metadados de exibição para resources. Isso permite labels e mensagens localizadas para valores aninhados. O Value Object pertence ao módulo onde foi gerado; ele não vira contrato compartilhado entre módulos sem uma integração explícita.
Proteja invariantes no próprio tipo: valores negativos, moeda inválida, período invertido ou documento malformado não devem nascer válidos.
Enumerações
As enumerações em DDD podem ir além dos enum tradicionais do C#. Elas podem ser objetos ricos
que representam estados fixos, contendo validações, métodos auxiliares e até comportamento.
Motivação:
- Os
enumdo C# são limitados a um valor inteiro ou string. - Modelar uma Enumeration como classe oferece maior flexibilidade e expressividade.
Características principais:
- São classes que herdam de uma base comum e encapsulam
IdeNome. - Permitem adicionar validações, métodos auxiliares e comportamento.
Criando uma enumeração com o Lino
Execute:
lino enumeration new
O assistente solicitará:
- Serviço.
- Módulo (se aplicável).
- Localização – Raiz do domínio ou agregado.
- Nome da enumeração.
- Tipo –
enumtradicional ou Smart Enum (class). - Armazenamento –
intoustringno banco de dados.
Exemplo
Enumeração PersonStatus:
┌───────┬───────────┬──────────────┐ │ Value │ Name │ Display Name │ ├───────┼───────────┼──────────────┤ │ 1 │ Active │ Active │ ├───────┼───────────┼──────────────┤ │ 2 │ Inactive │ Inactive │ ├───────┼───────────┼──────────────┤ │ 3 │ Suspended │ Suspended │ ├───────┼───────────┼──────────────┤ │ 4 │ Deleted │ Deleted │ └───────┴───────────┴──────────────┘
Estrutura gerada:
<ProjectName>/
└── src/
└── Services/
└── <ServiceName>/
└── Domain/
├── <ProjectName>.<ServiceName>.Domain.csproj
└── Aggregates/
└── People/
├── Person.cs
├── Enums/
│ └── PersonStatus.cs
├── ValueObjects/
│ └── Address.cs
├── Errors/
│ ├── AddressErrors.cs
│ └── PersonErrors.cs
├── Repositories/
│ └── IPersonRepository.cs
└── Resources/
├── Address/
│ ├── AddressResources.resx
│ ├── AddressResources.en.resx
│ └── AddressResources.pt-BR.resx
├── Person/
│ ├── PersonResources.resx
│ ├── PersonResources.en.resx
│ └── PersonResources.pt-BR.resx
└── PersonStatus/
├── PersonStatusResources.resx
├── PersonStatusResources.en.resx
└── PersonStatusResources.pt-BR.resx
Armazenar o valor de uma enumeração como string é válido e pode melhorar a legibilidade, mas tende a ser menos eficiente em termos de desempenho e armazenamento.
Por isso, recomendamos armazenar o valor como int, e, para manter a integridade referencial e facilitar a manutenção, criar uma entidade (tabela) auxiliar onde a chave primária corresponda ao valor da enumeração.
Após definir as enumerações, utilize o Lino para gerar e aplicar as Migrations, garantindo que o banco de dados reflita o modelo de domínio. Veja detalhes na seção Camada de Persistência.
Fluxo atual de evolução
Além do comando interativo, use parâmetros quando a localização e o nome já estiverem definidos:
lino enumeration new --name <EnumerationName> --service <ServiceName> --module <ModuleName> lino enumeration edit --service <ServiceName> --module <ModuleName> --enumeration <EnumerationName> lino enumeration list --service <ServiceName> --module <ModuleName>
Enumeration, entidade ou configuração
| Use enumeration | Use entidade ou configuração |
|---|---|
| Status de pedido, estado de publicação, tipo técnico de integração | Categoria administrável por usuário, motivo configurável por tenant, fornecedor cadastrado no backoffice |
| Valores versionados junto com o código | Valores alterados em runtime ou controlados por permissões |
| Regra simples ou comportamento pequeno por valor | Ciclo de vida, auditoria, tradução dinâmica ou relacionamento próprio |
Se a enumeration expõe nome de exibição, o Lino pode gerar resources para labels localizados. Se for persistida ou usada como seed, revise migrations após adicionar, remover ou renomear valores. A consistência entre código, banco e UI faz parte da mudança de domínio.
