DDD,处理依赖关系
无聊的介绍:
我知道 - DDD 与技术无关。在我看来,DDD 就是与产品负责人一起创建通用语言,并以一种简单且结构化的方式将其反映到代码中,这样就不会被误解或丢失。
但这里出现了一个悖论——为了摆脱领域模型中应用程序的技术方面,它变得有点技术——至少从设计的角度来看。
上次我尝试遵循 DDD - 它最终将域对象之外的整个逻辑变成了周围的“神奇”服务和贫乏的域模型。
我学到了一些新的忍者技巧,想知道这次我是否能对付歌利亚。
问题:
class store : aggregateRoot {
products;
addProduct(product){
if (new FreshSpecification.IsSatisfiedBy(product))
products.add(product);
}
}
class product : entity {
productType;
date producedOn;
}
class productTypeValidityTerm : aggregateRoot {
productType;
days;
}
FreshSpecification
应该指定产品是否没有气味。为了做到这一点 - 它应该检查产品类型,找出产品的新鲜天数,并将其与ProducedOn 进行比较。有点简单。
但问题来了 - productTypeValidityTerm
和 productType
应该由客户端管理。他应该能够自由添加/修改这些。因为我无法直接从产品遍历到 productTypeValidityTerm
,所以我需要以某种方式通过 productType
查询它们。
以前 - 我会创建类似 ProductService
的东西,它通过构造函数接收必要的存储库,查询术语,执行一些额外的巫术并返回布尔值(使相关逻辑远离对象本身并将其分散到谁知道在哪里)。
我认为这样做可能是可以接受的:
addProduct(product, productTypeValidityTermRepository){...}
但是话又说回来 - 我无法自由地从下面的多个规范中编写规范,这是它们的主要优点之一。
所以 - 问题是,在哪里做呢?商店如何了解条款?
Boring intro:
I know - DDD isn't about technology. As i see it - DDD is all about creating ubiquitous language with product owner and reflecting it into code in such a simple and structured manner, that it just can't be misinterpreted or lost.
But here comes a paradox into play - in order to get rid of technical side of application in domain model, it gets kind a technical - at least from design perspective.
Last time i tried to follow DDD - it ended up with whole logic outside of domain objects into 'magic' services all around and anemic domain model.
I've learnt some new ninja tricks and wondering if I could handle Goliath this time.
Problem:
class store : aggregateRoot {
products;
addProduct(product){
if (new FreshSpecification.IsSatisfiedBy(product))
products.add(product);
}
}
class product : entity {
productType;
date producedOn;
}
class productTypeValidityTerm : aggregateRoot {
productType;
days;
}
FreshSpecification
is supposed to specify if product does not smell. In order to do that - it should check type of product, find by it days how long product is fresh and compare it with producedOn
. Kind a simple.
But here comes problem - productTypeValidityTerm
and productType
are supposed to be managed by client. He should be able to freely add/modify those. Because I can't traverse from product to productTypeValidityTerm
directly, i need to somehow query them by productType
.
Previously - i would create something like ProductService
that receives necessary repositories through constructor, queries terms, performs some additional voodoo and returns boolean (taking relevant logic further away from object itself and scattering it who knows where).
I thought that it might be acceptable to do something like this:
addProduct(product, productTypeValidityTermRepository){...}
But then again - i couldn't compose specification from multiple specifications underneath freely what's one of their main advantages.
So - the question is, where to do that? How store can be aware of terms?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
冒着过度简化事情的风险:为什么不让产品“知道”产品是否新鲜这一事实呢? Store(或任何其他类型的相关对象)不必知道如何确定产品是否仍然新鲜;换句话说,
Store
不应该知道诸如freshSpecification
或productTypeValidityTerm
之类的东西存在的事实,它应该简单地检查Product .IsFresh
(或者可能是其他一些更符合现实世界的名称,例如ShouldbeSoldBy
、ExpiresAfter
等)。然后,产品可以知道如何通过注入存储库依赖项来实际检索productTypeValidityTerm
。在我看来,您正在将行为外化,而这些行为应该是您的域聚合/实体所固有的,最终(再次)导致贫血的域模型。
当然,在更复杂的情况下,新鲜度取决于上下文(例如,在廉价商店中可以接受的东西不被认为值得在高级商店中销售),您需要从产品和商店中外部化整个行为,并创建一个完全不同的类型来模拟这种特定的行为。
在评论后添加
对于我提到的简单场景,这些内容是这样的:将
FreshSpec
作为 Product 聚合的一部分,这允许ProductRepository
(构造函数-在此处注入)以在需要时(惰性)加载它。商店不知道这些内部情况:它只关心产品是否新鲜:
With the risk of oversimplifying things: why not make the fact whether a
Product
is fresh something a product "knows"? AStore
(or any other kind of related object) should not have to know how to determine whether a product is still fresh; in other words, the fact that something likefreshSpecification
orproductTypeValidityTerm
even exist should not be known toStore
, it should simply checkProduct.IsFresh
(or possibly some other name that aligns better with the real world, likeShouldbeSoldBy
,ExpiresAfter
, etc.). The product could then be aware how to actually retrieve theprotductTypeValidityTerm
by injecting the repository dependency.It sounds to me like you are externalizing behavior which should be intrinsic to your domain aggregates/entities, eventually leading (again) to an anemic domain model.
Of course, in a more complicated scenario, where freshness depends on context (e.g., what's acceptable in a budget store is not deemed worthy for sale at a premium outlet) you'd need to externalize the entire behavior, both from product and from store, and create a different type altogether to model this particular behavior.
Added after comment
Something along these lines for the simple scenario I mentioned: make the
FreshSpec
part of the Product aggregate, which allows theProductRepository
(constructor-injected here) to (lazy) load it when needed.The store doesn't know about these internals: all it cares about is whether or not the product is fresh: