领域和持久层,与存储库的关注点分离

发布于 2025-01-11 20:00:54 字数 1154 浏览 1 评论 0原文

域层

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我只土不豪 2025-01-18 20:00:54

无论如何,我都不是 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.

[浮城] 2025-01-18 20:00:54

您可以按照清洁架构 实践。从这个意义上说,存储库接口不属于存储库或数据访问层,而是属于业务逻辑。您应该能够设计和编码应用程序的核心,而无需实现真正的存储库,直到最后。

这在您的示例中具有多种含义:

  1. 不要定义通用存储库接口。由于接口属于业务逻辑,因此您将为每个实体定义存储库接口。并非所有实体都需要存储库中的相同方法,因此通用接口通常不是一个好主意。

  2. 如果向域对象添加基类,这样做是因为它在域对象设计中有意义,而不是因为持久层需要它。

  3. 您的存储库接口将获取并传递域对象,这是核心所需要的。无论存储库接口如何处理它们,从核心的角度来看都是无关紧要的。

对 Core 进行编码和单元测试后,您可以创建在对 Core 进行编码时定义的接口的实际实现。因此,在实现 Car 存储库时,您需要评估 2 个选项:

  1. 实体框架:EF 允许您以非侵入性方式将域对象直接映射到数据库(使用 IEntityTypeConfiguration)。因此,您可以使用域对象在 DbContext 中定义 DbSet,并且您的存储库实现变得非常简单,无需额外的映射。在某些情况下,如果您想要或需要与域对象非常不同的数据库结构,您可能仍然需要将域对象映射到数据对象。

  2. 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:

  1. 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.

  2. 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.

  3. 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:

  1. 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.

  2. 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.

一口甜 2025-01-18 20:00:54

在我看来,最好有两个不同的课程。一种用于领域逻辑,另一种用于持久性模型。为什么?这只是我的意见,但因为将来重构代码更简单、更容易。

在我看来,这可以被认为是一个非常主观的问题……我的建议是基于 DDD 实践。

在 DDD 的上下文中,存储库层应该将持久性实体转换为正确的业务实体并返回它。

否则每个服务都会有关于持久性的知识。

Sapiens Works 的更多解释

  • 领域模型模拟现实生活中的问题和解决方案,它模拟
    行为。

  • 持久性模型模拟数据的存储内容和方式,它模拟
    存储结构。看?他们有截然不同的目的。这
    域是应用程序存在和万事万物引力的原因
    围绕它。域不应该依赖于任何东西,特别是不依赖于
    持久性实施细节,如 EF 或 NH。当你设计
    领域实体,他们对持久性一无所知。
    持久性、数据库不存在。

还有一点:

当您设计持久层时,该层服务于域
并取决于它。所以持久化需要了解域
但反之则不然。您设计用于存储的持久性实体
目的并匹配 ORM 的约束(例如使所有
属性虚拟)。所以你将拥有域实体和持久性
实体,每个实体都有自己不同的目的和实现。

是的,它们确实相似,有时甚至可以相同(当域
很简单)但这只是巧合。每一个
当你在存储库中建模或使用 ORM 时,你
正在对持久性而不是域进行建模。埃里克是有原因的
埃文斯建议使用域启动应用程序并忽略
任何与数据库相关的内容:域不应被污染
基础设施细节,因为大多数人开始一切
采用以数据库为中心的方法。一旦你开始使用数据库,
一切事物都会围绕它发展,并受到它的制约。但
您不是为数据库构建应用程序,而是为
域,数据库只是一个Persistence的实现细节。

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:

  • The Domain Model models real-life problems and solutions, it models
    BEHAVIOR.

  • The Persistence Model models what and how data is stored, it models
    STORAGE STRUCTURE. See? They have pretty different purposes. The
    domain is the reason the application exists and everything gravitates
    around it. The domain should not depend on anything,especially not on
    a persistence IMPLEMENTATION DETAIL like EF or NH. When you design the
    Domain Entities, they don't know anything about persistence.
    Persistence, database, doesn't exist.

and a little bit more:

When you design the Persistence Layer, that layer serves the Domain
and depends on it. So the persistence needs to know about the domain
but not vice-versa. You design the persistence entities for storage
purposes and to match the ORM's constraints (like making all the
properties virtual). So you'll have Domain Entities and Persistence
Entities, each with their own different purposes and implementations.

Yes, they do resemble and sometimes can be identical (when the domain
is very simple) but that's nothing more than a mere coincidence. Every
time you're modeling something in a repository or using an ORM , you
are modelling the persistence NOT the domain. There is a reason Eric
Evans recommends to start the app with the domain and to IGNORE
anything db related: the Domain should not be tainted with
infrastructure details, because most of the people start everything
with a database centric approach. And once you start with the db,
everything will evolve around it and will be constrained by it. But
you don't build the application for the database, you build it for the
Domain, the database is just a Persistence implementation detail.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文