Sensible MVC project guidelines

Often when developers start working on brand new projects they are overwhelmed with the decisions laid out in front of them in terms of where things go, what they are called, what things should do, etc. This almost always leads to people making the wrong decisions and crapping all over what could be a beautifully structured simple and straight forward project layout.

This blog removes the need for those developers to make decisions when firing up a brand new project by laying out where things should go.

It's worth noting that I'm specifically referring to MVC 4/5 projects which make up the bulk of the projects that Wiliam works on these days. I'll be using <MVC APP> as a representative for the root of the MVC application.

The core principle behind most of these guidelines is simple: Don't make other code monkeys wonder where something is.

0) General

  • Don't install 1000 NuGet packages for no reason; Less assembly references means a quicker cold-start time which is important for rapid edit-compile-test cycles during development.
  • Don't use IOC containers; most of the time they are useless and only create a layer of ambiguity in the application.
  • Don't use 'Automappers'; control/flexibility/performance are critical and most automappers do way more than what 99% of real-world use cases require.
  • Do install MiniProfiler or Glimpse, for performance monitoring during development.
  • Don't use preprocessor directives. Even on the off chance that you do actually know what they are (you probably don't), still don't use them, code cow.
  • Use established naming conventions of properties, fields methods, public/private, etc.
  • Never, ever, declare classes/structs/enums inside other classes. If you do, you will incur the wrath of Code Monkey Jesus.
  • Keep your page loads quick. If they're not under 100ms then get back there and fix it.
  • Don't use #regions. "But my code is too hard to maintain without regions!" you cry out.. You're doing it wrong. Go and fix it.

1) Controllers

  • Put controllers in <MVC APP>\Controllers.
  • Name them <Whatever>Controller.
  • Have only this single class in the file
  • Inherit from a custom base controller ONLY if you intend to reuse functionality across controllers.
  • Keep controllers responsibilities compartmentalised. No god controllers!
  • Don't use #regions

2) Models

  • Put models in <MVC APP>\Models\<RelatedControllerName> eg <MVC APP>\Models\Account or <MVC APP>\Models\News.
  • Name them <Whatever>Model. Why? Because chances are you will have other classes in your project which use the specific <Whatever> that your model represents and this helps reduce falling back to namespaces to avoid name conflicts. Eg. You may have a 'User' data entity class, so your model would be UserModel or even better, action-specific, such as UserEditModel or UserCreateModel.
  • They should be specific to the action they are being used in, where it makes sense. Don't use 'UserModel' for registering, logging in, editing and deleting users. Chances are you will have different requirements for those actions and you don't want to form unnatural dependencies.
  • Use annotations to define data requirements such as [Required], [EmailAddress] etc.
  • Never include any business logic or data retrieval in any models. They are there for the purpose of transporting data from the controller to the view!
  • If your pages require a core set of model data, such as the logged in user name or the page title, then create a BaseModel and put those properties on it, then inherit from that model on your other models. Populate it using a generic BaseController method to prevent copy-paste code everywhere.
  • Don't use #regions

3) Actions

  • Keep the name simple and concise. Eg 'Edit', 'List', 'CreateUser'.
  • Keep the same name of the action between the GET and POST versions. /register should submit to /register and not /someothercrazyactionname.
  • Never ever hit your data layer directly. Use properly abstracted classes instead. Architect your project properly, code monkey.
  • Don't use #regions

4) Views

  • Put them in <MVC APP>\Views\<RelatedControllerName>\<ActionName>.cshtml eg <MVC APP>\Views\Account\Login.cshtml
  • Don't perform business logic in views. If you need to hide/show UI elements based on some data, then set flags on the model. For example, don't do "if (HttpContext.Current.User.IsLoggedIn) {... }" but instead use a base model with a 'IsUserLoggedIn' property which is set ONCE by the controller.
  • Don't overcomplicate views with multi-level nested partial rendering. Keep it simple.
  • .. Don't use #regions

5) "Repositories" - the things that fetch data for you

  • Don't require instantiation; use static methods. It's a useless and repetitive step in controller actions. Oh, static methods are slow? No they're not. Do some research for once, mark-up monkey.
  • Don't put fuzzy logic in your save routines. If someone calls UserRepository.Save(myNewUser) and the repository checks and does different things depending on some arbitrary conditions it will make the method unpredictable which is literally the worst thing the method could be. Even if the method was empty, at least it's predicable.
  • Put them all (including the EF data model) in a separate class library so it's easy for services, console apps and other web apps to use the data layer.
  • Oh yeah, don't use #regions

To sum it all up

Keep it simple and do things the way they were meant to be done. This way when some other guy has to work on your barely average project in the future, he can do so easily and quickly.