You've got a lot of moving parts in your question, touching on a lot of concepts, but here's my basic advice when it comes to how to think about a mid-to-large scale MVC application:
Presentation <---> Business Logic <---> Data Access
Firstly, it's best to not think of the the app as "an MVC application". It's an application that uses the MVC pattern as its presentation component. Thinking about it this way will help you separate out your business logic concerns from your presentation concerns. Perhaps it's ok for small applications to pile everything down to database access into the MVC structure, but it'll quickly become untenable for a mid-to-large application.
MVC (Presentation)
In your app, the ASP.NET MVC component should deal with transforming business data for display purposes (Models), displaying the user interface (Views), and communication issues such as routing, authentication, authorization, request validation, response handling, and the like (Controllers). If you have code that does something else, then it doesn't belong in the MVC component.
Repository/ORM (Data Access)
Also in your app, the data access layer should be concerned with retrieving and storing persistent data. Commonly that's in the form of a relational database, but there are many other ways data can be persisted. If you have code that isn't reading or storing persistent data, then it doesn't belong in the data layer. I shared my thoughts on the ORM/Repository discussion previously on SO, but to recap, I don't consider an ORM to be the same thing as a Repository, for several reasons.
Business Logic
So now you have your presentation layer (MVC), and your data layer (repository or ORM) ... Everything else is your business logic layer (BLL). All of your code that decides which data to retrieve, or performs complicated calculations, or makes business decisions, should be here. I usually organize my business logic in the form of 'services', which my presentation layer can call upon to do the work requested. All of my domain models exist here.
Your Approach
This is where your approach breaks down a little for me. You describe your MVC controller as the place you would get data from the repository, and call upon the MPGCalculator to do some work, etc. I would not have my controller do any of this, but instead would delegate all of this off to a service in the BLL.
In other words, I would not inject a repository and MPGCalculator into the controller, that's giving the controller too much responsibility (it's already handling all of the controller stuff I mentioned above). Instead, I would have a service in the BLL handle all of that, and pass the results back to the controller. The controller can then transform the results to the correct model, and pass that on to the correct view. The controller doesn't have any business logic in it, and the only things injected into the controller would be the appropriate BLL services.
Doing it this way means your business logic (for example, given a set of vehicles, calculate the MPG and sort best to worst) is independent from presentation and persistence concerns. It'll usually be in a library that doesn't know or care about the data persistence strategy nor the presentation strategy.
How large is your application? There's a good chance you're overthinking this. Unless the application is a large, enterprise-grade application, the high degree of loose coupling you are advocating is probably unnecessary.
If you decide that this level of loose coupling is still necessary, create a service layer that returns "service" objects. This fulfills a similar function to View Models in MVC. You will then write code in your service layer that maps objects from your domain model to your service model.
Note that part of your struggle may be due to the fact that, while your Data Repository is returning CRUD objects, your Service Layer should be returning the result of actions. For example:
public InvoiceView GetInvoice(int invoiceID);
returns a InvoiceView
object containing whatever data you wish to expose to the public, including Name, Address, Line Items and so forth, from several different tables/objects in your domain.
public class InvoiceView
{
public int InvoiceID;
public Address ShippingAddress;
public Address BillingAddress;
public List<LineItem> LineItems;
...
}
Similarly, there will be Service Layer methods that simply performs an action and returns an object indicating the result of the action:
public TransactionResult Transfer(
int sourceAccountID, int targetAccountID, Money amount, ValidationToken token);
IMPORTANT: You'll never be able to completely decouple from your data. Your consumer will always have to have some knowledge of the data, even if it's just an object ID or UUID.
Best Answer
I hate the idea of parallel object hierarchies just for the sake of layer purity.
This is a Core J2EE Pattern that in my view has become an anti-pattern. People used to create DTOs because the EJB 1.0 entity model was too chatty. I think POJOs/POCOs are smarter and don't have to resort to such contortions.
The data access layer might represent tables or objects, depending on which way you go.
The service layer maps to use cases and units of work. It owns transactions and orchestrates processes to fulfill the wishes of clients. It should be UI agnostic.
I'd recommend creating model objects and using them appropriately. Just make sure that you find a way to avoid cyclic dependencies between packages/namespaces. Don't couple all of them together. Data access layer should know about model objects but nothing else. Service layer should know about data access layer, model objects, other services and nothing else. View layer should have controllers that invoke services and nothing else.