“业务逻辑层”在 MVC 应用程序中的位置是什么?

首先,在任何人尖叫上当之前,我很难用一个简单的标题来概括它。另一个标题可能是“领域模型和 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..

例如,在我最近提出的一个关于存储库模式的问题中,有人直截了当地告诉我存储库是模型的一部分。但是,我读过其他一些观点,认为模型应该与持久性模型和业务逻辑层分离。毕竟,Repository 模式不是应该将具体的持久化方法从模型中解耦吗?其他人说,域模型和 MVC 模型之间存在差异。

举个简单的例子。MVC 默认项目中包含的 AccountController。我已经阅读了一些意见,包括帐户代码的设计很差,违反 SRP,等等..。等等。如果要为 MVC 应用程序设计一个“适当的”成员资格模型,那会是什么?

如何分离 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?

有人愿意解释一下这个问题吗?

27282 次浏览

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.

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.

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
  • the controller gathers the inputs of the UI, invokes methods on the model, and sends back a response to the 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.

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.

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.

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

<% 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.

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

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?

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?

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.

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.

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.

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

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