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
shortInteiro de 16 bits-32 768  →  32 767
intInteiro de 32 bits-2 147 483 648  →  2 147 483 647
longInteiro de 64 bits-9 223 372 036 854 775 808  →  9 223 372 036 854 775 807
stringTextoAté ~2 bilhões de caracteres
boolValor booleanotrue ou false
GuidIdentificador global únicoUnicidade distribuída
decimalNúmero decimal de alta precisãoIdeal p/ valores monetários
floatPonto flutuante (32 bits)≈ 6–9 dígitos de precisão
doublePonto flutuante (64 bits)≈ 15–17 dígitos de precisão
DateTimeData e horaInclui fuso horário
DateOnlySó data (C# 10+)
TimeOnlySó hora (C# 10+)
EntityReferência a outra entidade1 : 1 ou 1 : N
Value ObjectValue Object imutávelEx.: Endereço, CPF
EnumEnumeraçãoConjunto fixo de valores
List<Entity>Lista de entidades1 : N
ManyToManyMuitos‑para‑muitosRequer 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

TipoDescriçãoObservações
shortInteiro de 16 bits-32 768  →  32 767
intInteiro de 32 bits-2 147 483 648  →  2 147 483 647
longInteiro de 64 bits-9 223 372 036 854 775 808  →  9 223 372 036 854 775 807
stringTextoAté ~2 bilhões de caracteres
boolBooleanotrue/false
decimalDecimal precisoValores monetários
floatPonto flutuante (32 bits)≈ 6–9 dígitos
doublePonto flutuante (64 bits)≈ 15–17 dígitos
DateTimeData/horaInclui fuso
DateOnlySomente dataC# 10+
TimeOnlySomente horaC# 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 enum do 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 Id e Nome.
  • 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.
  • Tipoenum tradicional ou Smart Enum (class).
  • Armazenamentoint ou string no 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 enumerationUse entidade ou configuração
Status de pedido, estado de publicação, tipo técnico de integraçãoCategoria administrável por usuário, motivo configurável por tenant, fornecedor cadastrado no backoffice
Valores versionados junto com o códigoValores alterados em runtime ou controlados por permissões
Regra simples ou comportamento pequeno por valorCiclo 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.

Ocorreu um erro não tratado. Recarregar 🗙