Value对象应包含多少业务逻辑?

我尊重的一位导师认为,简单的bean浪费时间 – 值对象“必须”包含一些有用的业务逻辑。

另一个人说这样的代码很难维护,并且所有业务逻辑都必须外部化。

我意识到这个问题是主观的。 无论如何要求 – 想从更多角度了解答案。

您最好将它们称为传输对象或数据传输对象(DTO) 。

早先,这个相同的j2ee模式被称为“价值对象”,但他们更改了名称,因为它与此相混淆

http://dddcommunity.org/discussion/messageboardarchive/ValueObjects.html

为了回答你的问题,我只会将最小逻辑放在我的DTO中,这是显示原因所需的逻辑。

更好的是,如果我们谈论基于数据库的Web应用程序,我将超越核心j2ee模式并使用Hibernate或Java Persistence API来创建支持延迟加载关系的域模型,并在视图中使用它。

请参阅视图中的打开会话 。

这样,您就不必编写一组DTO,并且您可以在视图/控制器等中使用所有业务逻辑。

将数据和业务逻辑放在一起的想法是促进封装,并尽可能少地将内部状态暴露给其他对象。 这样,客户端可以依赖于接口而不是实现。 请参阅“告诉,不要问”原则和得墨忒耳定律 。 封装使得更容易理解数据可以处于状态,更容易读取代码,更容易解耦类,并且通常更容易进行unit testing。

外部化业务逻辑(通常是“服务”或“管理器”类)会产生诸如“这些数据在何处使用?”之类的问题。 和“它可以在哪些状态?” 更难回答。 它也是一种程序化的思维方式,包裹在一个物体中。 这可能导致贫血领域模型 。

外化行为并不总是坏事。 例如, 服务层可能会编排域对象,但不会接管其状态操作职责。 或者,当您主要对可以很好地映射到输入表单的数据库进行读/写操作时,您可能根本不需要域模型 – 或者它需要的痛苦的对象/关系映射开销。

传输对象通常用于通过提供调用层所需的最小状态信息来将架构层彼此分离(或与外部系统分离),而不暴露任何业务逻辑。

这可能很有用,例如在为视图准备信息时:只需为视图提供所需的信息,而不是其他任何内容,以便它可以专注于如何显示信息,而不是显示哪些信息。 例如,TO可能是多个数据源的聚合。

一个优点是您的视图和域对象是分离的。 在JSP中使用域对象可能会使您的域更难以重构并促使不加选择地使用getter和setter(从而破坏封装)。

但是,由于拥有大量的传输对象并且通常还有很多重复,因此也存在开销。 我一直在使用TO的一些项目基本上反映了其他域对象(我认为是反模式)。

这取决于。

哎呀,我只是脱口而出陈词滥调吗?

要求设计对象的基本问题是:管理对象数据的逻辑在被其他对象使用/使用时是否会不同相同

如果不同的使用领域需要不同的逻辑,则将其外部化。 如果无论物体移动到哪里都是相同的,请将它与课程放在一起。

我个人的偏好是将所有业务逻辑放在域模型本身中,即“真正的”域对象中。 因此,在创建数据传输对象时,它们大多只是域对象的(不可变)状态表示,因此不包含业务逻辑。 它们可以包含用于克隆和比较的方法,但业务逻辑代码的内容仍然存在于域对象中。

科罗斯说的话。

值对象:=一个小的简单对象,如金钱或日期范围,其等同性不基于身份。

DTO:=在进程之间传输数据的对象,以减少方法调用的数量。

这些是Martin Fowler提出的定义,我想推广它们。

我同意Panagiotis:视图模式中的开放会话比使用DTO要好得多。 换句话说,如果您从视图层一直向下传输域对象(或其某些复合对象),我发现应用程序要简单得多。

也就是说,很难实现,因为你需要让你的H​​ttpSession与你的持久层的工作单元一致。 然后,您需要确保所有数据库修改(即创建,更新和删除)都是有意的。 换句话说,您不希望视图层具有域对象,字段被修改并且修改被持久化而应用程序代码故意保存更改。 另一个重要的问题是确保您的事务语义令人满意。 通常,获取和修改一个域对象将在一个事务上下文中进行,并且使您的ORM层需要新事务并不困难。 具有挑战性的是嵌套事务,您希望在第一个打开的事务中包含第二个事务上下文。

如果您不介意调查非Java API如何处理这些问题,那么值得查看Rails的Active Record,它允许Ruby服务器页面直接使用域模型并遍历其关联。