“业务逻辑层”适合于MVC应用程序吗?

时间:2022-01-21 08:21:30

First, before anyone screams dupe, I had a hard time summarizing it in a simple title. Another title might have been "What is the difference between a domain model and MVC model?" or "What is a model?"

首先,在有人尖叫着被骗之前,我很难用一个简单的标题来概括它。另一个标题可能是“领域模型和MVC模型的区别是什么?”或者“什么是模型?”

Conceptually, I understand a Model to be the data used by the views and controller. Beyond that, there seems to be a great deal of differing opinions on what makes up the model. What's a domain model, versus an app model, vs a view model, vs a service model, etc..

概念上,我理解模型是视图和控制器使用的数据。除此之外,对于什么构成了这个模型,似乎有很多不同的观点。什么是领域模型,什么是应用模型,什么是视图模型,什么是服务模型等等。

For example, in a recent question I asked about the repository pattern, I was told point blank that the repository is part of the model. However, I have read other opinions that the model should be seperated from the persistence model and the business logic layer. After all, isn't the Repository pattern supposed to decouple the concrete persistence method from the model? Other people say there is a difference between the Domain model and the MVC model.

例如,在最近的一个关于存储库模式的问题中,我被直接告知,存储库是模型的一部分。但是,我已经阅读了其他观点,认为模型应该与持久性模型和业务逻辑层分离。毕竟,存储库模式不应该将具体的持久性方法与模型分离吗?另一些人说域模型和MVC模型之间是有区别的。

Let's take a simple example. The AccountController that is included with the MVC default project. I've read several opinions that the Account code included is of poor design, violates SRP, etc.. etc.. If one were to design a "proper" Membership model for an MVC application, what would that be?

让我们举一个简单的例子。MVC默认项目中包含的AccountController。我读过一些观点,其中包含的帐号代码设计不良,违反SRP等。等。如果要为MVC应用程序设计一个“合适的”成员模型,那会是什么呢?

How would you seperate the ASP.NET services (Membership provider, role provider, etc..) from the model? Or would you at all?

你如何区分ASP。NET服务(成员提供者、角色提供者等)来自模型吗?或者你愿意吗?

The way I see it, the model should be "pure", perhaps with validation logic.. but should be seperate from business rules (other than validation). For example, let's say you have a business rule that says someone must be emailed when a new account is created. That doesn't really belong in the model in my view. So where does it belong?

在我看来,模型应该是“纯的”,也许是用验证逻辑。但是应该与业务规则分开(除了验证)。例如,假设您有一个业务规则,规定在创建新帐户时必须通过电子邮件发送某人。在我看来,这并不属于模型。那么它属于哪里呢?

Anyone care to shed any light on this issue?

有人想了解一下这个问题吗?

4 个解决方案

#1


66  

The way I have done it - and I'm not saying it is right or wrong, is to have my View and then a model that applies to my view. This model only has what is relevant to my view - including data annotations and validation rules. The controller only houses logic for building the model. I have a service layer which houses all business logic. My controllers call my service layer. Beyond that is my repository layer.

我这样做的方式——我并不是说它是对的或错的,是有我的观点,然后有一个适用于我观点的模型。这个模型只具有与我的视图相关的内容——包括数据注释和验证规则。控制器只包含用于构建模型的逻辑。我有一个包含所有业务逻辑的服务层。控制器调用服务层。除此之外是我的存储库层。

My domain objects are housed separately (in their own project, actually). They have their own data annotations and validation rules. My repository validates the objects in my domain before saving them into the database. Because every object in my domain inherits from a base class which has validation built in, my repository is generic and validates everything (and requires it inherits from the base class).

我的域对象被单独放置(实际上是在它们自己的项目中)。它们有自己的数据注释和验证规则。我的存储库在保存到数据库之前对域中的对象进行验证。因为我的域中的每个对象都继承自一个基类,基类中有内置的验证,所以我的存储库是通用的,并且验证所有的东西(并且需要它从基类继承)。

You might think that having two sets of models is duplication of code, and it is to an extent. But, there are perfectly reasonable instances where the domain object is not appropriate for the view.

您可能认为拥有两组模型在某种程度上是重复的代码。但是,在一些完全合理的实例中,域对象不适合视图。

Case in point is when working with credit cards - I have to require a cvv when processing a payment, but I cannot store the cvv (it is a $50,000 fine to do so). But, I also want you to be able to edit your credit card - change of address, name, or expiration date. But you aren't going to give me the number or the cvv when editing it, and I certainly am not going to put your credit card number in plain text on the page. My domain has these values required for saving a new credit card because you give them to me, but my edit model doesn't even include the card number or cvv.

一个恰当的例子是在使用信用卡时——我在处理支付时需要一个cvv,但是我不能存储cvv(这样做要罚款5万美元)。但是,我也希望您能够编辑您的信用卡——地址、姓名或到期日的变更。但是你不会在编辑的时候给我数字或cvv,我当然不会把你的信用卡号码用纯文本写在页面上。我的域拥有保存新信用卡所需的这些值,因为您将它们提供给我,但我的编辑模型甚至不包括卡号或cvv。

Another benefit to so many layers is that if architected correctly, you can use structuremap or another IoC container and swap out pieces without detrimentally affecting your application.

如此多的层的另一个好处是,如果架构正确,您可以使用structuremap或另一个IoC容器并交换部分,而不会对应用程序产生有害的影响。

In my opinion, controller code should only be code targeted at the view. Show this, hide that, etc. The service layer should house the business logic for your app. I like having all of it in one place so it's easy to change or tweak a business rule. The repository layer should be relatively dumb - devoid of business logic and only query your data and return your domain objects. By separating the view models from the domain model, you have much more flexibility when it comes to custom validation rules. It also means you don't have to dump every piece of data into your view in hidden fields and push it back and forth between the client and server (or rebuild it on the backend). Your view model will then house only the information relevant to the view - and it can be customized to have bools for view logic or counts or enums so that the view itself isn't cluttered up with complicated logic statements like

在我看来,控制器代码应该只是针对视图的代码。显示这个,隐藏那个等等。服务层应该包含你的应用的业务逻辑。存储库层应该是相对哑的——没有业务逻辑,只查询数据并返回域对象。通过将视图模型与域模型分离,在涉及自定义验证规则时,您将拥有更大的灵活性。这也意味着您不必在隐藏字段中将每一块数据转储到您的视图中,并在客户端和服务器之间来回推(或者在后端重新构建)。您的视图模型将只存储与视图相关的信息,并且可以定制为查看逻辑或计数或枚举的bools,这样视图本身就不会被复杂的逻辑语句堆积起来。

<% if (!String.IsNullOrEmpty(Model.SomeObject.SomeProperty) && 
    Model.SomeObject.SomeInt == 3 && ...) { %>

While everything seems spread out and over-layered, it has a purpose for being architected this way. Is it perfect? not really. But I do prefer it to some past designs of calling repositories from the controller and having business logic mixed in the controller, repository, and model.

虽然所有的东西看起来都是分散的,而且是分层的,但是它的目的是这样的。它是完美的吗?不是真的。但是与以前从控制器调用存储库并在控制器、存储库和模型中混合业务逻辑相比,我确实更喜欢它。

#2


16  

I too often wondered how exactly the MVC elements fit in a traditional web application structure, where you have views (pages), controllers, services, and data objects (model). As you said, there are many versions of that.

我经常想知道MVC元素是如何与传统的web应用程序结构相适应的,其中包含视图(页面)、控制器、服务和数据对象(模型)。正如你所说,有很多版本。

I believe the confusion exists because of the above stated, widely accepted architecture, which uses the "anemic domain model" (alleged)-anti pattern. I won't go into much details about the "anti-patternness" of anemic data model (you can look at an effort of mine to explain things here (Java-based, but relevant for any language)). But in short, it means that our model holds only data, and business logic is placed in services/managers.

我认为,之所以会出现这种混乱,是因为上述被广泛接受的架构使用了“贫血域模型”(所谓的)-反模式。我不会详细介绍贫血数据模型的“反模式性”(您可以查看我在这里解释的工作(基于java,但与任何语言相关)。但简而言之,这意味着我们的模型只保存数据,业务逻辑放在服务/管理器中。

But let's assume we have domain driven architecture, and our domain objects are the way they are expected to be - having both state and business logic. And in this domain-driven perspective things come into place:

但是,让我们假设我们有域驱动的体系结构,我们的域对象是它们期望的方式——具有状态和业务逻辑。在这个领域驱动的视角下,事情会发生:

  • the view is the UI
  • 视图是UI。
  • the controller gathers the inputs of the UI, invokes methods on the model, and sends back a response to the UI
  • 控制器收集UI的输入,调用模型上的方法,并返回对UI的响应。
  • the model is our business components - holding the data, but also having business logic.
  • 模型是我们的业务组件——保存数据,但也有业务逻辑。

I guess that answers your main questions. Things get complicated when we add some more layers, like the repository layer. It is often suggested that it should be invoked by the business logic placed in the model (and hence each domain object has a reference to a repository). In the article of mine that I linked I argue that this is not quite a best practice. And that in fact it is not a bad thing to have a service layer. By the way, domain-driven design does not exclude the service layer, but it is supposed to be 'thin', and only coordinating domain objects (so no business logic there).

我想这能回答你的主要问题。当我们添加更多的层,比如存储库层时,事情就变得复杂了。通常建议应该由放置在模型中的业务逻辑来调用它(因此每个域对象都有一个对存储库的引用)。在我链接的文章中,我认为这并不是最好的做法。事实上,有一个服务层并不是坏事。顺便说一下,域驱动的设计并不排除服务层,但它应该是“瘦”的,并且只协调域对象(因此没有业务逻辑)。

For the anemic data model paradigm, which is widely adopted (for good or for bad), the model would be both the service layer and your data objects.

对于被广泛采用的贫血数据模型范例(无论是好是坏),模型将是服务层和数据对象。

#3


3  

In my opinion,

在我看来,

Model -

模型-

Should not contain business logic, it should be pluggable(WCF like scenario). It is used to bind to view so, it should have properties.

不应该包含业务逻辑,它应该是可插拔的(类似于WCF的场景)。它用于绑定到view,所以应该有属性。

Business Logic -

业务逻辑,

It should be placed at "Domain Services Layer", it is separate layer altogether. Also, will add one more layer here "Application Services".

它应该放在“域服务层”,它是完全独立的层。此外,还将在“应用服务”中添加一个层。

App Services talks to Domain Services layer to apply business logic and then lastly return the Model.

应用服务与域服务层进行对话,应用业务逻辑,最后返回模型。

So, Controller will ask Application Service for Model and the flow will go like,

控制器会向应用服务请求模型,流程会这样,

    Controller->Application Services(using domain services)->Model

#4


2  

The MVC pattern and the Asp.net framework makes no distinction on what the Model should be.

MVC模式和Asp.net框架没有区分模型应该是什么。

MS's own examples include persistence classes in the model. Your question about membership being in the model. This depends. Are classes in your model owned by something? Is there a link between who logs in and what data is displayed? Is there filtering of data part of a permissions system that is editable? Is who last updated or edited an object part of your domain as in somebody else needs to see it or something for backend support?

MS自己的示例包括模型中的持久性类。你关于模型中的成员的问题。这取决于。模型中的类是否为某物所有?登录者和显示的数据之间是否存在关联?是否有对可编辑的权限系统的数据部分进行过滤?谁最近更新或编辑了您的域的对象部分,就像其他人需要看到它或其他东西的后端支持?

The email example is also it depends. Are you familiar with domain eventing or eventing in particular? Do you have a separate service to send emails? Is the act of sending an email part of your domain or is it a application level concern outside of the scope of your system? Does the UI need to know if an email was sent successfully or not? Do emails that fail to send need retries? Does the content of the email sent need to be stored for support or customer service requirements?

电子邮件的例子也是如此。你是否特别熟悉定时器或定时器?你有单独的邮件发送服务吗?发送电子邮件的行为是您的域的一部分,还是在系统范围之外的应用程序级别的关注点?UI是否需要知道电子邮件是否发送成功?发送失败的邮件需要重试吗?发送的邮件内容是否需要存储为支持或客户服务需求?

These types of questions are overly broad and subjective but I'm answering so you and everybody who voted you up can understand this.

这些类型的问题过于宽泛和主观,但我回答的是你和所有投票给你的人都能理解这一点。

Your requirements/timelines/resources all bleed into your system's architecture. Even the revenue model can have an effect. You also have to consider the pattern you are shooting for. DDD is much different than persistence-as-model applications and all the slop in between are also valid for certain apps. Are you shooting for testing the app? All of this has an effect.

您的需求/时间线/资源都渗透到系统的体系结构中。甚至收入模式也会产生影响。你还必须考虑你要拍摄的图案。DDD与“持久化-as-model”应用程序大不相同,两者之间的所有残余物也适用于某些应用程序。你是为了测试应用程序而拍摄的吗?所有这些都有影响。

#1


66  

The way I have done it - and I'm not saying it is right or wrong, is to have my View and then a model that applies to my view. This model only has what is relevant to my view - including data annotations and validation rules. The controller only houses logic for building the model. I have a service layer which houses all business logic. My controllers call my service layer. Beyond that is my repository layer.

我这样做的方式——我并不是说它是对的或错的,是有我的观点,然后有一个适用于我观点的模型。这个模型只具有与我的视图相关的内容——包括数据注释和验证规则。控制器只包含用于构建模型的逻辑。我有一个包含所有业务逻辑的服务层。控制器调用服务层。除此之外是我的存储库层。

My domain objects are housed separately (in their own project, actually). They have their own data annotations and validation rules. My repository validates the objects in my domain before saving them into the database. Because every object in my domain inherits from a base class which has validation built in, my repository is generic and validates everything (and requires it inherits from the base class).

我的域对象被单独放置(实际上是在它们自己的项目中)。它们有自己的数据注释和验证规则。我的存储库在保存到数据库之前对域中的对象进行验证。因为我的域中的每个对象都继承自一个基类,基类中有内置的验证,所以我的存储库是通用的,并且验证所有的东西(并且需要它从基类继承)。

You might think that having two sets of models is duplication of code, and it is to an extent. But, there are perfectly reasonable instances where the domain object is not appropriate for the view.

您可能认为拥有两组模型在某种程度上是重复的代码。但是,在一些完全合理的实例中,域对象不适合视图。

Case in point is when working with credit cards - I have to require a cvv when processing a payment, but I cannot store the cvv (it is a $50,000 fine to do so). But, I also want you to be able to edit your credit card - change of address, name, or expiration date. But you aren't going to give me the number or the cvv when editing it, and I certainly am not going to put your credit card number in plain text on the page. My domain has these values required for saving a new credit card because you give them to me, but my edit model doesn't even include the card number or cvv.

一个恰当的例子是在使用信用卡时——我在处理支付时需要一个cvv,但是我不能存储cvv(这样做要罚款5万美元)。但是,我也希望您能够编辑您的信用卡——地址、姓名或到期日的变更。但是你不会在编辑的时候给我数字或cvv,我当然不会把你的信用卡号码用纯文本写在页面上。我的域拥有保存新信用卡所需的这些值,因为您将它们提供给我,但我的编辑模型甚至不包括卡号或cvv。

Another benefit to so many layers is that if architected correctly, you can use structuremap or another IoC container and swap out pieces without detrimentally affecting your application.

如此多的层的另一个好处是,如果架构正确,您可以使用structuremap或另一个IoC容器并交换部分,而不会对应用程序产生有害的影响。

In my opinion, controller code should only be code targeted at the view. Show this, hide that, etc. The service layer should house the business logic for your app. I like having all of it in one place so it's easy to change or tweak a business rule. The repository layer should be relatively dumb - devoid of business logic and only query your data and return your domain objects. By separating the view models from the domain model, you have much more flexibility when it comes to custom validation rules. It also means you don't have to dump every piece of data into your view in hidden fields and push it back and forth between the client and server (or rebuild it on the backend). Your view model will then house only the information relevant to the view - and it can be customized to have bools for view logic or counts or enums so that the view itself isn't cluttered up with complicated logic statements like

在我看来,控制器代码应该只是针对视图的代码。显示这个,隐藏那个等等。服务层应该包含你的应用的业务逻辑。存储库层应该是相对哑的——没有业务逻辑,只查询数据并返回域对象。通过将视图模型与域模型分离,在涉及自定义验证规则时,您将拥有更大的灵活性。这也意味着您不必在隐藏字段中将每一块数据转储到您的视图中,并在客户端和服务器之间来回推(或者在后端重新构建)。您的视图模型将只存储与视图相关的信息,并且可以定制为查看逻辑或计数或枚举的bools,这样视图本身就不会被复杂的逻辑语句堆积起来。

<% if (!String.IsNullOrEmpty(Model.SomeObject.SomeProperty) && 
    Model.SomeObject.SomeInt == 3 && ...) { %>

While everything seems spread out and over-layered, it has a purpose for being architected this way. Is it perfect? not really. But I do prefer it to some past designs of calling repositories from the controller and having business logic mixed in the controller, repository, and model.

虽然所有的东西看起来都是分散的,而且是分层的,但是它的目的是这样的。它是完美的吗?不是真的。但是与以前从控制器调用存储库并在控制器、存储库和模型中混合业务逻辑相比,我确实更喜欢它。

#2


16  

I too often wondered how exactly the MVC elements fit in a traditional web application structure, where you have views (pages), controllers, services, and data objects (model). As you said, there are many versions of that.

我经常想知道MVC元素是如何与传统的web应用程序结构相适应的,其中包含视图(页面)、控制器、服务和数据对象(模型)。正如你所说,有很多版本。

I believe the confusion exists because of the above stated, widely accepted architecture, which uses the "anemic domain model" (alleged)-anti pattern. I won't go into much details about the "anti-patternness" of anemic data model (you can look at an effort of mine to explain things here (Java-based, but relevant for any language)). But in short, it means that our model holds only data, and business logic is placed in services/managers.

我认为,之所以会出现这种混乱,是因为上述被广泛接受的架构使用了“贫血域模型”(所谓的)-反模式。我不会详细介绍贫血数据模型的“反模式性”(您可以查看我在这里解释的工作(基于java,但与任何语言相关)。但简而言之,这意味着我们的模型只保存数据,业务逻辑放在服务/管理器中。

But let's assume we have domain driven architecture, and our domain objects are the way they are expected to be - having both state and business logic. And in this domain-driven perspective things come into place:

但是,让我们假设我们有域驱动的体系结构,我们的域对象是它们期望的方式——具有状态和业务逻辑。在这个领域驱动的视角下,事情会发生:

  • the view is the UI
  • 视图是UI。
  • the controller gathers the inputs of the UI, invokes methods on the model, and sends back a response to the UI
  • 控制器收集UI的输入,调用模型上的方法,并返回对UI的响应。
  • the model is our business components - holding the data, but also having business logic.
  • 模型是我们的业务组件——保存数据,但也有业务逻辑。

I guess that answers your main questions. Things get complicated when we add some more layers, like the repository layer. It is often suggested that it should be invoked by the business logic placed in the model (and hence each domain object has a reference to a repository). In the article of mine that I linked I argue that this is not quite a best practice. And that in fact it is not a bad thing to have a service layer. By the way, domain-driven design does not exclude the service layer, but it is supposed to be 'thin', and only coordinating domain objects (so no business logic there).

我想这能回答你的主要问题。当我们添加更多的层,比如存储库层时,事情就变得复杂了。通常建议应该由放置在模型中的业务逻辑来调用它(因此每个域对象都有一个对存储库的引用)。在我链接的文章中,我认为这并不是最好的做法。事实上,有一个服务层并不是坏事。顺便说一下,域驱动的设计并不排除服务层,但它应该是“瘦”的,并且只协调域对象(因此没有业务逻辑)。

For the anemic data model paradigm, which is widely adopted (for good or for bad), the model would be both the service layer and your data objects.

对于被广泛采用的贫血数据模型范例(无论是好是坏),模型将是服务层和数据对象。

#3


3  

In my opinion,

在我看来,

Model -

模型-

Should not contain business logic, it should be pluggable(WCF like scenario). It is used to bind to view so, it should have properties.

不应该包含业务逻辑,它应该是可插拔的(类似于WCF的场景)。它用于绑定到view,所以应该有属性。

Business Logic -

业务逻辑,

It should be placed at "Domain Services Layer", it is separate layer altogether. Also, will add one more layer here "Application Services".

它应该放在“域服务层”,它是完全独立的层。此外,还将在“应用服务”中添加一个层。

App Services talks to Domain Services layer to apply business logic and then lastly return the Model.

应用服务与域服务层进行对话,应用业务逻辑,最后返回模型。

So, Controller will ask Application Service for Model and the flow will go like,

控制器会向应用服务请求模型,流程会这样,

    Controller->Application Services(using domain services)->Model

#4


2  

The MVC pattern and the Asp.net framework makes no distinction on what the Model should be.

MVC模式和Asp.net框架没有区分模型应该是什么。

MS's own examples include persistence classes in the model. Your question about membership being in the model. This depends. Are classes in your model owned by something? Is there a link between who logs in and what data is displayed? Is there filtering of data part of a permissions system that is editable? Is who last updated or edited an object part of your domain as in somebody else needs to see it or something for backend support?

MS自己的示例包括模型中的持久性类。你关于模型中的成员的问题。这取决于。模型中的类是否为某物所有?登录者和显示的数据之间是否存在关联?是否有对可编辑的权限系统的数据部分进行过滤?谁最近更新或编辑了您的域的对象部分,就像其他人需要看到它或其他东西的后端支持?

The email example is also it depends. Are you familiar with domain eventing or eventing in particular? Do you have a separate service to send emails? Is the act of sending an email part of your domain or is it a application level concern outside of the scope of your system? Does the UI need to know if an email was sent successfully or not? Do emails that fail to send need retries? Does the content of the email sent need to be stored for support or customer service requirements?

电子邮件的例子也是如此。你是否特别熟悉定时器或定时器?你有单独的邮件发送服务吗?发送电子邮件的行为是您的域的一部分,还是在系统范围之外的应用程序级别的关注点?UI是否需要知道电子邮件是否发送成功?发送失败的邮件需要重试吗?发送的邮件内容是否需要存储为支持或客户服务需求?

These types of questions are overly broad and subjective but I'm answering so you and everybody who voted you up can understand this.

这些类型的问题过于宽泛和主观,但我回答的是你和所有投票给你的人都能理解这一点。

Your requirements/timelines/resources all bleed into your system's architecture. Even the revenue model can have an effect. You also have to consider the pattern you are shooting for. DDD is much different than persistence-as-model applications and all the slop in between are also valid for certain apps. Are you shooting for testing the app? All of this has an effect.

您的需求/时间线/资源都渗透到系统的体系结构中。甚至收入模式也会产生影响。你还必须考虑你要拍摄的图案。DDD与“持久化-as-model”应用程序大不相同,两者之间的所有残余物也适用于某些应用程序。你是为了测试应用程序而拍摄的吗?所有这些都有影响。