管理数据持久性
在本节中,您将学习 Lino 如何配置 Entity Framework Core,使领域层保持完全隔离,数据库迁移过程可预测且可控 — 无论是在传统的单体服务还是模块化服务中。
通过 lino new service 命令创建服务时,CLI 会请求两个关键决策:
- 架构 — 传统服务或模块化服务。
- 数据提供程序 —
SqlServer
或PostgreSql
(未来版本将添加更多提供程序)。
在传统(单体)服务或模块化(模块化单体)服务中,每个服务使用单一数据库。然而,在模块化服务中,每个模块都映射到同一数据库内的不同schema中。 这允许每个bounded context保持独立的逻辑命名空间,确保功能隔离、独立版本控制和更有序且安全的迁移。
实体类型配置
Lino 遵循 Persistence‑Ignorant 原则:领域实体不涉及基础设施的细节。
为此,所有 ORM 映射都转移到了实现 IEntityTypeConfiguration<T>
的类中,位于 Configurations/<EntityName>Configuration.cs
。
- 每个实体都有一个专门的配置文件。
- 全局约定(例如:
decimal(18,2)
、排序规则、将DateTime
设为utc
)可以集中管理在ModelConfiguration
中。 - 配置通过
DbContext
的OnModelCreating
方法中使用modelBuilder.ApplyConfigurationsFromAssembly(...)
应用。
DbContexts
DbContext
表示应用程序的事务单元。Lino 会自动生成:
- 传统服务 — 一个包含所有
DbSet<TEntity>
的单一AppDbContext
。 - 模块化服务 — 每个 bounded context 对应一个
<Module>DbContext
。这样每个模块编译更快,迁移周期更短,减少了合并冲突的风险。
所有 DbContext
都注册在 <Module>/Infrastructure.Persistence
,通过 IUnitOfWork
暴露,并通过 dependency injection
解析。
仓储
具体的仓储位于 <Module>/Infrastructure.Persistence.Repositories
,并实现了在命名空间 <Module>/Domain.Repositories
中定义的接口。
每个仓储:
- 封装复杂查询(LINQ,
FromSql
,DTO 投影)。 - 仅公开领域所需的方法(聚合根为中心)。
工作单元(Unit of Work)
工作单元 作为一个事务边界,用于协调多个仓储(Repository),确保所有操作以 原子性 的方式执行。
在简单场景中,DbContext
本身通常已经足够。然而,Lino 会生成一个专用实现(UnitOfWork
),提供更高的控制力和灵活性。
- 在需要时,可通过
BeginTransaction
、Commit
和Rollback
手动控制事务。 - 在当前事务中发布 领域事件,确保它们在同一个
commit
循环内触发。 - 将 集成事件 持久化到 Outbox 中,遵循 事务出站模式(Transaction Outbox Pattern),以确保在分布式架构下的可靠投递(如适用)。
管理迁移
Lino 使用 Entity Framework Core 完全简化并自动化了 数据库迁移 过程,消除了重复任务和常见的不一致风险。 要创建新的迁移,请运行以下命令:
lino database migrations add
运行命令后,交互式向导将提示您输入以下信息:
- 服务 — 应用迁移的项目名称。
- 模块 — (仅限模块化服务)所属更改的模块。
-
迁移名称 — 输入更改描述(例如:
AddCustomerIsActive
)。 Lino 会自动管理当前版本和递增顺序,生成符合以下模式的.sql
脚本:/Scripts/v1.2.3/001_AddCustomerIsActive.sql
。
.cs
文件外,Lino 还会自动创建包含 DDL 指令的相应 .sql
脚本。
这方便了手动执行更改或基础设施团队及DBA的审核。