为什么应该将域实体与表示层隔离?

发布于 2024-07-19 02:11:54 字数 370 浏览 8 评论 0原文

领域驱动设计的一部分似乎没有太多细节,那就是如何以及为什么应该将领域模型与界面隔离。 我试图让我的同事相信这是一个很好的实践,但我似乎没有取得太大进展......

他们在表示层和界面层中随心所欲地使用域实体。 当我向他们争论他们应该使用显示模型或 DTO 将领域层与界面层隔离时,他们反驳说,他们没有看到这样做的业务价值,因为现在你有一个 UI 对象需要维护以及原始域对象。

所以我正在寻找一些可以用来支持这一点的具体原因。 具体来说:

  1. 为什么我们不应该在表示层中使用域对象?
    (如果答案是显而易见的,“解耦”,那么请解释为什么这在这种情况下很重要)
  2. 我们是否应该使用其他对象或构造来将域对象与接口隔离?

One part of domain-driven design that there doesn't seem to be a lot of detail on, is how and why you should isolate your domain model from your interface. I'm trying to convince my colleagues that this is a good practice, but I don't seem to be making much headway...

They use domain entities where ever they please in the presentation and interface layers. When I argue to them that they should be using display models or DTOs to insulate the Domain layer from the interface layer, they counter that they don't see the business value in doing something like that, because now you have a UI object to maintain as well as the original domain object.

So I'm looking for some concrete reasons I can use to back this up. Specifically:

  1. Why should we not use domain objects in our presentation layer?
    (if the answer is the obvious one, 'decoupling', then please explain why this is important in this context)
  2. Should we use additional objects or constructs to isolate our domain objects from the interface?

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

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

发布评论

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

评论(15

泪是无色的血 2024-07-26 02:11:55

另请参阅下面的“层之间的数据传播”部分,我认为该部分提出了令人信服的论点:

http://galaxy.andromda.org/docs/andromda-documentation/andromda-getting-started-java/java/index.html

See also the section "Data propagation between layers" in the following, which I think presents compelling arguments:

http://galaxy.andromda.org/docs/andromda-documentation/andromda-getting-started-java/java/index.html

桃扇骨 2024-07-26 02:11:55

借助“值注入器”等工具以及表示层中“映射器”的概念,同时使用视图,更容易理解每​​段代码。 如果你有一点代码,你不会立即看到优势,但是当你的项目越来越大时,你会很高兴使用视图而不必进入服务的逻辑,存储库来理解视图模型。 View Model 是广阔的反腐败层世界中的另一个守卫,在长期项目中价值不菲。

我认为使用视图模型没有优势的唯一原因是,如果您的项目足够小且简单,可以将视图直接绑定到模型的每个属性。 但是,如果将来需求发生变化,视图中的某些控件将不会绑定到模型,并且您没有视图模型概念,那么您将开始在许多地方添加补丁,并且您将开始拥有遗留代码你不会欣赏。 当然,您可以进行一些重构,以在视图视图模型中转换视图模型,并遵循 YAGNI 原则,同时如果不需要,则不添加代码,但对我自己来说,这更是我必须遵循的最佳实践,以添加表示层仅公开视图模型对象。

With the help of tool like 'Value Injecter' and the concept of 'Mappers' in the presentation layer while working with views, it's much more easy to understand each piece of code. If you have a little bit of code, you will not see the advantages immediately but when your project will growing more and more, you will be very happy while working with the views to don't have to enter into the logic of the services, repositories to understand the view model. View Model is another guard in the vast world of anti-corruption layer and worth its weight in gold in a long term project.

The only reason why I see no advantage of using view model is if your project is small and simple enough to have views binded directly to each property of your model. But if in the futur, the requirement change and some controls in the views will not be binded to the model and you don't have a view model concept, you will start adding patches in many places and you will begin having a legacy code that you will not appreciate. Sure, you can do some refactoring to transform your view-model in view-viewmodel and follow the YAGNI principle while not adding code if you don't need it but for myself, it's much more a best practice that I must follow to add a presentation layer exposing only view-model objects.

浅唱ヾ落雨殇 2024-07-26 02:11:55
I'm trying to convince my colleagues that this is a good practice

为了良好实践而采取的良好实践并不总是具有商业效率。 如果有道理的话,我非常乐意反对使用 CQRS、事件溯源、DDD 等。例如,在具有一两个用例的小型项目上。

虽然其他答案非常好,但没有提到让我相信解耦很重要的问题:您的存储库 (ORM) 可能会意外填充数据。

例如,考虑访问实体框架的这两个操作DbContext
(我没有测试过这段代码,但它应该能说明问题)


public JsonResult GetUser(string id) {
  return new JsonResult(_ctx.Users.FindOne(u => u.id == id));
}

public JsonResult AddUser(string id) {
  var currentUser = _ctx.Users.Include(u => u.Roles).FindOne(currentUserId); // <--
  if (!currentUser.Roles.Contains("Administrator")) throw new Exception("User not authorized to add new users");  

  _ctx.Users.Add(new User(id));

  return new JsonResult(_ctx.Users.FindOne(u => u.id == id));
}

虽然两个操作都返回用户的 JSON 对象,但由于实体框架的对象缓存,第二个操作将包含 Roles 属性——没有在最后一行明确要求。

如果您的应用程序或持久性/基础设施层有条件地包含属性,那么您的域对象属性将被有条件地实例化,并且您可能会得到意外的结果。

由于这一点,以及其他答案提供的重要原因,我确信不再将我的域对象暴露给我的表示层。

最后,我并不反对使用匿名对象来代替 Dto,尤其是当 JsonResult 失去类型安全性时。

例如:

public JsonResult GetUser(string id) {
  var user = _ctx.Users.FindOne(u => u.id == id);
  return new JsonResult(new {
    id = user.id,
    name = user.name,
  })
}

public JsonResult AddUser(string id) {
  var currentUser = _ctx.Users.Include(u => u.Roles).FindOne(currentUserId); // <--
  if (!currentUser.Roles.Contains("Administrator")) throw new Exception("User not authorized to add new users");  
  
  _ctx.Users.Add(new User(id));

  var user = _ctx.Users.FineOne(u => u.id == id);

  return new JsonResult(new {
    id = user.id,
    name = user.name,
  })
}
I'm trying to convince my colleagues that this is a good practice

A good practice for good practices sake is not always business efficient. If justified, I'm more than happy to argue against using CQRS, Event Sourcing, DDD, etc. For example, on smaller projects that have one or two use-cases.

While the other answers are excellent, the concerns that convinced me that decoupling is important hasn't been mentioned: Your repository (ORM) may populate data unexpectedly.

For example, consider these two actions accessing an Entity Framework DbContext:
(I've not tested this code, but it should get the point across)


public JsonResult GetUser(string id) {
  return new JsonResult(_ctx.Users.FindOne(u => u.id == id));
}

public JsonResult AddUser(string id) {
  var currentUser = _ctx.Users.Include(u => u.Roles).FindOne(currentUserId); // <--
  if (!currentUser.Roles.Contains("Administrator")) throw new Exception("User not authorized to add new users");  

  _ctx.Users.Add(new User(id));

  return new JsonResult(_ctx.Users.FindOne(u => u.id == id));
}

While both actions return a JSON object of a User, due to Entity Framework's Object Caching, the second action will include the Roles property—without explicitly asking for it on the final line.

If your application or persistence/infrastructure layer conditionally Includes properties, then your Domain object properties will be conditionally instantiated and you may get unexpected results.

Due to this, and the great reasons provided by other answers, I'm convinced to no longer expose my domain objects to my presentation layer.

Finally, I'm not against using Anonymous objects in place of Dto's—especially when type safety is lost with JsonResult.

For example:

public JsonResult GetUser(string id) {
  var user = _ctx.Users.FindOne(u => u.id == id);
  return new JsonResult(new {
    id = user.id,
    name = user.name,
  })
}

public JsonResult AddUser(string id) {
  var currentUser = _ctx.Users.Include(u => u.Roles).FindOne(currentUserId); // <--
  if (!currentUser.Roles.Contains("Administrator")) throw new Exception("User not authorized to add new users");  
  
  _ctx.Users.Add(new User(id));

  var user = _ctx.Users.FineOne(u => u.id == id);

  return new JsonResult(new {
    id = user.id,
    name = user.name,
  })
}
默嘫て 2024-07-26 02:11:55

在通用语义和特定领域语义之间添加额外映射的唯一合理原因是,您拥有(访问)基于与领域语义不同的通用(但可映射)语义的现有代码(和工具)主体。

领域驱动设计在与一组正交的功能域框架(例如 ORM、GUI、工作流等)结合使用时效果最佳。 永远记住,只有在外层邻接中才需要暴露域语义。 通常这是前端 (GUI) 和持久后端 (RDBM、ORM)。 任何有效设计的中间层都可以而且应该是域不变的。

The only sensible reason for adding additional mapping between generalized and domain specific semantics is that you have (access to) an existing body of code (and tools) that are based on a generalized (but mappable) semantics distinct from your domain semantics.

Domain driven designs work best when used in conjunction with an orthogonal set of functional domain frameworks (such as ORM, GUI, Workflow, etc.). Always remember that it is only in the outer layer adjacencies that domain semantics need to be exposed. Typically this is the front end (GUI) and persistent back-end (RDBM,ORM). Any effectively designed intervening layers can and should be domain invariant.

╭⌒浅淡时光〆 2024-07-26 02:11:54

很简单,原因之一是实施和漂移。 是的,您的表示层需要了解您的业务对象才能正确表示它们。 是的,最初看起来两种类型的对象的实现之间有很多重叠。 问题是,随着时间的推移,双方都会增加一些东西。 表示发生变化,表示层的需求不断发展以包含完全独立于业务层的内容(例如颜色)。 同时,您的领域对象会随着时间的推移而发生变化,如果您没有与接口进行适当的解耦,那么您就会冒着对业务对象进行看似良性的更改而搞砸接口层的风险。

就我个人而言,我认为处理事物的最佳方式是通过严格执行的接口范例; 也就是说,您的业务对象层公开一个接口,这是与其通信的唯一方式; 没有公开有关接口的实现细节(即域对象)。 是的,这意味着您必须在两个位置实现域对象; 你的接口层和BO层。 但是,这种重新实现虽然最初看起来像是额外的工作,但有助于加强解耦,这将在未来的某个时候节省大量的工作。

Quite simply, the reason is one of implementation and drift. Yes, your presentation layer needs to know about your business objects to be able to represent them properly. Yes, initially it looks like there is a lot of overlap between the implementation of the two types of objects. The problem is, as time goes on, things get added on both sides. Presentation changes, and the needs of the presentation layer evolve to include things that are completely independent of your business layer (color, for example). Meanwhile, your domain objects change over time, and if you don't have appropriate decoupling from your interface, you run the risk of screwing up your interface layer by making seemingly benign changes to your business objects.

Personally, I believe the best way to approach things is through the strictly enforced interface paradigm; that is, your business object layer exposes an interface that is the only way that it can be communicated with; no implementation details (i.e. domain objects) about the interface are exposed. Yes, this means that you have to implement your domain objects in two locations; your interface layer and in your BO layer. But that reimplementation, while it may initially seem like extra work, helps enforce the decoupling that will save TONS of work at some point in the future.

月下客 2024-07-26 02:11:54

我自己也曾为此苦苦挣扎。 在某些情况下,DTO 在演示中使用是有意义的。 假设我想在我的系统中显示公司的下拉列表,并且我需要它们的 id 来绑定值。

好吧,我可以发回带有名称和 ID 的 DTO,而不是加载可能引用订阅或谁知道还有什么的 CompanyObject。 恕我直言,这是一个很好的用途。

现在再举一个例子。 我有一个代表估计的对象,该估计可能由劳动力、设备等组成,它可能有用户定义的大量计算,这些计算将所有这些项目进行汇总(每个估计可能因不同类型而不同的计算)。 为什么我必须为这个对象建模两次? 为什么我不能简单地让 UI 枚举计算并显示它们?

我通常不使用 DTO 将域层与 UI 隔离。 我确实使用它们将我的域层与我无法控制的边界隔离开来。 有人将导航信息放入其业务对象的想法是荒谬的,不要污染您的业务对象。

有人会在他们的业务对象中进行验证的想法吗? 嗯,我说这是一件好事。 您的 UI 不应该独自负责验证您的业务对象。 您的业​​务层必须进行自己的验证。

为什么要将 UI 生成代码放在业务对象中? 就我而言,我有单独的对象,它们与 UI 分开生成 UI 代码。 我有单独的对象,它们将我的业务对象呈现为 Xml,必须分离各层以防止这种类型的污染的想法对我来说是如此陌生,因为为什么要将 HTML 生成代码放入业务对象中......

编辑
我想多一点,在某些情况下,UI 信息可能属于领域层。 这可能会使您所谓的域层变得模糊,但我开发了一个多租户应用程序,该应用程序在 UI 外观和感觉以及功能工作流程方面都有非常不同的行为。 取决于各种因素。 在本例中,我们有一个代表租户及其配置的域模型。 它们的配置恰好包含 UI 信息(例如通用字段的标签)。

如果我必须设计我的对象以使其可持久化,我是否还必须复制这些对象? 请记住,如果您想添加新字段,现在您有两个位置可以添加它。 也许这会引发另一个问题,如果您使用 DDD,所有持久实体都是域对象吗? 我知道在我的例子中他们是。

I have struggled with this myself. There are cases where a DTO makes sense to use in presentaton. Let's say I want to show a drop down of Companies in my system and I need their id to bind the value to.

Well instead of loading a CompanyObject which might have references to subscriptions or who knows what else, I could send back a DTO with the name and id. This is a good use IMHO.

Now take another example. I have an object which represents an Estimate, this estimate might be made up labor, equipment etc, it might have lots of calculations that are defined by the user which take all these items and sum them up (Each estimate could be different with different types of calculations). Why should I have to model this object twice? Why can't I simply have my UI enumerate over the calculations and display them?

I generally do not use DTO's to isolate my domain layer from my UI. I do use them to isolate my domain layer from a boundary that is outside of my control. The idea that someone would put navigation information in their business object is ridiculous, don't contaminate your business object.

The idea that someone would put validation in their business object? Well I say that this is a good thing. Your UI should not have sole responsability to validate your business objects. Your business layer MUST do its own validation.

Why would you put UI generation code in a busienss object? In my case I have seperate objects which generates the UI code seperatley from the UI. I have sperate objects which render my business objects into Xml, the idea that you have to seperate your layers to prevent this type of contamination is so alien to me because why would you even put HTML generation code in a business object...

Edit
As I think a little bit more, there are cases where UI information might belong in the domain layer. And this might cloud what you call a domain layer but I worked on a multi-tenant application, which had very different behavior both UI look and feel and functional workflow. Depending on various factors. In this case we had a domain model that represented the tenants and their configuration. Their configuration happened to include UI information (Label's for generic fields for example).

If I had to design my objects to make them persistable, should I also have to duplicate the objects? Keep in mind if you want to add a new field now you have two places to add it. Perhaps this raises another question if your using DDD, are all persisted entities domain objects? I know in my example they were.

墨洒年华 2024-07-26 02:11:54

我不同意。

我认为最好的方法是从表示层中的域对象开始,直到有意义为止。

与流行的看法相反,“域对象”和“值对象”可以在表示层中愉快地共存。 这是最好的方法 - 您可以获得两个世界的好处,减少域对象的重复(和样板代码); 以及跨请求使用值对象的剪裁和概念简化。

I disagree.

I think the best way to go is to start with domain objects in your presentation layer UNTIL IT MAKES SENSE TO DO OTHERWISE.

Contrary to popular belief, "Domain Objects" and "Value Objects" can happily co-exist in the presentation layer. And this is the best way to do it - you get the benefit of both worlds, reduced duplication (and boilerplate code) with the domain objects; and the tailoring and conceptual simplification of using value objects across requests.

轻许诺言 2024-07-26 02:11:54

这样做的原因与将 SQL 排除在 ASP/JSP 页面之外的原因相同。

如果您只保留一个域对象,用于表示层和域层,那么该对象很快就会变得单一。 它开始包括UI验证代码、UI导航代码和UI生成代码。 然后,您很快就会在其上添加所有业务层方法。 现在你的业务层和UI都混在一起了,而且都在领域实体层搞乱了。

您想在另一个应用程序中重用这个漂亮的 UI 小部件吗? 好吧,您必须创建一个具有此名称、这两个模式和这 18 个表的数据库。 您还必须配置 Hibernate 和 Spring(或您选择的框架)来进行业务验证。 哦,你还必须包含这85个其他不相关的类,因为它们是在业务层中引用的,而恰好位于同一个文件中。

You do it for the same reason you keep SQL out of your ASP/JSP pages.

If you keep only one domain object, for use in the presentation AND domain layer, then that one object soon gets monolithic. It starts to include UI validation code, UI navigation code, and UI generation code. Then, you soon add all of the business layer methods on top of that. Now your business layer and UI are all mixed up, and all of them are messing around at the domain entity layer.

You want to reuse that nifty UI widget in another app? Well, You have to create a database with this name, these two schemas, and these 18 tables. You must also configure Hibernate and Spring ( or your frameworks of choice ) to do the business validation. Oh, you must also include these 85 other non-related classes because they are referenced in the business layer, which just happens to be in the same file.

身边 2024-07-26 02:11:54

答案取决于您的应用程序的规模。


简单的 CRUD(创建、读取、更新、删除)应用程序

对于基本的 CRUD 应用程序,您没有任何功能。 在实体之上添加 DTO 会浪费时间。 它会增加复杂性,但不会增加可扩展性。

输入图片此处描述


中等复杂的非 CRUD 应用程序

在这种规模的应用程序中,您将拥有很少的实体,这些实体具有真正的生命周期以及与之相关的一些业务逻辑。

在这种情况下添加 DTO 是一个好主意,原因如下:

  • 表示层只能看到实体拥有的字段的子集。 您封装实体
  • 后端和前端之间没有耦合
  • 如果您在实体内部有业务方法,但在 DTO 中没有,那么添加 DTO 意味着外部代码不能破坏实体的状态。

输入图片这里的描述


复杂的企业应用

单个实体可能需要多种表示方式。 他们每个人都需要不同的字段集。 在这种情况下,您会遇到与前面的示例相同的问题,并且需要控制每个客户端可见的字段数量。
为每个客户端提供单独的 DTO 将帮助您选择应该可见的内容。

输入图片此处描述

Answer depends on scale of your application.


Simple CRUD (Create, Read, Update, Delete) application

For basic crud applications you do not have any functionality. Adding DTO on top of entities woudl be a waste of time. It would increase complexity without increasing scalability.

enter image description here


Moderately complicated Non-CRUD application

In this size of application you will have few entities which have true lifeclycle and some business logic associated with them.

Adding DTOs on this case is a good idea for few reasons:

  • Presentation layer can see only subset of fields which entity has. You encapsulate Entities
  • No coupling between backend and frontent
  • If you have business methods inside entities, but not in DTO then adding DTOs means that outside code can't ruin state of your entity.

enter image description here


Complicated Enterprise Application

Single entity might need multiple ways of presentation. Each of them will need different set of fields. In this case you encounter the same issues as in previous example plus need to control amount of fields visible for each client.
Having separate DTO for each client will help you to chose what should be visible.

enter image description here

偏闹i 2024-07-26 02:11:54

我们在服务器和用户界面上使用相同的模型。 这很痛苦。 有一天我们必须重构它。

问题主要是因为领域模型需要被分割成更小的部分,以便能够在不引用整个数据库的情况下对其进行序列化。 这使得在服务器上使用变得更加困难。 缺少重要链接。 有些类型也不可序列化,无法发送到客户端。 例如“类型”或任何通用类。 它们需要是非通用的,并且类型需要作为字符串传输。 这会生成额外的序列化属性,它们是多余且令人困惑的。

另一个问题是 UI 上的实体并不真正适合。 我们正在使用数据绑定,许多实体有大量冗余属性仅用于 ui 目的。 此外,实体模型中还有许多“BrowsableAttribute”和其他属性。 这实在是太糟糕了。

最后,我认为这只是哪种方式更容易的问题。 在某些项目中,它可能可以正常工作,并且不需要编写另一个 DTO 模型。

We are using the same model in the server and on the ui. And it's a pain. We have to refactor it some day.

The problems are mainly because the domain model needs to be cut into smaller pieces to be able to serialize it without having the whole database referenced. This makes it harder to use on the server. Important links are missing. Some types are also not serializable and can't be sent to the client. For instance 'Type' or any generic class. They need to be non-generic and Type needs to be transferred as string. This generates extra properties for serialization, they are redundant and confusing.

Another problem is that the entities on the UI don't really fit. We are using databinding and many entities have lots of redundant properties only for ui purposes. Additionally there are many 'BrowsableAttribute' and others in the entity model. This is really bad.

At the end, I think it is just a matter of which way is easier. There might by projects where it just works fine and where is no need to write another DTO model.

一抹微笑 2024-07-26 02:11:54

该死,我发誓这就是坚持。

不管怎样,这是同一件事的又一个例子:帕纳斯定律说模块应该保守秘密,而秘密是可以改变的要求。 (鲍勃·马丁(Bob Martin)有一个规则,这是这个规则的另一个版本。)在这样的系统中,演示可以独立于而改变。 例如,一家公司以欧元维持价格并在公司办公室使用法语,但希望以美元显示价格并使用普通话文本。 是相同的; 演示文稿可能会改变。 因此,为了最大限度地减少系统的脆弱性(即为了实现需求变更而必须更改的事物的数量),您可以将关注点分开。

Dammit, I swear this said persistence.

Anyway, it's one more instance of the same thing: Parnas's law says a module should keep a secret, and the secret is a requirement that can change. (Bob Martin has a rule that's another version of this.) In a system like this, the presentation can change independently of the domain. Such as, for example, a company that maintains prices in Euros and uses French in the company offices, but wants to present prices in dollars with text in Mandarin. The domain is the same; the presentation can change. So, to minimize the brittleness of the system — that is, the number of things that must be changed to implement a change in requirements — you separate the concerns.

苍景流年 2024-07-26 02:11:54

这在很大程度上与依赖关系有关。 组织的核心功能结构有其自身的功能需求,UI应使人们能够修改和查看核心; 但核心本身不需要容纳 UI。 (如果需要发生,通常表明核心不是设计的。)

我的会计系统有一个结构和内容(和数据),应该模拟我公司的运营。 无论我使用什么会计软件,这种结构都是真实存在的。 (一个给定的软件包不可避免地包含其自身的结构和内容,但部分挑战是最大限度地减少这种开销。)

基本上,一个人有一份工作要做。 DDD 应与工作流程和内容相匹配。 DDD 旨在明确所有需要完成的工作,并尽可能完全且独立地完成。 然后,UI 希望有助于尽可能透明、尽可能高效地完成工作。

接口是关于为正确建模和不变的功能核心提供的输入和视图。

It's about dependencies for the most part. The core functional structure of the organization has its own functional requirements, and the UI should enable people to modify and view the core; but the core itself should not be required to accommodate the UI. (If it needs to happen, it's usually an indication the core is not property designed.)

My accounting system has a structure and content (and data) that are supposed to model the operation of my company. That structure is real and exists regardless of what accounting software I use. (Inevitably a given software package contains structure and content for its own sake, but part of the challenge is minimizing this overhead.)

Basically a person has a job to do. The DDD should match the flow and content of the job. DDD is about making explicit all the jobs that need being done ad completely and independently as possible. Then the UI hopefully facilitates getting the job done as transparently as possible, as productively as possible.

Interfaces are about the inputs and views provided for the properly modeled and invariant functional core.

说好的呢 2024-07-26 02:11:54

您的演示文稿可能引用您的域层,但不应该直接从您的用户界面绑定到您的域对象。 域对象不适合 UI 使用,因为如果设计得当,它们通常是基于行为而不是数据表示。 UI 和 Domain 之间应该有一个映射层。 MVVM,或 MVP,是一个很好的模式。 如果您尝试将 UI 直接绑定到域,您可能会给自己带来很多麻烦。 他们有两个不同的目的。

Your presentation may reference your domain layer, but there should be no binding directly from your ui to your domain objects. Domain objects are not intended for UI usage since they are often, if properly designed, based around behaviors and not data representations. There should be a mapping layer between the UI and the Domain. MVVM, or MVP, is a good pattern for this. If you try to directly bind your UI to the Domain, you will probalby create a lot of headache for yourself. They have two different purposes.

橙幽之幻 2024-07-26 02:11:54

这是一个真实的例子,说明了为什么我认为将域实体与视图分开是一种很好的做法。

几个月前,我创建了一个简单的 UI,通过一系列 3 个仪表显示土壤样本中氮、磷和钾的值。 每个仪表都有一个红色、绿色和红色部分,即每个组件的数量可能太少或太多,但中间有一个安全的绿色水平。

没有太多考虑,我对业务逻辑进行了建模,以提供这 3 种化学成分的数据和一个单独的数据表,其中包含有关 3 种情况中每种情况下可接受水平的数据(包括使用的测量单位,即摩尔或百分比)。 然后,我使用一个非常不同的模型对 UI 进行建模,该模型关注仪表标签、值、边界值和颜色。

这意味着当我后来必须显示 12 个组件时,我只需将额外的数据映射到 12 个新的仪表视图模型中,它们就会出现在屏幕上。 这也意味着我可以轻松地重复使用仪表控件并让它们显示其他数据集。

如果我将这些仪表直接耦合到我的域实体中,我将不会拥有任何上述灵活性,并且任何未来的修改都会令人头疼。 在用户界面中建模日历时,我遇到过非常类似的问题。 如果要求日历约会在有 10 名以上与会者时变为红色,则处理此问题的业务逻辑应保留在业务层中,并且 UI 中的所有日历都需要知道,它已被指示变红,应该不需要知道为什么。

Here's a real example as to why I find it good practice to separate domain entities from the view.

A few months ago I created a simple UI to show the values of Nitrogen, Phosphorus and Potassium in a soil sample through a series of 3 gauges. Each gauge had a red, green and red section, i.e. you could either have too little or too much of each component, but there was a safe green level in the middle.

Without much thought, I modelled my business logic to supply data for these 3 chemical components and a separate data sheet, containing data about the accepted levels in each of the 3 cases (including which measurement unit was being used, i.e. moles or percentage). I then modelled my UI to use a very different model, this model was concerned about gauge labels, values, boundary values and colours.

This meant when I later had to show 12 components, I just mapped the extra data into 12 new gauge view models and they appeared on the screen. It also meant I could reuse the gauge control easily and have them display other data sets.

If I'd had coupled these gauges directly into my domain entities, I would not have any of the above flexibility and any future modifications would be a headache. I've come across very similar issues when modelling calendars in the UI. If there is a requirement for a calendar appointment to turn red when there are 10+ attendees, then the business logic to handle this should remain in the business layer and all the calendar in the UI needs to know, is that it has been instructed to turn red, it should not need to know why.

妳是的陽光 2024-07-26 02:11:54

也许您没有从足够广泛的角度来概念化 UI 层。 考虑多种形式的响应(网页、语音响应、印刷信件等)和多种语言(英语、法语等)。

现在假设电话呼入系统的语音引擎运行在与运行网站的计算机(可能是 Windows)完全不同类型的计算机(例如 Mac)上。

当然,很容易陷入“在我的公司,我们只关心英语,在 LAMP(Linux、Apache、MySQL 和 PHP)上运行我们的网站,并且每个人都使用相同版本的 Firefox”的陷阱。 但5年或10年后呢?

Perhaps you are not conceptualizing the UI layer in broad enough terms. Think in terms of multiple forms of response (web pages, voice response, printed letters etc) and in terms of multiple languages (English, French etc.).

Now suppose that the speech engine for the telephone call-in system runs on a completely different type of computer (Mac for example) from the computer that runs the website (Windows perhaps).

Of course it is easy to fall into the trap "Well in my company we only care about English, run our website on LAMP (Linux, Apache, MySQL and PHP) and everyone uses the same version of Firefox". But what about in 5 or 10 years?

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