ASP.NET MVC Large Project Architecture

architecture asp.net asp.net-mvc-4 c# entity-framework

Question

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
1
15
9/6/2013 3:46:00 AM

Accepted Answer

There is no best practices / architecture. Every design has drawbacks. Regarding your purposed architecture and the 90% duplication of code, here is my thoughts. It is divided as Entity (DTO/model) or services/repository.

Background

The basic concept I usually follow is N-Tier archiecture design. It is basically stated as "separate the layer of domain / business with other layer (UI / Data Access). The main goal is, when your application are migrated to other system (UI / Storage), the business layer remains the same.

If you put 95% of domain logic into business layer (other 5 maybe in database, such as transaction / report generation), then you almost not need to change anything and still have same domain rule. Problem solved for the domain layer, and you only need to conecentrate on the UI or storage (repository).

Usually, the structure of N-tier is as follows:

          entity

        interfaces

DataAccess  |  BusinessLogic

           UI

With each layer are separated by assembly (project/solution), so no coupling between each layer is emphasized.

The Entity Duplication

Now imagine a common "operation message" class. I imagine that class as this:

public class OperationMessage{
    public bool IsError{get;set;}
    public string OperationMessage{get;set;}
}

Feel free to modify the class as to add enum for warning, etc. (this is a code smell using auto property, don't follow if you go for maintenance).

Say that your MVC app has css named "message_error" in which has color:red; and font-weight:bold; property. Usually you want to assign it to the class with property such as CssClassName. You has 3 options in this:

  1. Modify the basic OperationMessage in entity layer

    This is the easiest thing to do. However, you break the n-tier architecture because now your business layer has knowledge about "css" and it refer to web-like architecture. It add logic fo ui-specific (assigning the CssClassName in business layer). If someday you want to migrate it to C# Winform or maybe Windows Mobile / Azure, it tainted the architecture.

  2. Add a new class called WebOperationMessage

    This is what I call ViewModel. Now it became duplicated because it is similiar by 90% with OperationMessage class. But your N-Tier architecture are kept in order. After getting the OperationMessage object from your business layer, you need to do some conversion. That conversion, is what I called Presentation Logic.

  3. Inherit the OperationMessage class

    This is maybe a better approach for entity-type class. It ensures your N-Tier architecture are kept in order, and does not duplicate 90% of the code. I'm not yet found a flaw in this design yet, but maybe there are any in defensive-code style entity. However, you still need to do conversion though.

Services Duplication

The services is already duplicated in interface. However, it is due to achieve the N-Tier architecture, creating persistence ignorant code. It makes them easier to do unit test and mock. I hope the reader already understand for the mocking and unit testing so this answer is still relevant.

But say, if you do not do unit testing or mocking, then do this layering or duplication worth the effort? As quoted from the article,

The choice is whether or not you want to build a layered application. If you want layering, the separation must be strict. If it isn't, it's not a layered application.

In short, as soon that you violates / breaks the layering, you lost the portability. If you violated the BLL / DAL layering, you lost the flexibility to changing the repostiory. If you violated the BLL / PL, you lost the flexibility to migrating your applications from one type of UI to other.

Is it worth it? In some cases, yes. In other cases, such as enterprise applications, usually it is more rigid, and the migration is less likely to happen. However, most of the times the enterprise can expanding and mobility is required. So, the zookeepers must become the rangers.

My 2 cents

3
9/6/2013 1:13:49 PM

Popular Answer

When I finally come to the implementation I get lost in the concepts.

The concepts are very important but also abstract. It's hard to imagine how best to structure your solution until after it's complete (i.e. too late), and no one can really tell you how to structure it because every project is so different.

I would have a domain model [...] and then I would have a view model? [...] wouldn't these be 90% the same?

I believe this is ok. A domain model describes the actual object (usually from a database). A view model describes the information that the view will need to render everything correctly. Both typically contain no logic and only a list of properties. I think it's fine for these to be almost identical. Use Automapper to easily map between models.

Controllers and Views shouldn't have the Domain model?

Most developers prefer this approach, yes. So do I. Views should be given a view model and a controller can simply map between models if needed.

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'

It's an interesting approach, but I can't recommend it. Duplicating models is acceptable.

TDD, feels like to have to write everything twice

Yes, it can. The approach that you're taking separates abstraction and implementation a lot. It does make it feel like there is more to write, but it's also simpler to understand. Especially because you communicate with interfaces rather than implementations.

I am appealing [...] for some assistance and guidance



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow