This is a question related to how to structure an ASP.NET MVC project for a medium to large application.
I thought I understood the concepts of MVC but after looking into architectures for medium and large applications I am confused. (trying to take into consideration scalability, extensibility and ongoing maintenance)
My confusion come in when I try to think of how to structure an application following guidelines of 'best practices' (from many and numerous sources included printed and web)
Trying to respect things like
- Controllers should be kept very simple
- TDD principles (or at least an approach that will make testing easier in the future)
- Separation of concern
- Services and repositories
- Dependency injection
Now when creating small (basic, simple) MVC apps, then all of this is pretty much done in the same project (I'm talking about Visual Studio Project in this case), and the separation between the MVC "Layers" is pretty much just folders in the VS project (well separate namespaces).
With some of our other projects we have adopted a Service -> repository style, so this one is not going to be any different.
We are using Entity Framework as the DB persistence (DB first approach).
We have separated our DB access (the EF stuff) into another VS project, so we have a Web project and Models (or data) project in the solution.
The web project has the controllers and views, and the data project has the Services, Repositories and the EF stuff.
My confusion is with the models (or perhaps with understanding a Domain Model vs a View model)
If I was to try to follow the methodology (I think), I would have a domain model (the model that the EF and repository layers deal with), and then I would have a view model? (the model that the Controller and view would deal with), now wouldn't these be 90% the same? Isn't this way of separating out the concerns just making you write model code twice? As I am sure I read somewhere that Controllers and Views shouldn't have the Domain model?
One way that we have approached it is the EF makes all its model classes partial. We then extend that same class and add a MetaDataType class to it to make the 'View Model' (add the DataAnnotations to the properties) and then in essence the same model is passed through all layers, but is this 'best' practice (there is a splinter in my mind that this is just not right)
eg
[MetadataType(typeof(Product_Metadata))]
public partial class Product
{
//Pretty much deliberately kept empty, just so
// the EF model class can have the attribute added
//The other side of this partial class is of course in the EF models
}
public class Product_Metadata
{
[Required]
[Display(Name = "Product name")]
public string Name { get; set; }
[Required]
[Display(Name = "Unit Cost")]
public decimal Cost { get; set; }
//etc... for the rest of the properties on the product EF model
}
Maybe this is the 'best' way to attack it but I have not come across this method before.
We are creating all the Services and Repositories as Interfaces and using structure map as the IoC container. Another thing I admit, even though we are using the dependency Injection I am still struggling to come to terms with TDD, feels like to have to write everything twice (whole point of the DI I would think)
I suppose ultimately I am appealing to the willing here at SO that know more than me about architecting large ASP.NET MVC applications for some assistance and guidance. There seems to be a huge wealth of information out there, but all seems to be very conceptual. When I finally come to the implementation I get lost in the concepts.
EDIT
In response to Mr Karl Anderson
- Paging of data in a view - Yes completely agree where this is where a viewmodel is pertinent and makes sense, but again is the CategoryListViewModel which has a List property, is it a list of the viewmodel category or the domain model category
- Mass assignment vulnerability - I would think that this vulnerability would exist with a domain model or a view model (after all how will you set IsAdmin if genuinely needed to be set, surely it would still be on the ViewModel). I would think this would need to be dealt with at a different layer i.e. authorization, so that only uses of a curtain role can only set the IsAdmin
- Displaying view information in a specific format - Surely this is just to do with model binding and/or view html helpers for formatting - i.e. a view and model binding issue only. After all, all models that are rendered through a view, have their properties end up in html and are all string at this point, so returning values have to be parsed anyway, the main principle of model binding, so if I needed a custom one, just write a new model binder.
- Using your domain model as more than just data transfer objects (DTOs) - I actually try to avoid this as much as possible, trying to stick with the fact that models are exactly that, DTOs. But if that scenario came up I would probably write an extension method on the domain model, after all methods don't get serialized anyway, or yes add a view model but it would probably contain a domain model
- Having different abstractions of the same domain model information - Agree in part. I would have a PagedAccountListViewModel (would still contain Domain models) but I would only use one model for the new and update account (I treat a new the same as an update, ones just prepopulated) and it would be the domain model