MVC模型对象、领域对象和DTO之间有什么区别?

113
什么是MVC模型对象、领域对象和DTO之间的区别?
我的理解是:
MVC模型对象:
用于模型化要由相应视图显示的数据。它可能不直接映射到领域对象,即可能包含来自一个或多个领域对象的数据。
1. 客户端 2. 可包含业务逻辑,例如验证、计算属性等 3. 没有与持久化相关的方法
领域对象:
模型化问题域中的真实对象,例如预订、客户、订单等。用于持久化数据。
1. 服务器端 2. 没有业务逻辑
DTO(数据传输对象):
用于在层之间传输数据,当这些层位于不同的进程中时,例如从数据库到客户端应用程序。允许在传输过程中进行单个事务,而不是多次调用以获取与多个领域对象对应的数据。DTO仅包含数据和访问器方法,没有逻辑。数据是针对特定数据库事务的,因此它可能直接映射到一个或多个领域对象的数据。
  1. 在服务器和客户端之间传递时都会使用
  2. 没有业务逻辑
  3. 没有与持久化相关的方法

所以,问题是:

  1. 上述理解正确吗?我是否遗漏了任何关键点?

  2. 如果模型对象不需要额外的业务逻辑,有没有理由不使用领域对象作为MVC模型?

  3. 如果模型对象不需要额外的业务逻辑,有没有理由不使用数据传输对象(DTO)作为MVC模型?

7个回答

34

域对象和模型对象本质上是相同的,并且可能包含业务逻辑。根据实现方式,如果将模型中的业务逻辑移动到服务类中,则域对象和DTO对象可能是等效的。

常见的DTO变体之一是视图模型(View Model),它仅用于在域模型和视图之间传输数据,尽管通常视图模型可能包含逻辑,但这应该仅限于UI逻辑。


1
感谢两位回答者。现在我对此更清楚了。领域对象可以拥有业务逻辑,例如验证和与数据直接相关的逻辑。 - Timothy Mowlem
3
单独的MVC模型对象仅需要封装与在视图中显示数据相关的逻辑。如果没有,则更容易使用域对象作为MVC模型对象。 - Timothy Mowlem

10

领域对象和数据传输对象(DTO)也可以是您的“模型”对象 - 您可以有一个视图来呈现“Customer”领域对象的详细信息。

领域对象可以具有业务逻辑,以强制执行领域实体的属性。验证就是这样一种情况。领域对象本身不包含与持久性相关的方法,但它可以具有元数据(如注释)来支持持久性。

POJO编程模型使得将同一个对象用作领域对象、DTO和模型对象成为可能 - 实质上,您不会实现任何只适用于一个层但不适用于其他层的多余接口。


1
是的,这就是我正在做的事情。实际上,在几乎所有情况下,我从未需要使用除了Domain对象之外的任何东西。DTO将用于具有跨域对象的多个数据项的复杂查询。 - Timothy Mowlem
1
如果模型数据与重要的业务逻辑/处理相关联,则需要单独的MVC模型类才是真正必要的吗? - Timothy Mowlem
1
是的,这将是拥有适当的专用模型而不是使用领域对象的一个原因。你的领域对象可能只存储UTC日期,这已足够应对所有业务逻辑。但是在用户界面上,比如说你需要以用户帐户的时区显示日期。使用模型将有助于进行UI特定的计算。 - kartheek
如果您要测试一些UI行为,那么您可能需要一些模型类。在复杂的应用程序中,您可以选择、移动和修复一些视图模型。 - Andres Camilo Sierra Hormiga

9
A DTO = is an object that carries data between processes.

但最有趣的部分是它除了存储和检索自己的数据之外没有任何行为!!!
坚持使用MVC方法...
Domain = subject of your entire application.

Model = contains the (programming languages objects : EX: C# objects) to make up the universe of your application.

他们显然可以具有行为和属性(与DTO的区别见)。

通常,一个应用程序(轻量级应用程序)可以拥有一个模型 - 在这种情况下,您的模型恰好是您的领域。 另一个模型可以是完全不同的对象类型,正在处理另一个对象。在这种情况下,它们都是您的领域的一部分,并被命名为“领域模型 - 对象”。

希望这个答案详尽且清晰明了!


8

我的理解(简略版)如下:

(MVC) 模型对象:

  • 在某些应用场景下代表一些事物,例如 PersonEditModelPersonViewModel 或仅仅是 PersonModel
  • 没有业务逻辑
  • 可能会被一些验证逻辑等等所涉及
  • 用于在不同的应用程序层之间提供数据,例如 MVC Controller <-> MVC View

领域对象:

  • 表示一些业务对象(问题域中的真实世界对象)
  • 具有业务逻辑
  • 不允许无效的对象状态,具有方法以正确更改对象的状态
  • 用于封装与其相关的业务逻辑
  • 不需要用于持久化数据(甚至不应该)

DTO (数据传输对象):

  • 类似于模型对象,但应该具有扁平的结构
  • 仅包含简单类型属性/字段(字符串、数字、日期时间、布尔值)
  • 用于跨应用程序边界传输数据,例如在 Web 服务器和 Web 浏览器之间

6

MVC和DDD可以一起使用。我们在DDD和MVC中都称之为“模型”的内容实际上是相同的:抽象层。以下是一些示例的伪代码。

Model View Controller (MVC)

Model View Controller(MVC)架构将软件分为三个部分:

模型层

MVC架构中的模型层是存储逻辑的位置。在这个层次上,我们有我们的模型和业务逻辑。

class Car {

  String color;
  String year;  

  Cat(color, year) {
    this.color = color;
    this.year = year;
  }

  //getters & setters
}

一个简单的汽车抽象。

class CarService {

  save(car) {
    if(car.getColor() != null && car.getYear() != null) {
      methodToSave(car);
    } else {
      throwsException();
    }
  }

  find(car) {
    return methodToFind(car);
  }

  update(car) {
    assertThatExists(car);
    methodToSave(car);
  }

  delete(car) {
    assertThatExists(car);
    methodToDelete(car);
  }
}

使用服务实现简单的汽车CRUD

视图层

视图层是用户界面所在的地方。这里是用户可以与系统交互的地方,当用户执行操作时将触发控制器,控制器将通知模型层并请求数据。视图层可以存在于应用程序的客户端或服务器端(例如:Java Server Faces (JSF) 作为服务器端,ReactJS 作为客户端)。无论如何,即使视图层位于客户端,客户端也需要请求服务器端发送请求。这可以通过Web应用程序的HTTP请求来完成。

<theCarPage>
  <theCar>
    getTheCarOnLoad();
  </theCar>
</theCarPage>

汽车伪页面。

控制器层

控制器层基本上从视图接收输入,然后将数据转换并发送到模型层,反之亦然。

class CarController {
  
  @OnLoadingTheCarPage
  getTheCarOnLoad() {
     return theCar();
  }

}

领域驱动设计(DDD)

领域驱动设计是一个概念: DDD的基础在于类、类变量和类方法必须与其核心业务领域相匹配。

领域驱动设计居住在“M”中

在这种情况下,当应用MVC架构时,领域驱动设计位于MVC架构的模型层中。如前所述,模型层是应用程序业务逻辑所在的地方。

无论你是否有实体,它们仍然是模型。模型只是现实世界中某个东西的抽象表示。如果抽象化,猫可以成为一个模型:

装载汽车的方法。

class Cat {
  
  String color;
  String age;  

  Cat(color, age) {
    this.color = color;
    this.age = age;
  }

  //getters & setters
}

简单的猫抽象。它是猫的模型。

DDD 实体

在领域驱动设计中,我们有实体,也称为模型。它们之间的区别在于实体是可识别的。如果您拥有一个可识别且可以持久化的类,则它是实体。实体仍然是一种模型。

@AnEntity
@ThisCanBePersisted
class Cat {
  
  @ThisIsAnId
  @ThisValueIncrementsAutomatically   
  @PersistentProperty
  Long id;
  @PersistentProperty
  String color;
  @PersistentProperty
  String age;  

  Cat(color, age) {
    this.color = color;
    this.age = age;
  }

  //getters & setters
}

一个简单的实体。实体是模型。

数据传输对象 (DTO)

数据传输对象本身没有任何逻辑,在传输数据从一个端点到另一个端点时,它们仅用作容器。通常情况下,企业实体本质上不可序列化,因此我们需要一种方式,只发送需要发送到客户端的数据。 由于模型可能具有敏感数据或者我们不想在请求中共享的数据,例如考虑我们的 Cat Model,我们可以创建一个不共享 Cat ID 的 DTO:

class CatDTO {

  String color;
  String age;  

  //getters & setters
}

一种用于猫咪的数据传输对象。我们只需要它的属性以及获取和设置这些属性的方法。我们不想分享它的ID。

因此,如果我们需要通过REST从客户端请求所有猫咪的列表,则应该请求响应我们的CatDTO而不是Cat Entity的端点:

[
  Cat {
    "color": "yellow",
    "age": "1"
  },
  Cat {
    "color": "black",
    "age": "4"
  }
]

这就是客户可以看到的所有数据。


到目前为止,这个特定问题的最佳答案。非常感谢! - Arif Garayev

5

大多数对象的定义是根据对象使用的位置而不同:

模型:是在客户端或服务器中使用对象通用定义。

  1. 模型视图:大多数情况下是在客户端中使用的对象
  2. 领域对象:是在服务器中使用并将数据传输到数据库的对象
  3. 数据传输对象(DTO):是一种从一个对象传输数据到另一个对象的对象,特别是在API调用中获取数据时(例如:在api GET方法调用中获取数据,您不应该将数据库模型提供给客户端,为此目的您使用dto)。

注意:这些定义在大多数情况下是正确的,但在某些情况下不实用。


2

1) 不是的,这是ViewModel的定义。MVC模型对象和领域对象是相同的。
2) 领域模型(对象)总是存在的,业务逻辑是可选的。
3) 如果领域对象中没有业务逻辑,则自动变成DTO。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接