服务访问实体属性

时间:2019-04-28 14:47:12

标签: oop domain-driven-design encapsulation ddd-service

在我们眼中,我们寻求封装。我们尝试不通过获取器或公共字段公开内部状态,而仅公开方法。

到目前为止,一切都很好。

在我们希望对多个实体进行操作的情况下,我们引入了服务。 但是,该服务如何在这些实体上自由运行?

如果所有(Service和Entities)都在同一个包中,则Entities可以公开包的私有方法或字段,而Service可以使用它们,并保留封装。但是,当实体和服务来自不同的软件包时该怎么办?看来,实体应该公开公共获取程序(贫血模型的第一步和从实体泄漏逻辑),或者执行可能特定于服务需求的,特定于服务需求的逻辑的公共方法,这似乎也很糟糕。该如何解决?

3 个答案:

答案 0 :(得分:1)

在OO环境中,您要了解的最重要的事情是对象响应消息,尤其是在OO P 中,方法这些响应的实施方式

例如,假设您有一个Person对象,您(作为程序员)已分配了对“增长”消息作出响应的责任。通常,您可以像这样将其实现为Person.grow()方法。

class Person {
    int age;

    public void grow() { this.age++; }
}

这似乎很明显,但是您必须注意,从消息发送者的角度来看,Person对象的反应是没有意义的。尽其所能,方法Person.grow()可能会触发导弹的发射,这并不重要,因为其他一些对象(或多个对象)可能以正确的方式进行响应(例如,UI组件会在屏幕)。但是, you 决定当Person对象处理“增长”消息时,它必须增加其age属性的值。这就是封装。

因此,为解决您的问题,“执行特定于服务需求的逻辑的公共方法,可能仅由该服务的需求引入-似乎也很糟糕”。都是因为您正在设计实体,以便以特定方式响应来自服务的消息以匹配应用程序的需求。要记住的重要一点是,服务并不决定实体的行为方式,而是实体以自己的方式响应服务的请求。

最后,您可能会问自己:实体如何知道他们需要响应某些消息?这很容易回答: YOU 决定如何链接消息回应。换句话说,您要考虑应用程序的需求(各种对象将发送什么“消息”)以及如何满足它们(对象以及如何响应消息)。

答案 1 :(得分:0)

  

在我们希望对多个实体进行操作的情况下,我们引入服务。

不,我们没有。好吧,我想有些人会这么做,但重点是他们不应该

在面向对象中,我们对特定的问题域进行建模。我们(同样也不应该)基于单个对象操作的其他对象数量进行区分。如果我必须对AppointmentAppointment的集合进行建模,而不必引入AppointmentService,引入ScheduleTimetable或其他任何模型,适用于该域。

EntityService的区别不符合域。它纯粹是技术性的,通常是对过程思维的回归,其中Entity是数据,而Service是对其执行操作的过程。

如今,

DDD并非基于OOP,它仅使用对象语法。一个明显的迹象是,在大多数项目中,实体是直接持久的,甚至包含数据库ID或与数据库相关的注释。

所以OOP 做DDD,就不能同时做到。 Here是我关于OO和DDD的话题(话题是德语,但幻灯片是英文)。

答案 2 :(得分:0)

我看不到将吸气剂用作贫血模型的一步。或者至少,就像编程中的一切一样,这取决于。
贫血模型的缺点是,访问对象的每个组件都可以对其进行变异,而无需对其变量进行任何强制性处理(可能会出现数据不一致的情况),可以使用setter方法轻松完成此操作。

(我将使用terms命令和查询来指示修改对象状态的方法和仅返回数据而不更改任何内容的方法)

具有聚集/实体的目的是强制执行对象不变式,因此它公开了“命令”方法,这些方法不反映对象的内部结构,而是“面向领域的”(使用“泛在语言” ”,以公开其“域行为”(建议避免使用get / set命名,因为它们是代表对象内部结构的标准命名)。

这是与 set 方法有关的,而 get 又如何呢?
因为set方法可以看作是聚合的“命令”,所以您可以将getter看作是用于向聚合请求数据的“ query”方法。如果不破坏强制不变式集合的责任,那么将数据请求到集合是完全可以的。这意味着您应该注意查询方法返回的内容。
如果查询方法的结果是一个 value 对象,那么,不可变的,拥有它是完全可以的。这样,查询聚合的人将返回只能读取的内容。
因此,您可以让查询方法使用对象内部状态进行计算(例如,方法int missingStudents()用于计算Lesson实体中有totalNumber个学生和一个List<StudentId>个学生的丢失学生的数量List<StudentId> presentStudent()处于内部状态),或者像List<StudentId> getStudents()这样的简单方法仅以内部状态返回列表,但是与@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .disable(); } } 只是名称的变化)。
因此,如果get方法返回的东西是由谁使用的不可变的东西,则它不会破坏聚合的不变性。
如果该方法返回的聚合对象是聚合状态的一部分,则访问该对象的任何人都可以查询该对象,并且现在可以对保留在聚合中的某些对象进行变异而无需通过正确的命令方法,从而跳过不变式检查(除非它是某种东西)被通缉和管理)。
另一种可能性是,对象是在查询期间动态创建的,并且不是聚合状态的一部分,因此,如果有人访问它,并且该对象是可变的,则聚合是安全的。 最后,如果您是ddd极端主义者,则将get和set方法视为一件丑事,但有时它们作为标准命名约定也很有用,并且某些库在此命名约定下起作用,因此我认为它们并不坏,如果他们没有违反汇总/实体职责。

最后,当您说想要在多个实体上进行操作时,我们会引入Service。,这是对的,但是服务也应该在一个对象上进行操作(变异,保存)。单个聚合,但这是另一个主题。