ASP.NET MVC Large Project Architecture [closed]
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
Although you mentioned TDD, I don't recall you mentioning onion architecture. Make sure you read through The Onion Architecture by Jeffrey Palermo.
Watch Put your controllers on a diet by Jimmy Bogard.
- Look at some existing example solutions such as EFMVC and CodeCampServer.
You raise some interesting questions and points about the myriad of ways you can architect an ASP.NET MVC application, let alone any application for that matter. I can provide you with my thoughts on the topic and you can use it however you wish.
You said you are worried that in creating both a domain model and view model you will be making the same thing twice, because they will look almost identical. Let me give you a few scenarios that might make you change your mind:
- Paging of data in a view - you could have your controller request a
List<Category>
, but you would not want your domain model to have to keep track of metadata for paging, such as total records, page size and page number, right? ACategoryListViewModel
would address this issue. - Mass assignment vulnerability - this is tied to how ASP.NET MVC tries to help your controller code populate a model's data via model binding. Say for instance, you had an account page where there are properties like first name, last name, phone number, etc. on the UI. Your domain model representation of an account has
IsAdmin
Boolean value; if you used the domain model to populate the view, then if a bad user figured out that aIsAdmin
property existed and passedIsAdmin=true
in the query string, then the model binder would pick that up and make the account an administrator even though the view never displayed a field to allow for that value to be changed. If you bound to a view model, which did not contain thatIsAdmin
property, but rather only the pieces of data relevant to the view, then this vulnerability would not exist. - Displaying view information in a specific format - say you have a view that displays a phone number and your domain model stores phone number as a number type (i.e
int
), then directly binding your domain model to your view would require the view to parse and format thatint
value. Instead a view model could hold the phone number as a string, already properly formatted and just display the value. - Using your domain model as more than just data transfer objects (DTOs) - if you want to attach behavior to your domain model instead of just holding data, then you will want to have a view models that do not contain that logic for displaying the data.
- Having different abstractions of the same domain model information - this ties into the paging scenario referenced earlier. You might want to have a
PagedAccountViewModel
,AddNewAccountViewModel
,UpdateAccountViewModel
, etc.
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 interfacesDataAccess | 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:
Modify the basic
OperationMessage
in entity layerThis 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 toC# Winform
or maybeWindows Mobile
/Azure
, it tainted the architecture.Add a new class called
WebOperationMessage
This is what I call
ViewModel
. Now it became duplicated because it is similiar by 90% withOperationMessage
class. But your N-Tier architecture are kept in order. After getting theOperationMessage
object from your business layer, you need to do some conversion. That conversion, is what I calledPresentation Logic
.Inherit the
OperationMessage
classThis 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