如何创建丰富的领域对象,同时保持持久性无知?
首先,我使用的是没有任何 ORM 框架的 Web 表单。
我一直在努力解决如何使我的域对象尽可能“智能”和“丰富”,而又不允许它们访问我的服务和存储库层。我最近的尝试是为在线商店创建礼券模型。
我看到的主要反复出现的问题是:
服务层中不断引入越来越多的逻辑。对存储库的所有调用都必须通过服务层,并且每次验证参数(例如 - 存在于数据库中等)。因此,我的服务层不断增长,但我的域对象只有一些简单的合同验证。即使对象验证也在服务层中,因为如果项目的 ID 为空,它将检查数据库以确保代码是唯一的。 IHMO,系统的使用者不应该关心他们需要的功能是否涉及持久性。
我有一个单独的 POCO,用于保存兑换礼券时的交易日志条目。我认为我应该将这些交易的列表或集合作为我的礼券模型的属性,但我仍然不确定何时应填充该属性。我是否在服务上添加一个单独的方法来按需将交易加载到对象中(例如 - LoadTransactions(gc object)),或者是否应该在请求现有礼券或礼券列表时自动加载交易(或者可能getGCs 中的一个选项也可以加载交易)
像“可用余额”这样的计算字段怎么样...我的对象上是否应该有这样的属性?每当我使用该对象时,我都需要不断更新该属性以确保它是最新的。现在我只有一个服务方法 GetBalanceByCode(gc code)。
即使是像兑换礼品券这样的操作基本上也是 100% 以数据为中心的(获取一些输入参数,验证它们并向数据库添加事务日志条目)。
First off, I am using web forms without any ORM framework.
I have been struggling with how to make my domain objects as "smart" and "rich" as they can be without allowing them access to my service and repository layer. My most recent attempt was in creating a model for gift certificates for a online store.
The main recurring issues that I am seeing is that:
More and more logic keeps being introduced in the service layer. All the calls to the repository must pass through the service layer and each time the parameters are validated (eg - exists in db, etc). As a result my service layer is growing, but my domain objects just have some simple contractual validations. Even object validation is in the service layer since if the ID of the item is null, it will check the db to ensure that the code is unique. IHMO, the consumer of the system should not care if the functionality they need deals with persistence or not.
I have a separate POCO for transaction log entries for when a gift certificate is redeemed. I assume that I should put a list or collection of these transactions as a property of my Gift Certificate model, but I am still unsure of when that property should be filled. Do I add a separate method on the service for loading the transactions into a object on demand (eg - LoadTransactions(gc object)) or should the transactions be automatically loaded any time a existing gift certificate or list of gift certificates are requested (or maybe a option in the getGCs to load transactions as well)
What about computed fields like "Available Balance"... should I even have properties like this on my object? Anytime I am working with the object, I will need to keep updating that property to insure it is up to date. Right now I simply have a service method GetBalanceByCode(gc code).
Even actions like redeeming a gift certificate are basically 100% data-centric (take some input parameters, validate them and add a transaction log entry to db).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
验证不是域模型元素的最佳候选。输入(我个人的偏好是它表示为命令)应该在应用程序服务级别进行验证。领域逻辑应该对业务的工作方式进行建模,并假设所有参数都是有效的。例如,域逻辑的良好候选者是计算:您希望将它们放在一个位置并对其进行良好的测试。
这种对象称为事件。您可以通过 Eric Evans “自蓝皮书以来学到的东西”演示文稿了解事件。事件基本上是一个不可变的实体。事件通常是单独聚合的,因为通常事件数量很多。通过使它们聚合,将它们作为其他对象集合的一部分延迟加载时不会有任何问题。
计算属性是一种自然适合域模型的逻辑,但是,更好的方法是每次计算值还是在对象更改时计算值并将其保留在数据库中,这是有争议的。
此操作将被建模为创建CertificateRedeemed 事件。此事件可能由证书聚合或其他一些对象创建。 Udi Dahan 的这篇博客文章可能会有所帮助
Validation is not the best candidate as domain model element. Input (my personal preference is that it's represented as commands) should be validated at application service level. Domain logic should model how business work and assume that all the arguments are valid. Good candidates for domain logic are computations for example: you want to have them in one single place and have them well tested.
This kind of object is known as Event. You can learn about Events from Eric Evans 'What I learnt since the Blue Book' presentation. Event is basically an entity which is immutable. Events are quite often aggregates on their own because usually there's lots of them. By making them aggregates, you don't have any problems with lazy loading them as part of other objects's collection.
Computed properties are kind of logic that naturally fits in domain model, however it's debatable if a better approach is to compute the value each time or compute it when object changes and persist it in the DB.
This action would be modeled as creating a CertificateRedeemed event. This event would be probably created by Certificate aggregate or some other object. This blog post by Udi Dahan can be helpful
鉴于领域模型非常主观,并且很大程度上依赖于您的……嗯,领域,这并不是一个完全容易回答的问题。听起来您实际上正在创建类似于 洋葱架构 的东西(和 Jeffery Palermo 描述的第 2 部分)。这不是一个糟糕的使用模式,尽管 DDD 纯粹主义者会告诉你它会导致“贫乏”的领域模型(其中你的领域对象基本上是没有行为的数据持有者)。问题是,这可能正是您的场景中所需要的。对于您正在做的事情来说,“完整、丰富”的域模型可能有点过分了(考虑到您的最后一个要点,听起来可能就是这种情况)。
您的系统可能根本不需要域模型。您可以使用一些视图模型(即描述视图的简单数据模型)来很好地服务,并让您的 UI 通过您的服务发送一些 DTO 以将数据放入数据库中。如果您发现某些东西需要更复杂的方法,那么您可以对该组件应用更丰富的域模型。另请记住,您的系统中不一定有一个域模型。可以而且在许多情况下应该有不同的模型来以不同的方式描述事物(通常分为限界上下文) 。 DDD 的总体目标是简化原本复杂的系统。如果它给你带来额外的复杂性,那么你可能会走很长的路。
This is not an entirely easy question to answer given the fact that domain models are very subjective, and rely a lot on your...well, domain. It sounds like you are actually creating something similar to The Onion Architecture (and Part 2) described by Jeffery Palermo. This is not a bad pattern to use, though DDD purists will tell you it leads to "anemic" domain models (where your domain objects are basically Data holders with no behavior). The thing is, that may be exactly what you need in your scenario. A "full, rich" domain model may be overkill for what you are doing (and given your last bullet point it sounds like that could be the case).
You may not need a domain model for your system at all. You could be well served with some View Models (that is simple data models to describe your view) and have your UI send some DTOs to through your services to put the data in the database. If you find something that requires a more complex approach, then you can apply a richer domain model to that component. Also remember that you don't necessarily have one domain model in your system. There can, and in many cases should, be different models that describe things differently (often grouped into Bounded Contexts). The overall goal of DDD is to simplify otherwise complex systems. If its causing you additional complexity, then you may be taking the long way round.
有一种名为 DCI (data-context-interactions) 的方法,它应该是替代方法到老派的OOP。尽管它没有明确解决持久性无知的问题,但你的问题让我想起了它,因为它处理类似的问题。
在 DCI 域对象中,它们是小型数据持有者,只有一点逻辑,就像您的情况一样,并且它们之间的交互是单独实现的。交互的算法不是通过多个对象的小方法传播的,而是集中在一个地方,这可能会使其更加清晰易懂。
我认为这仍然是一个学术问题,而不是我们明天应该开始实施的解决方案,但遇到这个问题的人可能会感兴趣。
There is an approach called DCI (data-context-interactions) which is supposed to be alternative to the old school OOP. Although it does not address explicitly the issue of persistence ignorance, your question brought it to my mind, because it deals with similar issues.
In DCI domain objects are small data-holders with only a little logic, like in your case, and interactions between them are implemented separately. The algorithm of interaction is not spread through small methods of several objects, but it is in one place, which might make it more lucid and understandable.
I think it is still rather academic thing than a solution we should start implementing tomorrow, but someone who comes across this question might be interested.