领域模型对象中应该有多少逻辑

发布于 2024-07-13 02:14:57 字数 284 浏览 4 评论 0原文

刚刚读完这篇文章作者:Greg Young,他在其中谈论了 Microsoft 推荐具有哑数据传输对象的模式。 他暗示,在 Java 社区中,事情正朝着相反的方向发展。

我的问题是实体对象中应该有多少逻辑? 我工作的地方(C# 商店)的理念是,如果无法序列​​化它,就不要将其放入实体中。

Just finished read this post by Greg Young, where he is talking about Microsoft recommending patterns with dumb data transfer objects. He implied that in the Java community, things are trending the other direction.

My question is how much logic should be in your entity objects? Our philosophy where I work (C# shop) is that if you can't serialize it, don't put it in the entity.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

捶死心动 2024-07-20 02:14:57

马特,

我想说你的商店正在编写程序代码。 我想澄清的是,许多大型系统(包括我参与过的许多系统)都是使用过程代码编写的,这并没有什么问题。 有一个时间和地点。

现在,过程代码在域模型中没有位置。 如果您想使用更程序化的样式,那也可以,但可以将其与表模块或活动记录模式等一起使用。 我认为指导中具有如此破坏性的并不是缺乏面向对象,而是使用带有过程逻辑的领域模型。

这导致人们花费大量资源来构建领域层(阻抗失配、构建聚合的思维过程时间、隔离、通用语言等),而没有获得领域层(通常是可维护性)本来可以提供的任何好处。 换句话说,虽然您可能很好地满足了您的功能需求,但您最终会花费大量预算而几乎没有回报。

现在回到什么是“行为”,我想从面向对象而不是“领域驱动设计”的角度重点讨论这个问题。 对象通常会封装一些状态并且通常会公开一些行为。

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

那么对象应该具有哪些行为呢? 简而言之,它应该是对其所封装的状态进行操作的行为。 在理想的行为面向对象世界中,状态永远不会从仅对象的行为中暴露出来。 如果我们开始看到如下代码,请从战术上将其放入代码中:

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() 等,而是简单地断言使用模拟在客户接缝上调用了删除方法。

现在回到标题。 业务对象中应该包含的逻辑量​​就是属于其封装其状态的责任的逻辑量。 有时这很多,有时则不然。 有些地方您也想使用服务......一个很好的例子是协调许多域对象之间针对给定行为的交互,但即使在这里,服务也应该在域对象上调用行为

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

格雷格

Matt,

I would say that your shop is writing procedural code. I want to make clear that there is nothing wrong with that many large systems (including many I have worked on) have been written using procedural code. There is a time and place for it.

Now procedural code has no place in a domain model. If you want to use a more procedural style that is fine but use it with something like a Table Module or an Active Record pattern. It is not the lack of OO that I am considering to be so destructive in the guidance but the use of a domain model with procedural logic.

This causes one to spend a large amount of resources building the domain layer (impedance mismatch, thought process time to build aggregates, isolation, ubiquitous language etc) without receiving any of the benefits that the domain layer (generally maintainability) would otherwise provide. In other words while you may meet your functional requirements just fine you end up spending a large amount of your budget with almost no return.

Now to come back to what "is behavior" I would like to focus on the question from an Object Oriented as opposed to a "Domain Driven Design" viewpoint. An object will generally encapsulate some state and will generally expose some behaviors.

quick reiteration: encapsulate state, expose behavior

So what behaviors should an object have? Put simply it should be the behaviors that operate upon the state it is encapsulating. In an ideal behavioral OO world the state would never be exposed from the object only behaviors. Put tactically into code if we start seeing code like:

Customer c = GetCustomerFromRepository();
c.Status = CustomerStatuses.Deleted;
c.LastUpdated = DateTime.Now;
c.UpdatedBy = GetCurrentUser();
CustomerRepository.Save(c);

We have a SRP violation ... This code is code that should be a behavior of the customer object because the "Responsibility" of the customer object is to.

Encapsulate state about a customer and expose behaviors.

As such we can see that we would be better off having a Customer.Delete() method. (yes this is a bad example I know ...)

Now we would also get to this by using TDD. It is much easier for us to deal in tests with the seam that the behavior provides than the seams where all of the state is exposed. The reason for this is that I don't need to duplicate the logic in my tests. The client code doesn't care how a delete works ... it only cares that the customer exposes the behavior. As such in our tests instead of asserting that c.State == CustomerStates.Deleted and c.UpdatedBy==GetCurrentUser() etc etc we would simply assert that the delete method was called on the customer seam by using a mock.

Now to come back to the title. The amount of logic that should be in a business object is the amount of logic that falls under its responsibility of encapsulating its state. Sometimes this is alot, sometimes its not. There are places where you want to use services as well ... a good example would be coordinating the interaction between many domain objects for a given behavior but even here the service should be calling behaviors on the domain objects.

Does this help to clarify things a bit?

Greg

云裳 2024-07-20 02:14:57

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

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

不幸的是,术语“域模型”最近似乎被淡化为仅表示没有行为的数据的对象模型。

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

希望有帮助!

If you're calling them your "domain model objects" then I will assume that you're refering to Fowler's Domain Model pattern. http://martinfowler.com/eaaCatalog/domainModel.html

Given that assumption, then the answer to your question is "all the business logic" since that is essentially the definition of the pattern.

Unfortunately the term "domain model" seems to have been watered down recently to only mean an object model of your data with no behaviour.

If you haven't already done so, I would encourage you to read PoEAA and decide where you think that domain logic belongs in your situation. If you decide on a domain model, then I would encourage you to read Evan's DDD book and learn about the differences between entities, value objects and services.

Hope that helps!

指尖凝香 2024-07-20 02:14:57

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

  1. 我不会将函数 getFullName() 分类在 Person 实体中,它只是连接一些字符串作为逻辑。
  2. 计算订单商品价值更有可能符合逻辑。
  3. 我肯定会说,进行一些预订交易是合乎逻辑的。

第 1 点和第 2 点可能适合我进入实体。 第3点不是。 因此,我将逻辑定义为:

  • 任何执行任何与持久性相关的操作(读/写)的
  • 操作 任何涉及任何其他(不直接相关,例如主从)实体的操作

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

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

The main point is how one defines logic. To give some examples:

  1. I wouldn't categorize a function getFullName() in a Person entity, which just concatenates some strings, as logic.
  2. Calculating an order item value would more likely qualify for being logic.
  3. Doing some booking transactions I would definitely say is logic.

Point 1 and maybe 2 would go for me into the entity. Point 3 not. So I define logic as:

  • any operation that does any persistence related thing (read/write)
  • any operation that involves any other (not directly related, e.g. master-detail) entity

IMO, any of these operations don't belong into entities.

Now, why/when I wouldn't put also point 1 and 2 type operations into an entity? It's a rather rare situation, but I wouldn't do it, as soon as the data stored in the entity needs to be interpreted in some way before it can be used by the application (e.g. if depending on current user, content of field X has a different meaning), that means the entity's data itself yields some logic.

宫墨修音 2024-07-20 02:14:57

最近,我一直在考虑创建具有结构的域模型,并且仅具有该模型通用的行为(即可以跨多个有界上下文使用的行为),并为特定于有界上下文的行为提供扩展方法。 这使域模型接近 DTO(对于那些喜欢这样的人),并将该域模型的使用限制为仅在有界上下文中允许的行为。 因此,这可能是一种中间路线响应的选择。 :)

Lately, I've been toying with the idea of creating domain models that have structure and only those behaviors that are universal for that model (i.e. behaviors that can be used across multiple bounded contexts) with extension methods for the behavior specific to a bounded context. This keeps the domain models close to a DTO (for those that like that) and restricts the use of that domain model to only the allowed behaviors within a bounded context. So that could be an option for a middle-of-the-road response. :)

倦话 2024-07-20 02:14:57

据我了解,与实体相关的所有业务逻辑都应该进入该实体。 这由基于系统的业务规则定义实体的行为或内部结构的任何逻辑组成。 这不应该包括表示逻辑或持久性逻辑(明显的例外是 Active Record 设计模式),但应该包括数据验证、实体关系、状态机和其他定义实体在真实情况下如何行为的东西。它试图模拟的世界事物。

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

As far as I understand it, all business logic related to an entity should go into that entity. This consists of any logic that defines the behaviour or internal structure of the entity based on the business rules of the system. This should not include presentation logic or persistance logic (the obvious exception being with the Active Record design pattern) but should include things such as data validation, entity relationships, state machines and other things that define how the entity behaves in terms of the real-world thing it is trying to model.

The way I try to look at it is to try and make my models be as resuable as possible. Always try to think of how the model would be used if it where to be ported to a different system where the client code (or code using the entity) may be different. If the functionality is not part of the entity will it still behave in the same way following the same business rules? If the answer is no then the the functionality should go in the entity.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文