MVC: 业务逻辑放在哪里?

首先,我已经看到了许多这方面的问题,但没有足够的理由支持这一点。如果我的问题不够好,应该删除,我会理解。

例如,我看了一下 这个,一个45 + 的投票回答说,他建议你把业务逻辑放在模型中,这听起来很合乎逻辑。

然而,我的第一个大型项目,我已经完全与我的 BL 在控制器,因为我没有问这些事情,看看它是如何在 AccountController中完成的,这是自动添加的,如果你选择 MVC 与形式认证。所有的方法看起来都充满了 BL。或者也许这是最少量的代码可以被添加,我忽略的东西?

一个人在 youtube 上问我,他把所有的逻辑都放到他的模型中是否正确,一开始我并不这么认为!然后我开始想,也许他是对的! ?

那么,毕竟,我该把我的业务逻辑放在哪里呢?如果是在模型类中,那么在一个处于控制器中的方法中,有多少代码应该被认为是一个健康的数量?只需要一行代码就可以从控制器中的模型中调用某个方法,然后返回到视图?

72027 次浏览

I like to keep my models clean I.e. Just with properties and no business logic. I always think its good to inject dependencies into the controller and these dependencies contain the logic I perform on my models. I like to adhere to the single responsibility principle where possible and I find that models with tons of methods get bloated very quickly. There's pros and cons for both, injecting a lot of dependencies has an overhead yet allows to test in isolation and keeps classes simple and you'll end up having leaner controllers. Despite my logic not actually existing on my model as members of the class, its still business logic. I tend to not have business logic defined in the controller as mocking things like the Httpcontext are a bit of a nightmare and unnecessary.

The business logic belongs to the problem domain and everything that belongs to the problem domain goes to the model in MVC.

The controller should be responsible for passing the data from the model to the view and from the view back to the model. The controller is therefore the bridge between what the user interacts with and how the program models and stores the state of the problem. The plumbing, so to speak.

The key here is the distinction between the business logic and the plumbing logic. In my opinion, what the autogenerated Account Controller does is mostly plumbing, not really business logic. Keep in mind that the plumbing logic isn't necessarily short at all, so you don't need to impose artificial limits (like "X number of calls at most in the controller").

I prefer to put domain logic in the model for a couple of reasons.

  1. The model should have no UI code in it and thus be easier to test. Whenever possible, I like to have a fully working (meaning complete test coverage) model before writing any UI code. The controller can trust that the model is doing the right thing and just deal with UI concerns.

  2. If you put domain logic in a controller, it's not as easy to share between different apps, or even between different controllers.

I like to keep my models clean as well (ref: @Mark Walsh). The problem of not being able to reuse logic embedded in controllers can easily be overcome through dependency injection, or, if you think there would be too much of that, expose your business/domain logic through interfaces and use the façade pattern in the controllers. That way you get the functionality you need, but keep both the controllers and model nice and clean.

I would also prefer to keep models clean. The MVC controllers should be used only to make calls and should also be kept clean. So depending upon its reusability, sensitivity and relevance the business logic can be written in

1.WebApi Controller: The advantage of using a webapi controller is that you can expose these as services later to other devices making your code reuseable.

2. BAL / Common commonent: There are some logics which have specific usage and cannot be exposed as an api, can be pushed in this class.

3. Repository: All the database related queries are added in a repository. There can be a generic repository which will implement all the functions (CRUD operations)or specific repositories for each table. Depending upon the operations to be performed.

My team when moved to mvc from webforms (asp.net) did alot of research and came up with the following structure. According to me its not about how big or small the application is. Its about keeping the code clean and clear.

DALProject

AccountsDAL.cs --- > Calls SP or any ORM if ur using any

BLLProject

AccountsBLL.cs ---> Calls DAL

WebProject

Model
AccountsModel --- > Contains properties And call BLL
Controllers
IndexController ---> Calls Models and returns View
Views
Index

Controllers should be responsible for the data passing between model and view. Other than that it there should not be any unnecessary code. For example if you are logging it should be done at model level rather than controller.

I know that it's a question about MVC, but I think the example I'm giving (Web API) will be usefull.

I'm developing my first Web API and I'm re-using the business logic from other applications. To be specific, it comes from an external DLL, so my API is used just to "talk" with a SAP solution, receiving requests from PO and send responses back.

How could I put my logic (already implemented) into my controller? I don't need it. My controller will only receive, validate requests and compose responses to send data back.

I'm working with ViewModel classes and all they must have is a mapping function, just to read information from TransferObjects (that comes from the external DLL) and translate to a ViewModel.

I'm not comfortable with my application (in this case Web API) holding the business logic, I think that the re-usability will be lost in this way.

I'm treating my business logic as a dependency that I inject into controller.

I've done a lot of refactoring in the legacy to provide a Unit Testable solution, I had to create a lot of interfaces and implement some design patterns into the legacy to provide this solution.

In my point of view, the business layer must be apart of the application, preferably in another class library. So you will have a real separation concept implemented in your application.

Of course, if your CORE (business) is your application (API/WebSite), the business rules will be implemented into your MVC classes. But in the future if you want to develop a new app and some business rules are the same, I bet you will have a lot of problems just to maintain the same logic implemented in both applications.

There seems to be some confusion around this topic. Mostly it seems that people tend to confuse the MVC pattern with N-tier architecture as an either/or situation. The reality is that the two approaches can be used together, but one is not dependent on the other and neither is required.

N-tier architecture is concerned with separating an application into multiple tiers. A simple example is where the application is split into a presentation layer, a business logic layer, and a data access layer.

MVC is a design pattern dealing with the presentation layer of an application. It is entirely possible to design an application following an MVC approach without separating business logic and data access logic from the presentation layer and thus end up with a single tier design.

The result, if you are following an MVC approach without also separating the application into tiers is that you end up with Models, Views, and Controllers that have bits of business rules and data access logic mixed in with the rest of the logic.

By definition, in an N-tier architecture, the presentation tier is only supposed to be able to communicate with the business logic layer so it should hold that any of the MVC components can only communicate with the business logic layer.

If you are building an application that does not involve presentation, and thus not a presentation layer, you should not have to concern yourself with the MVC pattern. However, you very well may still split your application into multiple tiers and thus follow an N-tier design even though there is no presentation layer involved.

Generally speaking, business logic shouldn't reside in any of the MVC players; it should only be consumed by your controller actions.

As many have mentioned, it's best to create a library to host business logic as a set of client agnostic, reusable components.

When done this way, we greatly increase reusability, compatibility, scalability, and testability with our software. We also reduce our dependence on certain framework features, which makes it easier to migrate to newer / different technologies.

Abstracting our business logic into a stand alone assembly (or assemblies) has served us well over the years. Our business logic can then be consumed by virtually any .NET technology (ASP.NET MVC/API/Core, WPF, Win Forms, WCF, UWP, WF, Console, etc.).

In addition, we like our middle tier to handle business rule and validation logic to reduce our dependencies on the .NET MVC Framework's. For example, we avoid using the .NET MVCs validation helpers and instead rely on our own. This is another factor that allows us to easily consume our business logic from any .NET technology.

Logically designing our middle tier this way has allowed us to easily achieve this physical architecture:

enter image description here

It was written with Peasy.NET, and has served us well over the years. So well in fact that we decided to open source it.

If anyone is curious as to what our middle tier looks like, here is a sample of a client agnostic, business layer. It also showcases the consumption of it by multiple .NET clients (ASP.NET MVC, Web Api, and WPF).

Hope this helps someone!

As ahanusa wrote, you should put your business logics into separate DLL or separate directory.
I often use a directory named Logics at same level of Models and Controllers where I put classes which do business logics.
In this way I let both models and controllers clean.

The general answer I have is that business logic normally fits into two categories:

Object Oriented Business Logic: Gets modeled as objects (in the model), usually injected as repositories.

Procedural Business Logic: Goes in a service with an interface that can be injected into a controller.

Controller Logic: Logic that controls how commands are received and passed to the models/services, then how those results are passed to the view.

Controllers should have no business logic, it's a very specific part of a design pattern for controlling how a user interface passes input off to the models that handle business logic (or services if your problems are more procedural in nature).

The business logic should not go in your Models Views or Controllers. There should be a separate Business Logic Layer ; the sole purpose of this layer is to handle your business logic. This is more in-line with SOLID.

If you were to put your business logic in M V or C, you end up with code that is difficult to test / reuse.

What about putting the logic in the Models?

That is a bad solution.

You will end up in a Dependency Hell where objects rely on objects. enter image description here

Even if you have a dead simple function, you'll still have to satisfy all the dependencies to call it.

It will also cause unnecessary and unused data to be passed around for no reason. This could also affect performance depending on how bad it gets.

I should also mention that unit testing becomes a pain in the a** because you have to mock multiple objects just to test a simple function.

Principles of Clean Code Applicable

  1. Classes / Functions take only what they need to get the job done.
  2. Functions should take 3 parameters or less if possible
  3. Name classes / functions / variables intelligently (follow Microsoft's standards)
  4. Do not couple business logic to Model View or Controller

Controllers

In your controller, you should be able to use dependency injection to inject the business logic layer. Make sure you controller is only used to route information to the business logic layer. The controller should NOT have business logic directly in it. Any validation needs to be handled by IValidatable on the Model. Any business logic needs to be routed to a separate layer.