I'm new to MVC but from what I have learned so far (for example here, by ScottGu) one should aspire to "skinny controllers" rather than "fat" ones.
Add to that the fact that views are inherently thin, and you'll get a lot of code in your model.
So my question is - How do you partition the code in your model to different logical parts in order to reduce complexity?
Do you use Data Access Layer and Business Logic Layer within the model itself (which I guess would still hold a lot of code), or are there better ways of doing that?
Thank you.
The layers we use are:
- View (with strongly typed View Models)
- Controller
- View Model Service
- Business Services
- Repositories
- (EF) Contexts
Views - as thin as can be - no logic - just display
View Models - Strongly typed per view - don't contain Entities, but just the fields we want in any one view.
Controller - just routing and calls to VMS. Handles exceptions that bubble up from the lower levels by routing to error pages.
View Model Services - creates and unpacks view models into the EF entities. No data access logic. One VMS per controller. Makes heavy use of AutoMapper to transfer the view model's data into entities.
Business Services - main point of data access. One BS per controller. Uses as many repositories as required to do its job. Transaction scope controller here. The VMS makes a single call to the BS - which wraps all the necessary DB calls in a single transaction if required. We anticipate the BS making calls out to external services in future.
Repositories - One per (top level) entity - does all CRUD operations for a group of entities. Our entities are large, complex object graphs - so we handle the top-most parent per repository.
Contexts - thin wrappers around the EF generated contexts so they can me mocked.
In terms of MVC - The Model part is made up of everything below the controller.
Here is how I typically divide things up and recommend dividing things up:
Model: represents the information. Should never contain rendering-related code. Should not contain code for publishing/subscribing updates. Should not contain reading/writing code.
Model I/O: code that reads / writes the model to / from disk, network, SQL, or some other backing store should typically be separate from the model object, itself, to allow for alternative storage.
Controller infrastructure: provides a wrapper on top of the model that adds publish / subscribe behavior to the model (i.e. signals change notification events when the model changes).
Controller: constructs the model and view, loads the model from storage, registers handlers on the model to update the view when changed. Registers handlers on the view to update the model on view actions and to save / store the model when save actions are invoked.
View: one or more components that render the model.
One way I found works really well is to separate the model, as you say, into a Data Service Layer and a Business Logic Layer. The foundation of the Business Logic layer are those objects with nearly direct mapping to database tables. The Data Service Layer is then able to map these objects directly into the database when changes are made or when new ones are created.
When I have to handle objects that refuse to neatly map to a single table in the database I'll create a facade or composite object through which the business logic can interact. This ensures that even though they are treated as one by the Business Logic the Data Service Layer still sees them as distinct and is able to update only those that need it.
I recommend this article on Mapping Objects to the Relational Model to get you started.
You might also want to look at Service Layer that can assist in trying to make controller as thin as possible. The controller can just simply delegate some operations to the service layer for execution. Normally, this layer has a dependency on the Data Access Layer(DAO), for persistence of your domain objects. In this case, your controller is simple there to coordinate your flow of control. The main idea with MVC is the separation of concerns. In the traditional MVC, the presentation logic in shared between your controller and view. In the case on MVP(another variation of MVC), all the presentation logic in handled by the controller.