如何在CQRS .NET API中创建实体仓库且避免持久层依赖领域层?
解决CQRS .NET API分层依赖的可行方案
针对你遇到的「持久层需操作领域实体但不想直接依赖领域层」的问题,以下是几个落地性强的解决方案:
方案1:优化你提出的实体契约分层思路
不用单独新建独立的Domain.EntityInterfaces层,而是在领域层内部拆分出一个轻量契约子项目(比如Domain.Contracts),只存放实体的核心标识和公共契约,不包含领域业务逻辑:
- 具体步骤:
- 创建
Domain.Contracts项目,定义基础实体接口:public interface IEntity { int Id { get; } } - 领域层的
User实体实现该接口:public class User : IEntity { public int Id { get; private set; } // 其他领域属性、业务方法 } - 应用层的仓库接口基于
IEntity定义:public interface IUserRepository { Task<IEntity> GetByIdAsync(int id); Task AddAsync(IEntity entity); } - 持久层仅引用
Domain.Contracts项目,实现仓库接口时,内部通过ORM(比如EF Core)完成数据库映射,将数据库实体转换为IEntity返回给应用层。 - 应用层同时引用领域层和
Domain.Contracts,在拿到IEntity后,转换为完整的User实体用于领域逻辑处理。
- 创建
这个方案的优势是既保持了领域实体的契约一致性,又避免了持久层直接依赖完整领域层。
方案2:用DTO做中间层完全隔离依赖
彻底切断持久层与领域层的关联,通过应用层定义的DTO作为数据传输载体:
- 具体步骤:
- 在应用层定义
UserDto(仅包含持久化所需字段)和仓库接口:public class UserDto { public int Id { get; set; } public string Username { get; set; } // 其他需要持久化的字段 } public interface IUserRepository { Task<UserDto> GetByIdAsync(int id); Task AddAsync(UserDto dto); } - 持久层实现仓库接口,直接操作
UserDto与数据库交互(比如用EF Core将UserDto映射为数据库表实体,或用ADO.NET直接读写)。 - 应用层负责领域实体与DTO的转换:调用仓库前把
User实体转成UserDto,从仓库拿到UserDto后再转成User实体供领域逻辑使用。
- 在应用层定义
这个方案的优势是完全隔离了持久层与领域层的依赖,适合对分层隔离要求极高的场景。
方案3:调整仓库接口的定义位置(符合DDD规范)
在标准DDD分层中,仓库接口应该定义在领域层,这是依赖倒置原则的正确应用:领域逻辑需要依赖仓库来获取/持久化实体,所以接口归属于领域层;应用层依赖领域层调用接口;持久层依赖领域层实现接口。
如果你坚持要让应用层作为唯一直接访问领域层的模块,可以调整为:
- 在应用层创建
Application.DomainContracts子项目,定义实体契约和仓库接口:public interface IUserEntity { int Id { get; } string Username { get; } } public interface IUserRepository { Task<IUserEntity> GetByIdAsync(int id); } - 领域层引用
Application.DomainContracts,让User实体实现IUserEntity接口。 - 持久层引用
Application.DomainContracts,实现IUserRepository时,内部用自己的数据库实体做映射,再转换为IUserEntity返回给应用层。
这个方案既符合DDD的依赖逻辑,又满足了你「仅应用层直接访问领域层」的要求。
内容的提问来源于stack exchange,提问作者Dodge
相关产品推荐
相关产品推荐

