Managing Data Persistence
In this section, you will learn how Lino configures Entity Framework Core so that the domain layer remains fully isolated and the database migration processes are predictable and controlled — whether in a traditional (monolithic) service or a modular service.
When creating a service via the lino new service command, the CLI asks for two essential decisions:
- Architecture — traditional or modular service.
- Data provider —
SqlServer
orPostgreSql
(other providers will be added in future versions).
In traditional (monolithic) or modular (modular monolith) services, the database is unique per service. However, in modular services, each module is mapped to a distinct schema within the same database. This allows each bounded context to maintain a separate logical namespace, ensuring functional isolation, independent versioning, and more organized and safer migrations.
Entity Type Configurations
Lino follows the Persistence‑Ignorant principle: domain entities do not know infrastructure details.
To achieve this, all ORM mapping is moved to classes that implement IEntityTypeConfiguration<T>
, located in Configurations/<EntityName>Configuration.cs
.
- Each entity has a dedicated configuration file.
- Global conventions (e.g.,
decimal(18,2)
, collation,DateTime
asutc
) can be centralized inModelConfiguration
. - Configurations are applied in the
OnModelCreating
method of theDbContext
viamodelBuilder.ApplyConfigurationsFromAssembly(...)
.
DbContexts
The DbContext
represents the application's unit of transaction. Lino automatically generates:
- Traditional service — a single
AppDbContext
containing allDbSet<TEntity>
. - Modular service — a
<Module>DbContext
for each bounded context. This way, each module compiles faster, has shorter migration cycles, and reduces the risk of merge conflicts.
All DbContext
instances are registered in <Module>/Infrastructure.Persistence
, exposed via IUnitOfWork
, and resolved through dependency injection
.
Repositories
Concrete repositories reside in <Module>/Infrastructure.Persistence.Repositories
and implement the interfaces defined in the <Module>/Domain.Repositories
namespace.
Each repository:
- Encapsulates complex queries (LINQ,
FromSql
, DTO projections). - Exposes only the methods necessary for the domain (Aggregate Root-centric).
Unit of Work
The Unit of Work acts as a transactional boundary that coordinates multiple repositories, ensuring that all operations are executed atomically.
In simple scenarios, the DbContext
itself is often sufficient. However, Lino generates a dedicated implementation (UnitOfWork
) that offers greater control and flexibility.
- Allows manual control of transactions using
BeginTransaction
,Commit
, andRollback
, when needed. - Publishes domain events during the current transaction, ensuring they occur within the same
commit
cycle. - Persists integration events to the outbox, following the Transaction Outbox Pattern, ensuring reliable delivery in distributed architecture scenarios (when applicable).
Managing Migrations
Lino fully simplifies and automates the Database Migrations process with Entity Framework Core, eliminating repetitive tasks and common risks of inconsistency. To create a new migration, run the following command:
lino database migrations add
When running the command, an interactive wizard will prompt you for the following information:
- Service — The name of the project where the migration will be applied.
- Module — (only for modular services) the module to which the change belongs.
-
Migration Name — Enter a description of the change (e.g.,
AddCustomerIsActive
). Lino will automatically handle the current version and incremental ordering when generating the.sql
script, following the pattern:/Scripts/v1.2.3/001_AddCustomerIsActive.sql
.
.cs
files generated by Entity Framework, Lino also automatically creates the corresponding .sql
script containing the DDL instructions.
This facilitates manual execution of changes or review by infrastructure teams and DBAs.