Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why it's not a good idea to pass entities as Models in MVC?

We're developing a pretty large application with MVC 2 RC2 and we've received some feedback on the way we're using the Entity Framework's Lazy Loading.

We're just getting the entities in the controller and sending them as models to the Views, that is causing that the view code asks the Database for the navigation properties we are using in it. We have read about this and it appears is not a good design, but we were wondering why?

Can you help us understand this design problem?

Thanks!

like image 958
sabanito Avatar asked Feb 24 '10 23:02

sabanito


People also ask

Is it OK to use entities for view models in ASP NET MVC?

You can't. You need to represent different contexts as different view models because validation on those models changes depending on the screen representation. Previously in MVC 1 you could get around this by simple not posting fields you didn't want validated.

Why do we need models in MVC?

The central component of MVC, the model, captures the application's behavior in terms of its problem domain, independent of the user interface. The model directly manages the application's data, logic and rules. So the Model is the biggest, most important layer in most MVC applications.

Do you have to use Entity Framework with MVC?

No it is not a must. You can implement your database layer.

What is difference between model and model in MVC?

@model is used to "import" a model in the view page while the @Model represents the imported model and is where you retrieve its properties. Here you see that @model simply imports the Model object to the page while the @Model you get actual value from the retrieved model.


2 Answers

The main issue here is coupling. The idea behind a model, which is the "M" in "MVC", is that it has no external dependencies. It is the "core" of your application. The dependency tree of a well-designed app architecture should look something like this:

                       +---------------------------------------> Views
                       |                                           |
                       |                                           |
                       |                                           v
                  Controllers ----+-> Model Transformer -----> View Model
                       |           \         |
                       |            \        |
                       v             \       v
Data Access <---- Persistence --------> Domain Model
                       |             /
                       |            /
                       v           /
                     Mapper ------+

Now I realize it's not exactly convincing to just say "here's an architecture, this is what you should use", so let me explain what's happening here:

  1. Controller receives a request.
  2. Controller calls out to some kind of persistence layer (i.e. repository).
  3. Persistence layer retrieves data, then uses a mapper to map to a domain model.
  4. Controller uses a transformer to change the domain model into a view model.
  5. Controller selects the necessary View and applies the View Model to it.

So, why is this good?

  • The domain model has no dependencies. This is a very good thing, it means that it's easy to perform validation, write tests, etc. It means that you can change anything else anywhere in your architecture and it will never break the model. It means that you can reuse the model across projects.

  • The persistence layer returns instances of the domain model. The means that it can be modeled as a totally abstract, platform-agnostic interface. A component that needs to use the persistence layer (such as the controller) does not take on any additional dependencies. This is ideal for Dependency Injection of the persistence layer and, again, testability. The combination of persistence, data access, and mapper can live in its own assembly. In larger projects you might even be able to further decouple the mapper and have it operate on a generic record set.

  • The Controller only has two downstream dependencies - the domain model and the persistence layer. The model should rarely change, as that is your business model, and since the persistence layer is abstract, the controller should almost never need to be changed (except to add new actions).

  • The Views depend on a separate UI model. This insulates them from changes in the domain model. It means that if your business logic changes, you do not need to change every single view in your project. It allows the views to be "dumb", as views should be - they are not much more than placeholders for view data. It also means that it should be simple to recreate the view using a different type of UI, i.e. a smart client app, or switch to a different view engine (Spark, NHaml, etc.)


Now, when using O/R Mappers such as Linq to SQL or Entity Framework, it is very tempting to treat the classes they generate as your domain model. It certainly looks like a domain model, but it is not. Why?

  • The entity classes are tied to your relational model, which over time can and will diverge significantly from your domain model;

  • The entity classes are dumb. It is difficult to support any complex validation scenarios or integrate any business rules. This is referred to as an anemic domain model.

  • The entity classes have hidden dependencies. Although they may appear to be ordinary POCOs, they may in fact have hidden references to the database (i.e. lazy loading of associations). This can end up causing database-related issues to bubble up to the view logic, where you are least able to properly analyze what's going on and debug.

  • But most importantly of all, the "domain model" is no longer independent. It cannot live outside whatever assembly has the data access logic. Well, it sort of can, there are ways to go about this if you really work at it, but it's not the way most people do it, and even if you pull this off, you'll find that the actual design of the domain model is constrained to your relational model and specifically to how EF behaves. The bottom line is that if you decide to change your persistence model, you will break the domain model, and your domain model is the basis for just about everything else in your app.

Entity Framework classes are not a domain model. They are part of a data-relational model and happen to have the same or similar names that you might give to classes in a domain model. But they are worlds apart in terms of dependency management. Using classes generated from an ORM tool as your domain model can only result in an extremely brittle architecture/design; every change you make to almost any part of the application will have a slew of predictable and unpredictable cascade effects.

There are a lot of people who seem to think that you don't need a cohesive, independent domain model. Usually the excuse is that (a) it's a small project, and/or (b) their domain model doesn't really have any behaviour. But small projects become large, and business rules become (far) more complicated, and an anemic or nonexistent domain model isn't something you can simply refactor away.

That is in fact the most insidious trait of the entities-as-model design; it seems to work fine, for a while. You won't find out how much of a mistake this is until a year or two down the road when you're drowning in defect reports and change requests while desperately trying to put together a real domain model piecemeal.

like image 183
Aaronaught Avatar answered Oct 12 '22 19:10

Aaronaught


One potential issue with this design is that the view might iterate through the model objects (that are lazy loaded) more than once and cause an unnecessary overhead. For instance, if a Web page displays the same data in two different forms in a couple of different locations in the page, it'll loop through the query twice and causes two round-trips to the database (Look at the tags under the question and in the sidebar on this page and assume they came from a single query). The view could deal with this problem by caching the results once and loop twice on the cached data, but this is not what a view should deal with. It should present the data given to it without worrying about these stuff.

like image 40
mmx Avatar answered Oct 12 '22 17:10

mmx