让我们举一个产品分类的例子。所有产品都需要归类为蔬菜或不分类。业务逻辑是,如果该产品来自A,B&C公司,则可以将其归类为蔬菜。如果产品不是来自这些公司,它们就不是蔬菜。有数以百万计的产品。这可以在具有几行代码的存储过程中完成。如果同步完成,则操作可能只需几秒钟。
据我了解,DDD 违背了将逻辑放入存储过程的想法。该逻辑可以作为产品的行为,可以根据谁是来源进行自我分类。为此,需要将所有百万个产品读入内存,处理,然后将其保存回数据库。
这里的问题是此操作需要大量内存。如果操作是在 50,000 这样的卡盘中完成的,存储库必须首先弄清楚产品需要如何分类,并应该告诉域长时间运行的操作必须分块进行。当然,这种方法将花费更多的时间和糟糕的用户体验,因为用户必须等待的时间比进程比存储过程花费的时间更长。
对于长时间运行的进程,DDD 的合理方法是什么?延迟是否在预期之中,因此应用必须通知用户分类需要时间,并在完成时让用户知道?并且不应该使用存储过程,而是具有域的逻辑部分。
更新
为了增加一些清晰度,此分类过程经常进行。应用程序必须支持分类过程,而不是 ETL,或者不能等待更长时间。这就是我试图找到使用存储过程与 DDD 之间的权衡的原因。
另请注意,它不是查询,而是命令。该命令可以称为 ClassifyAllProductsCommand()。运行此命令时,以前没有分类。分类后,系统的其他用户应看到新的分类。例如,产品 A 分类为不可用,分类后它可以分类为蔬菜或肉类。
分类是一件有趣的事情。 这是另一回事。 分类永远不应该作为结构来实现......但那是另一回事:)
您的分类甚至可以被视为边界上下文,就像报告可能被视为边界上下文一样。 因此,您可能希望单独处理分类。 您的分类不是聚合根。 它起着辅助作用。 如果它对域建模的一致性没有影响,它甚至不一定是Product
聚合的一部分。 它可以被添加,甚至可以独立更改(不是批量更改),但如果它用于确定聚合的有效性,那么您的分类子系统将不得不考虑到这一点。
请记住,这不是 DDD 与存储过程的问题。 您正在对数据存储执行查询。 这是通过存储过程完成还是动态完成,都不会影响您的决定。 没有什么可以阻止ProductRepository
调用存储过程。
您可以让分类子系统仍执行 SP 或直接使用 DML。 但是,这不一定是您域的一部分。 您肯定不希望对每个产品进行单独分类,如果它是经常发生的事情并且作为批量操作。 如果您当前的设计规定这些是批量操作,请保留它们,不要强制它们进入令人望而却步的 DDD 结构。
这是一种设计选择,有时对单个项目进行更改是没有意义的。 当然,您的目标应该是一次处理一个聚合,但是像报告或分类之类的东西是另一种并不总是完全适合领域驱动设计思维的动物。
我认为你混淆了DDD。 如果要查找Vegetable
类型 Products
,您将调用一个服务来检索特定Company
的Products
。 无需将所有产品加载到内存中。
,只是意味着围绕业务域设计应用程序,而不是从数据库表的集合向上设计(如以数据为中心的方法)。
相反,最终在应用程序中完成的数据关联(联接)更多,而在整体式存储过程中完成的数据关联(联接)更少。 这会将所有业务逻辑移动到应用程序中,而不是在持久性设备(数据库)中,这很有意义。
此外,如果您拒绝自己的巨大表联接,那么您也会仔细考虑传统上导致数据库大量开销并最终转向更好的设计的事情,例如创建单独的报告数据库、消息总线、异步任务等。
编辑
这似乎是DDD中的常用短语,但"这取决于您的特定域"。
在不知道细节的情况下,我想知道这些分类发生的频率。 它们可以在创建Products
时完成吗? 它们是经常还是很少,有计划还是不可预测?
如果分类是通用的,并且必须在所有一百万个产品中进行,那么最好为Product
创建一个较小的模型,也许只有SmallProduct.Id
和SmallProduct.CompanyId
(可能将其命名为更好的模型)。 然后,数据将此较小的集合缓存在内存中并对其执行操作。
如果检查产品是否是Vegetable
很常见,并且只是几种可能的分类之一,则最好在自己的表中Classifications
,并有一个链接表将它们链接到Products
。 然后,问题就变成了一次性数据设置问题。
在使用文档数据库的极少数情况下,您可以将这些分类存储在Product
对象本身的集合中。
聚合根时,你正在解释"分类",包含产品(作为实体)。
老实说,这感觉不是一个好的设计决策(我可能是错的,取决于需求细节)。
如果您将产品视为聚合根(包含供应商、折扣等)怎么办?在这种情况下,您一次只需要加载一个产品。
如果分类/供应商具有复杂的域,则应考虑为此使用单独的边界上下文。
另外,在您的评论中:
为了增加一些清晰度,此分类过程经常进行。应用程序必须支持分类过程,而不是 ETL,或者不能等待更长时间。这就是我试图找到使用存储过程与 DDD 之间的权衡的原因。
真?当供应商有更新时,您不能触发事件并让产品服务更新分类吗?用户将具有不一致的状态(例如。"未定义"类别"),几秒钟/分钟。没那么糟糕,是吗?
但是,如果您谈论的是批处理作业,那么请务必使用存储过程。