Currently at Giant Digital our approach to building models for MVT (Model, View, Template) applications could be nestled under the methodology of Fat Models / Skinny Views (mantra: models should be fat, and views should be skinny). Using this methodology has resulted in well structured projects which are maintainable, extensible, and highly testable.
That being said there have been projects whereby the boundaries of the application of (and maybe understanding of) the Fat models/Skinny views methodology has become noticeable and resulted in some unexpected adverse side-effects in large projects.
One of the main side-effects of the methodology is that it inadvertently encourages a developer (especially one who is new to an existing project) to place logic into a model where there is some type of interaction (or inference of an interaction) with the model and/or its attributes, especially logic which originated from a view. The consequence of which results in a model eventually becoming a god model due to their monolithic nature.
- In addition to the above side-effect their are several other behaviours/patterns which have become noticeable:
- Models become brittle, and therefore expert knowledge is required in order to make even the most minor of changes, and consequently refactoring maybe avoided for fear of breaking something.
- Separation of concerns becomes mirky, as model methods contain logic which is best suited to being in an app/project level file like `logic.py` or `utils.py`. For example sending emails. There should be one function in logic.py or utils.py which handles all of the email processing.
- Duplicate code fragments are introduced via new model methods which are based on subtle conditional logic. For example multiple model methods which are used to send emails which are based on a subtle variation.
- Testing requires large scale setups and require extensive mocking.
In an attempt to avoid the above the following considerations may help:
- In this context, remembering that the main objective of a model is to store data about an object and to use model methods to manipulate the stored data may contribute towards decision making.
- Utilize files such as `logic.py` or `utils.py` in an attempt to adhere to the DRY principle.
- Proactively maintain a separation of concerns. For example, logic pertaining to user authentication should remain in a view and not leaked to a model method or elsewhere.
- Reduce the complexity of functions and model methods, and make them small. According to Bob Martin (Clean Code) "... a function should do one thing and only one thing." Adopting these smaller considerations will ensure that the code is loosely coupled and highly testable.
- Model mixins should be used for reusable code to keep things DRY. However abstractions should be made with meaningful intent and purpose and should not be representative of diminished returns.
- Actively refactor code as you go along, as part of general maintenance, don't leave it for someone else.
Final thoughts, a model should read like a chapter of a good novel not a tabloid (in my opinion).