Currently in my application and using the unit of work pattern and generic repository, all my controllers contain all of the business logic. I'm in the process of putting everything over to use ViewModels instead of the straight Model.
While this is a good idea, now comes a question that can significantly separate my business logic in the controllers. For controll开发者_StackOverflow社区ers and ViewModels, which should contain most of the business logic?
I've tried a few ways to get my ViewModels to practically contain all the business logic. However, I do have to have an argument in my ViewModel's constructor that takes a Unit of Work. Is this a good idea?
My code smell tells me it is. However, I am just a little worried how this will be in consistency with controllers that perform actions that do not need ViewModels. Simply put, actions that do not require to pass a Model/ViewModel to a View; this case happens on actions that do redirects to other actions. Which means, my business logic can either stay in that action or I could separate that business logic into a function.
What is the best practice here?
I can't say that my approach is a best practice, but I prefer to put any business logic in a separate "service" layer.
I tend to use the ViewModel to only store the properties required for a specific view. If there are any methods on the ViewModel, they are likely just to retrieve collections related to that view.
I keep my controllers lightweight by trying to limit them to validation and redirection/displaying views as much as possible.
So if I have any complex logic, I'll have a controller action call out to a separate service just to handle that logic. This way the logic is isolated which makes it easier to test since there's no longer a need to create a controller or ViewModel to test it. It's also easier to reuse a service than to pick apart a ViewModel.
Hopefully this helps. Good luck.
For controllers and ViewModels, which should contain most of the business logic?
None of those.
I've tried a few ways to get my ViewModels to practically contain all the business logic. However, I do have to have an argument in my ViewModel's constructor that takes a Unit of Work. Is this a good idea?
imho It's a very bad idea. First of all you are breaking several of the SOLID principles. Bundling all code into the view model makes it hard to test. What if you want to use some of the business logic in another view? Do you duplicate that code?
What is the best practice here?
Let's go back to the MVC pattern first. It's a quite wide definition but knowing it should give you a feeling of what you should place where.
The "Model" in MVC is really everything that is used to pull the data together. It can be webservices, a business layer, repositories etc.
The view is all code that generates the HTML (since we are talking about web).
The controller should be considered to be a glue between the model and the view. Hence it should take the information from the Model and transform it into something usable by the view.
The problem with that structure is that it's quite easy to "leak" layer specific information into the other parts of the pattern. Hence Microsoft introduced ViewModels into their implementation of MVC.
In this way we can remove all rendering logic from the views and put it into the ViewModel. Instead of doing this in your view:
<span>@(model.Age == 0 ? "n/a" : model.Age)</span>
you put that code inside the ViewModel instead and simply call @model.Age
. In this way you don't have to duplicate that code in all views that are using your view model.
The answer to your question about the ViewModel is that it should only contain logic which is used to render the information from the "Model" properly.
As for the controller, I would not put any business logic into it either. First of all, it makes it very hard to test your logic. Then you add more responsibilities to it (and by doing so breaking SRP). The only logic that is valid in the controller is to take the information from the ViewModel and transform it into something usable by the "Model" and vice versa.
Hope that answers your question.
Update
I would create a separate project and add classes to it. Then just add a reference from your webproject and call those classes in the controllers.
I would also start using an inversion of control container to get those dependencies created for me automagically.
Autofac can both discover your services for you (zero-configuration) and inject itself into MVC.
To follow the separated interface pattern create the following projects:
- YourProject.BusinessLayer <-- Add your classes here
- YourProject.BusinessLayer.Specification <-- Add you interfaces that defines your business layer here.
- YourProject.Mvc <-- The MVC project.
The "Specification" project can be used to make it easier to test things and to make it easier to switch implementation (might be only a few classes and not necessarily the entire business layer). Read up on "Seperated Interface Pattern"
Model-View-View Model (MVVM) is a design pattern for building user interfaces. Your View Model is a pure-code representation of the data and operations on a UI. Thus it should contain logic related to that UI.
For example:
if you’re implementing a list editor, your view model would be an object holding a list of items, and exposing methods to add and remove items.
From Wikipedia:
ViewModel: the ViewModel is a “Model of the View” meaning it is an abstraction of the View that also serves in data binding between the View and the Model. It could be seen as a specialized aspect of what would be a Controller (in the MVC pattern) that acts as a data binder/converter that changes Model information into View information and passes commands from the View into the Model. The ViewModel exposes public properties, commands, and abstractions. The ViewModel has been likened to a conceptual state of the data as opposed to the real state of the data in the Model.[7]
your controller call the UoW to get the data needed to construct your viewmodel. you may call more than 1 method of your UoW
then you pass all your needed data to your viewmodel constructor. (passing the Uow to de viewmodel sounds like really bad)
If you need some complex 'logic' on your controller calling lot of methods from the UoW, etc, you should consider creating another repository or a business logic only layer that does all the hard work and you call it from your controller like
SomeClass something = Uow.BLGoodName.DoSomeFancyStuff(params ..)
ViewData.model = new ControllerActionViewModel(something);
Return View();
精彩评论