MVC – How to Determine What Should Get Its Own Controller

mvc

I'm using the MVC pattern in my web application built with PHP.

I'm always struggling to determine whether I need a new dedicated controller for a set of actions or if I should place them inside an already existing controller.

Are there any good rules of thumb to follow when creating controllers?

For example I can have:

AuthenticationController with actions:

  • index() to display login form.
  • submit() to handle form submission.
  • logout(), self-explanatory.

OR

LoginController with actions:

  • index() to display login form.
  • submit() to handle form submission.

LogoutController with action:

  • index() to handle logging out.

OR

AccountController with actions:

  • loginGet() to display login form.
  • loginPost() to handle login form submission.
  • logoutGet() to handle logging out.
  • registerGet() to display registration form.
  • registerPost() to handle form submission.

    And any other actions that are are involved with an account.

Best Answer

In order to find the right grouping for controllers, think of the testing.

(Even if you don't actually do any testing, thinking of how you would go about testing your controllers will give you some very good insights on how to structure them.)

An AuthenticationController is not testable by itself, because it only contains functionality for logging in and logging out, but your testing code will need to somehow create fake accounts for testing purposes before it can test a successful login. You could bypass the subsystem-under-test and go directly to your model for the creation of the testing accounts, but then you will have a fragile test in your hands: if the model changes, you will have to modify not only code which tests the model, but also also code which tests the controller, even though the interface and behavior of the controller has remained unchanged. That's unreasonable.

A LoginController is unsuitable for the same reasons: you cannot test it without creating accounts first, and there are even more things that you cannot test, like for example preventing duplicate logins but then allowing a user to login after having logged out. (Since this controller has no logout functionality.)

An AccountController will give you everything you need in order to do your testing: you can create a test account and then try to login, you can delete the account and then make sure you cannot login anymore, you can change the password and make sure that the right password has to be used in order to login, etc.

To conclude: in order to write even the smallest test suite, you will need to make all of the functionality of the AccountController available to it. Subdividing it down to smaller controllers appears to be yielding handicapped controllers with insufficient functionality for a proper test. This is a very good indication that the functionality of AccountController is the smallest subdivision that makes sense.

And generally speaking, the "think of the testing" approach will work not only in this particular scenario, but in any similar scenario you come across in the future.

Related Topic