Modeling the domain
At the heart of any domain-driven application is the model that represents the systemβs core knowledge and business rules. Modeling the domain well means translating real-world concepts into expressive, cohesive, and consistent software structures.
Entities
An entity is an object defined primarily by its identity and not just by its attributes. Even if the attributes change over time, the identity of an entity remains the same.
Main characteristics:
- Has a unique identity (usually an
Id
). - What matters is who the entity is, not just what it contains.
- Its attributes may change over time.
Creating an entity with Lino
To create a new entity using Lino, run:
lino entity new
The CLI wizard will ask for:
- Service β The service where the entity will be created.
- Module β The module where the entity will be created (only in modular services).
- Entity name β The name used in the domain and in the database table.
Then, you will define the fields that make up the entity, configuring each one.
Available field types
Type | Description | Range / Notes |
---|---|---|
short | 16-bit integer | -32,768 β 32,767 |
int | 32-bit integer | -2,147,483,648 β 2,147,483,647 |
long | 64-bit integer | -9,223,372,036,854,775,808 β 9,223,372,036,854,775,807 |
string | Text | Up to ~2 billion characters |
bool | Boolean value | true or false |
Guid | Globally unique identifier | Distributed uniqueness |
decimal | High-precision decimal number | Ideal for monetary values |
float | Floating point (32-bit) | β 6β9 digits of precision |
double | Floating point (64-bit) | β 15β17 digits of precision |
DateTime | Date and time | Includes time zone |
DateOnly | Date only (C# 10+) | β |
TimeOnly | Time only (C# 10+) | β |
Entity | Reference to another entity | 1:1 or 1:N |
Value Object | Immutable value object | e.g., Address, CPF |
Enum | Enumeration | Fixed set of values |
List<Entity> | List of entities | 1:N |
ManyToMany | Many-to-many | Requires a join table |
Example
Creating the entity Person
:
ββββββ¬βββββ¬ββββββββββββββββ¬βββββββββ¬βββββββββ¬ββββββββββββ¬βββββββββββββββββ β PK β FK β Property name β Type β Length β Required β Auto-increment β ββββββΌβββββΌββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββΌβββββββββββββββββ€ β x β β Id β int β β x β x β ββββββΌβββββΌββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββΌβββββββββββββββββ€ β β β Name β string β 100 β x β β ββββββ΄βββββ΄ββββββββββββββββ΄βββββββββ΄βββββββββ΄ββββββββββββ΄βββββββββββββββββ
Structure generated by Lino:
MyApp/ βββ src/ βββ Services/ βββ MyService/ βββ Domain/ βββ MyApp.MyService.Domain.csproj βββ Aggregates/ βββ People/ βββ Person.cs βββ Errors/ β βββ PersonErrors.cs βββ Repositories/ β βββ IPersonRepository.cs βββ Resources/ βββ Person/ βββ PersonResources.resx βββ PersonResources.en.resx βββ PersonResources.pt-BR.resx
After defining your entities, use Lino itself to manage the Migrations and keep the database synchronized. We will cover this process in detail in the Persistence Layer section.
Value Objects
A value object represents a domain concept defined only by its attributes β it does not have its own identity. Two value objects are considered equal if all their values are equal.
Main characteristics:
- Immutable after creation.
- Do not have an
Id
.
Creating a value object with Lino
Run:
lino value-object new
The CLI will ask for:
- Service β Service in which the object will be created.
- Module β Module in which the object will be created (only in modular services).
- Location β Domain root or specific aggregate.
- Value object name.
Then, define the fields that make up the object.
Available field types
Type | Description | Notes |
---|---|---|
short | 16-bit integer | -32,768 β 32,767 |
int | 32-bit integer | -2,147,483,648 β 2,147,483,647 |
long | 64-bit integer | -9,223,372,036,854,775,808 β 9,223,372,036,854,775,807 |
string | Text | Up to ~2 billion characters |
bool | Boolean | true /false |
decimal | Precise decimal | Monetary values |
float | Floating point (32-bit) | β 6β9 digits |
double | Floating point (64-bit) | β 15β17 digits |
DateTime | Date/time | Includes timezone |
DateOnly | Date only | C# 10+ |
TimeOnly | Time only | C# 10+ |
Example
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 β βββββββββββββββββ΄βββββββββ΄βββββββββ΄ββββββββββββ
Generated file structure (aggregate Person
):
MyApp/ βββ src/ βββ Services/ βββ MyService/ βββ Domain/ βββ MyApp.MyService.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
As with entities, Migrations can be managed by Lino to keep the data model in sync.
Enumerations
In DDD, enumerations can go beyond the traditional C# enum
. They can be rich objects
that represent fixed states, containing validations, helper methods, and even behavior.
Motivation:
- C#
enum
s are limited to an integer or string value. - Modeling an Enumeration as a class offers greater flexibility and expressiveness.
Main characteristics:
- They are classes inheriting from a common base, encapsulating
Id
andName
. - Allow adding validations, helper methods, and behavior.
Creating an enumeration with Lino
Run:
lino enum new
The assistant will ask for:
- Service.
- Module (if applicable).
- Location β Root of the domain or aggregate.
- Enumeration name.
- Type β Traditional
enum
or Smart Enum (class
). - Storage β
int
orstring
in the database.
Example
Enumeration PersonStatus
:
βββββββββ¬ββββββββββββ¬βββββββββββββββ β Value β Name β Display Name β βββββββββΌββββββββββββΌβββββββββββββββ€ β 1 β Active β Active β βββββββββΌββββββββββββΌβββββββββββββββ€ β 2 β Inactive β Inactive β βββββββββΌββββββββββββΌβββββββββββββββ€ β 3 β Suspended β Suspended β βββββββββΌββββββββββββΌβββββββββββββββ€ β 4 β Deleted β Deleted β βββββββββ΄ββββββββββββ΄βββββββββββββββ
Generated structure:
MyApp/ βββ src/ βββ Services/ βββ MyService/ βββ Domain/ βββ MyApp.MyService.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
Storing an enumeration value as a string
is valid and can improve readability, but tends to be less efficient in terms of performance and storage.
Therefore, we recommend storing the value as an int
, and to maintain referential integrity and ease maintenance,
create an auxiliary entity (table) where the primary key corresponds to the enumeration value.
After defining your enumerations, use Lino to generate and apply Migrations, ensuring the database reflects the domain model. See details in the Persistence Layer section.