领域和持久层,与存储库的关注点分离
域层
public interface IRepository<T> where T : MyEntity
{
}
public class Car : MyEntity
{
...
}
public interface IMyCarRepository : IRepository<Car>
{
}
持久层
public class Repository<T> : IRepository<T> where T : MyEntity
{
public DbContext Context;
public DbSet<T> DbSet;
public Repository(DbContext db)
{
Context = context;
DbSet = context.Set<T>();
}
... Crud method implementation from the IRepository
}
public class CarRepository : Repository<Car>, ICarRepository
{
private CarContext _db;
public CarRepository(CarContext db) : base(context)
{
_db = db;
}
}
这工作正常,但正如您所看到的,CarRepository 使用的是域中的汽车模型。 假设我想更改汽车模型以便使用 MyEntityMongo
我会更改 MyEntityMongo
而不是 MyEntity
public class MyEntityMongo
{
[BsonId]
public string Id {get; set;}
}
但这个 [BsonId]
会将 MongoDB.Bson.Serialization.Attributes
带来我不想做的对域的依赖。
在不将 Mongo 库引入域(或任何其他相关数据库)的情况下,您将如何做到这一点?目标是保持域和持久层之间的关注点清晰分离?
Domain layer
public interface IRepository<T> where T : MyEntity
{
}
public class Car : MyEntity
{
...
}
public interface IMyCarRepository : IRepository<Car>
{
}
Persistance layer
public class Repository<T> : IRepository<T> where T : MyEntity
{
public DbContext Context;
public DbSet<T> DbSet;
public Repository(DbContext db)
{
Context = context;
DbSet = context.Set<T>();
}
... Crud method implementation from the IRepository
}
public class CarRepository : Repository<Car>, ICarRepository
{
private CarContext _db;
public CarRepository(CarContext db) : base(context)
{
_db = db;
}
}
This works fine but as you can see CarRepository is using Car model from the Domain.
Let's say that I want to change Car model in order to use MyEntityMongo
I would change MyEntityMongo
instead of MyEntity
public class MyEntityMongo
{
[BsonId]
public string Id {get; set;}
}
but this [BsonId]
will bring MongoDB.Bson.Serialization.Attributes
as a dependency into Domain which I don't want to do.
How would you do this without introducing Mongo library into a domain (or any other db related) The goal is to keep a clean separation of concerns between domain and persistance layer?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
无论如何,我都不是 C# 开发人员,但我会考虑将持久性实体与域实体区分开来,即 MyEntity 是持久性表示,而其他类型是实体的域表示。
那么,存储库本质上是加载和保存持久性表示到数据库的持久性基础设施与域逻辑之间的适配器(或者甚至可能是反腐败层......);作为适配器,它在逻辑上依赖于两者。
I'm not a C# dev by any means, but I would consider having persistence entities be distinct from domain entities, i.e.
MyEntity
is a persistence representation while some other type is the domain representation of the entity.The repository is then essentially an adapter (or perhaps even an anti-corruption layer...) between the persistence infrastructure of loading and saving persistence representations to the database and the domain logic; as an adapter it logically depends on both.
您可以按照清洁架构 实践。从这个意义上说,存储库接口不属于存储库或数据访问层,而是属于业务逻辑。您应该能够设计和编码应用程序的核心,而无需实现真正的存储库,直到最后。
这在您的示例中具有多种含义:
不要定义通用存储库接口。由于接口属于业务逻辑,因此您将为每个实体定义存储库接口。并非所有实体都需要存储库中的相同方法,因此通用接口通常不是一个好主意。
如果向域对象添加基类,这样做是因为它在域对象设计中有意义,而不是因为持久层需要它。
您的存储库接口将获取并传递域对象,这是核心所需要的。无论存储库接口如何处理它们,从核心的角度来看都是无关紧要的。
对 Core 进行编码和单元测试后,您可以创建在对 Core 进行编码时定义的接口的实际实现。因此,在实现 Car 存储库时,您需要评估 2 个选项:
实体框架:EF 允许您以非侵入性方式将域对象直接映射到数据库(使用 IEntityTypeConfiguration)。因此,您可以使用域对象在 DbContext 中定义 DbSet,并且您的存储库实现变得非常简单,无需额外的映射。在某些情况下,如果您想要或需要与域对象非常不同的数据库结构,您可能仍然需要将域对象映射到数据对象。
MongoDb:Mongo 确实可以很好地处理具有映射属性的非常具体的数据模型。因此,您的 mongo 存储库会将域对象来回映射到 Mongo 对象。
You can implement repositories following Clean Architecture practices. In this sense, the repository interface does not belong to the repository or the Data Access Layer, but to the Business Logic. You should be able to design and code the Core of your application without implementing the real repositories until the end.
This has multiple implications in your example:
Don't define a generic repository interface. As the interface belongs to the business logic, you will define repository interfaces for each entity. Not all entities will need the same methods in the repositories, so a generic interface is normally a bad idea.
If you add a base class to your domain objects, do it because it makes sense in your domain object design, not because your persistence layer needs it.
Your repository interfaces will get and pass domain objects, which is what the Core needs. Whatever the repository interfaces do with them, is irrelevant from the Core's point of view.
Once your Core is coded and unit tested, you can create the real implementations of the interfaces that you have defined while coding the Core. So, while implementing the repository for Car you evaluate 2 options:
Entity Framework: EF allows you to map your Domain Objects directly to the DB with non-invasive ways (use IEntityTypeConfiguration). So, you can use your domain objects to define the DbSets in the DbContext and your repository implementation becomes quite simple with no extra mappings. In some cases, you might still have to map the domain object to a data object if you want or need a database structure very different from your domain object.
MongoDb: Mongo really works well with very specific data models with mapping attributes. So, your mongo repositories will map the domain objects to Mongo objects back and forth.
在我看来,最好有两个不同的课程。一种用于领域逻辑,另一种用于持久性模型。为什么?这只是我的意见,但因为将来重构代码更简单、更容易。
在我看来,这可以被认为是一个非常主观的问题……我的建议是基于 DDD 实践。
在 DDD 的上下文中,存储库层应该将持久性实体转换为正确的业务实体并返回它。
否则每个服务都会有关于持久性的知识。
Sapiens Works 的更多解释:
还有一点:
In my view, it is better to have 2 different classes. One is for the domain logic and another one is for Persistence Model. Why? It is just my opinion, but because it is simpler and easier to refactor code in future.
In my opinion, this can be considered a highly subjective question.... My suggestion is based on DDD practices.
In the context of
DDD
, the repository layer should convert persistence entities to the correct business entities and return it.Otherwise every service will have knowledge about persistence.
Some more explanations from Sapiens Works:
and a little bit more: