您的域模型对象应该有多少逻辑

时间:2009-01-23 16:47:17

标签: design-patterns n-tier-architecture

刚刚完成了Greg Young的阅读this post,他在谈论微软推荐使用哑数据传输对象的模式。他暗示,在Java社区中,事情正朝着另一个方向发展。

我的问题是你的实体对象应该有多少逻辑?我工作的理念(C#shop)是,如果你不能序列化它,不要把它放在实体中。

5 个答案:

答案 0 :(得分:17)

马特,

我会说你的店铺正在编写程序代码。我想清楚地表明,使用过程代码编写了许多大型系统(包括我曾经使用过的许多系统)都没有错。有时间和地点。

现在,程序代码在域模型中没有位置。如果你想使用一个更好的程序风格,但使用类似表模块或活动记录模式的方式。我认为在指导中如此具有破坏性,而不是缺乏OO,而是使用具有程序逻辑的域模型。

这会导致人们花费大量资源来构建域层(阻抗不匹配,思考处理时间来构建聚合,隔离,无处不在的语言等),而不会获得域层(通常可维护性)否则会带来的任何好处提供。换句话说,虽然您可以满足您的功能要求,但最终会花费大量预算而几乎没有回报。

现在回到“行为”,我想关注面向对象的问题,而不是“领域驱动设计”的观点。对象通常会封装某些状态,并且通常会暴露某些行为。

快速重申:封装状态,暴露行为

那么对象应该有什么样的行为?简而言之,它应该是对它所封装的状态进行操作的行为。在理想的行为OO世界中,状态永远不会从仅对象行为中暴露出来。如果我们开始看到像:

这样的代码,请在战术上加入代码
Customer c = GetCustomerFromRepository();
c.Status = CustomerStatuses.Deleted;
c.LastUpdated = DateTime.Now;
c.UpdatedBy = GetCurrentUser();
CustomerRepository.Save(c);

我们有违反SRP的行为......此代码是应该是客户对象行为的代码,因为客户对象的“责任”是。

封装客户的状态并公开行为。

因此我们可以看到使用Customer.Delete()方法会更好。 (是的,这是我知道的一个不好的例子......)

现在我们也可以通过使用TDD来实现这一目标。对于我们来说,使用行为所提供的接缝进行测试比接触所有状态的接缝要容易得多。原因是我不需要在测试中复制逻辑。客户端代码不关注删除的工作方式......它只关心客户公开行为。因此在我们的测试中,而不是断言c.State == CustomerStates.Deleted和c.UpdatedBy == GetCurrentUser()等等,我们只是断言使用模拟在客户接缝上调用了delete方法。

现在回到标题。应该在业务对象中的逻辑量是在其封装其状态的责任范围内的逻辑量。有时这很多,有时候不是。有些地方你也想要使用服务......一个很好的例子是协调给定行为的许多域对象之间的交互,但即使在这里,服务也应该在域对象上调用 behavior

这有助于澄清一些事情吗?

格雷格

答案 1 :(得分:4)

如果您将它们称为“域模型对象”,那么我将假设您正在引用Fowler的域模型模式。 http://martinfowler.com/eaaCatalog/domainModel.html

鉴于这种假设,那么你的问题的答案就是“所有业务逻辑”,因为这基本上是模式的定义。

不幸的是,“域模型”这个术语最近似乎已被淡化,只是意味着没有行为的数据对象模型。

如果您还没有这样做,我会鼓励您阅读PoEAA并确定您认为域逻辑在您的情况中所属的位置。如果您决定使用域模型,那么我建议您阅读Evan的DDD书籍,了解实体,价值对象和服务之间的差异。

希望有所帮助!

答案 2 :(得分:2)

最近,我一直在想要创建具有结构的域模型,只有那些对该模型具有通用性的行为(即可以在多个有界上下文中使用的行为)以及特定行为的扩展方法有界的背景。这使域模型保持接近DTO(对于那些喜欢它的人)并且将该域模型的使用限制为仅限于有界上下文中的允许行为。因此,这可能是一个中间响应的选择。 :)

答案 3 :(得分:1)

重点是如何定义逻辑。举一些例子:

  1. 我不会在Person实体中对函数getFullName()进行分类,它只是将某些字符串连接起来作为逻辑。
  2. 计算订单商品值更有可能成为逻辑。
  3. 做一些预订交易,我肯定会说是逻辑。
  4. 第1点和第2点会让我进入实体。第3点没有。所以我将逻辑定义为:

    • 执行任何持久性相关事务(读/写)的任何操作
    • 涉及任何其他(非直接相关,例如主要细节)实体的任何操作

    IMO,这些操作中的任何一个都不属于实体。

    现在,为什么/何时我不会将第1点和第2点类型的操作放入实体中?这是一个相当罕见的情况,但我不会这样做,只要存储在实体中的数据需要以某种方式解释才能被应用程序使用(例如,如果依赖于当前用户,字段X的内容具有不同的含义),这意味着实体的数据本身会产生一些逻辑。

答案 4 :(得分:0)

据我了解,与实体相关的所有业务逻辑都应该进入该实体。这包括根据系统的业务规则定义实体的行为或内部结构的任何逻辑。这不应该包括表示逻辑或持久性逻辑(显而易见的例外是Active Record设计模式),但应该包括诸如数据验证,实体关系,状态机和其他定义实体如何根据实际行为表现的事物。它试图建模的世界事物。

我试图看待它的方法是尝试让我的模型尽可能地恢复。如果要将模型移植到不同的系统(客户端代码(或使用实体的代码)可能不同),请始终考虑如何使用模型。如果功能不是实体的一部分,它仍然会遵循相同的业务规则吗?如果答案为否,那么该功能应该放在实体中。