\u003C/figure>\n\u003C!-- /wp:image -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Models are responsible for holding the state of the application. In other words this means models store and do low-level work with the data, it can be a class or an object with methods and members holding data or it can be a wrapper of a third-party API. Models frequently have an asynchronous API because they work with low-level asynchronous operations like working with the database, reading from a file, or communicating over a network. Models can work with other models, call them, and use their methods. One model may create composite and more complex operations based on the functionality of other low-level models. As an example, a model may pull some data using another model that wraps a third-party service and store that data into a database table using a model for that table. Models should not work on their own. They don’t call controllers or views or hold any references to them.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Views render the data in a web browser or mobile application. Web application views are stateless and they can be as simple as JSON serializers or as complex as HTML templating engines. Views take data in the form of model instances or custom objects and translate the data into the requested form. Sometimes web applications can support multiple forms of data representation driven by the \u003Ccode>HTTP Accept\u003C/code> header. In MVC, this could just mean employing a different serializer for JSON or XML or a templating engine for the data. Views may call other views to render different types of objects or sub-objects. Views may receive model instances and work with model APIs. However, patterns where a view would directly manipulate data in the model is not recommended. It is always and only the responsibility of the controller to call functions on models that could manipulate the state of the application. Avoid the possibility of this pattern by creating a proxy object that holds the data ready to be rendered in the controller, initializing these objects with data from the models and passing this proxy object down to a view. This way, the controller stays in control of the view’s access patterns and data exposed to the view.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2 class=\"wp-block-heading\" id=\"h-interfaces\">Interfaces\u003C/h2>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Each component of the MVC design pattern has an interface. This interface provides a way to interact with the objects. In an MVC application on the web, this interaction starts with the controllers. A web server typically interacts with a controller after it receives an HTTP request. The requests are first processed by lower levels of framework or web server logic. This processing handles parsing and deserializing the request and calling a method on the controller.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The web MVC controllers typically implement methods that represent operations provided by the HTTP protocol: \u003Ccode>GET\u003C/code>, \u003Ccode>POST\u003C/code>, \u003Ccode>PUT\u003C/code>, and \u003Ccode>DELETE\u003C/code>. But these HTTP methods don’t need to map to a single method on the controller. The mapping is controlled by routing logic that provides additional options to invoke the right controller method based on the paths or parameters provided in the request URL. In an app using a database, some controllers might implement methods like \u003Ccode>list\u003C/code> or \u003Ccode>index\u003C/code> to wrap model calls and display lists of records with their ids and then a \u003Ccode>get\u003C/code> method to get a specific record based on its id.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>For retrieving records, the call comes from controllers to models. In web application frameworks like Ruby on Rails or Django, you may find models providing or implementing several \u003Ccode>find\u003C/code> methods. These methods accept a record id and or other parameters that are passed to queries to lookup specific records. To create records, the models implement \u003Ccode>create\u003C/code> factory methods or allow instancing the model. The models are instanced via class constructor and their properties are set through the constructor parameters or through setter methods defined on the model. The models sometimes wrap specialized record creation into class or static factory methods. These factory methods can provide a nice interface allowing you to quickly understand how the model is instanced in the application code or in tests.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2 class=\"wp-block-heading\" id=\"h-implementation-pitfalls\">Implementation pitfalls\u003C/h2>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The most common pitfall when implementing new MVC apps is the lack of respect towards the separation of concerns presented and enforced by the pattern. Developers sometimes decide to cut corners and skip the separation of concerns in order to be quickly done with their changes.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading {\"level\":3} -->\n\u003Ch3 class=\"wp-block-heading\" id=\"h-too-smart-views\">Too smart views\u003C/h3>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The views in MVC web applications are responsible for translating internal data structures into presentable text format like XML or HTML. Some logic is necessary to do so. You can iterate over arrays of records using loops. You can switch between different types or sections of content for display using branching. Views by nature contain a large amount of markup code. When the markup code is mixed with looping and branching logic, it is much harder to make changes to it and visualize the results. Too much branching or conditions in the view also make the markup hard to read. Spaghetti only works as food, not code. There are two tools that help with this problem: helper functions and view file splitting.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Helper functions are a great way to isolate logic that may be called multiple times, shared, or re-used between views and let you follow \u003Ca href=\"https://en.wikipedia.org/wiki/Don%27t_repeat_yourself\">DRY principles\u003C/a>. Helpers can be as simple as providing an element class name based on some input or as complex as generating larger pieces of the markup.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>File splitting is another powerful approach that can improve code readability and maintainability. Every time a view grows too large or too complex, you can break it down to smaller pieces where each piece does only a single thing. Just the right amount of view file splitting can make a big difference in organization of the project.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Overapplying helper functions or view file splitting can also lead to problems, so it is best to drive changes when there’s a reason—and that reason should be a too complex view.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading {\"level\":3} -->\n\u003Ch3 class=\"wp-block-heading\" id=\"h-heavy-controllers\">Heavy controllers\u003C/h3>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Controllers are responsible for implementing actions coming from the frontend, the UI, or the API of the application. For many developers, it becomes tempting to implement data processing logic in the controllers. Sometimes you may see database queries built directly in the controllers and the results stitched together with other data in some complex logic and then the results returned to the views or serializers. Other instances of this problem look like third-party API calls made directly from controllers and the results adjusted or mixed with other data from the local models and returned back to the client. Both of these are cases of heavy controllers.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The controllers should be light on logic. They should process input parameters and pass the adjusted input down to the models. The controllers take the inputs, choose and call the right models and their methods, retrieve the results, and return the results in the right format back to the client. Heavy lifting with data shouldn’t be done directly in the controller methods to avoid code complexity problems and issues with testing.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading {\"level\":3} -->\n\u003Ch3 class=\"wp-block-heading\" id=\"h-light-models\">Light models\u003C/h3>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Models are built to manage, hold and represent the application data. Every time the application needs to work with the data, it should reach into some model. Sometimes application developers decide to perform data manipulation in controllers, which makes the controller code more complex and creates code maintenance problems. Since models work with data, any incorrect logic on the models may corrupt the data. This is why the models should have the highest test coverage out of the entire app. Well-implemented models are very easy to unit test. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Application logic that reads and writes data should be concentrated on the related models. For example, if the data queries are spread all over the controller code then it is harder to get good model test coverage. When the developers debug issues like slowness in the database, they may not see all queries performed against a table right away so it may take them longer to optimize the queries and implement better indexing.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Another example can be an interaction with third-party services. Applications frequently require communication with cloud and data providers and this is done through asynchronous calls and APIs. These methods return data that gets processed, altered and saved in the application. It is beneficial to treat these interactions with third-party APIs as model logic and place it on the respective model. This puts the third-party logic to a specific place in the app where it can be quickly covered with integration tests.\u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusion\u003C/h2>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The MVC pattern was developed to separate concerns and to keep the application code highly organized. Frameworks like Ruby on Rails that are built on top of this pattern and reap the benefits of strict naming and placement. Closely following this pattern makes projects based on it easier to understand, maintain, and test. Correct usage of its rules reduces developers' mental load and speeds up any bug fixing or new feature development. Long live the model-view-controller!\u003C/p>\n\u003C!-- /wp:paragraph -->","html","2023-05-17T14:00:00.000Z",{"current":423},"keep-em-separated-get-better-maintainability-in-web-projects-using-the-model-view-controller-pattern",[425,433,437,441,446],{"_createdAt":426,"_id":427,"_rev":428,"_type":429,"_updatedAt":426,"slug":430,"title":432},"2023-05-23T16:43:21Z","wp-tagcat-code-for-a-living","9HpbCsT2tq0xwozQfkc4ih","blogTag",{"current":431},"code-for-a-living","Code for a Living",{"_createdAt":426,"_id":434,"_rev":428,"_type":429,"_updatedAt":426,"slug":435,"title":436},"wp-tagcat-model-view-controller",{"current":436},"model-view-controller",{"_createdAt":426,"_id":438,"_rev":428,"_type":429,"_updatedAt":426,"slug":439,"title":440},"wp-tagcat-mvc",{"current":440},"mvc",{"_createdAt":426,"_id":442,"_rev":428,"_type":429,"_updatedAt":426,"slug":443,"title":445},"wp-tagcat-software-architecture",{"current":444},"software-architecture","software architecture",{"_createdAt":426,"_id":447,"_rev":428,"_type":429,"_updatedAt":426,"slug":448,"title":450},"wp-tagcat-software-design",{"current":449},"software-design","software design","Keep ‘em separated: Get better maintainability in web projects using the model-view-controller pattern",[453,459,465,471],{"_id":454,"publishedAt":455,"slug":456,"sponsored":12,"title":458},"370eca08-3da8-4a13-b71e-5ab04e7d1f8b","2025-08-28T16:00:00.000Z",{"_type":10,"current":457},"moving-the-public-stack-overflow-sites-to-the-cloud-part-1","Moving the public Stack Overflow sites to the cloud: Part 1",{"_id":460,"publishedAt":461,"slug":462,"sponsored":412,"title":464},"e10457b6-a9f6-4aa9-90f2-d9e04eb77b7c","2025-08-27T04:40:00.000Z",{"_type":10,"current":463},"from-punch-cards-to-prompts-a-history-of-how-software-got-better","From punch cards to prompts: a history of how software got better",{"_id":466,"publishedAt":467,"slug":468,"sponsored":12,"title":470},"65472515-0b62-40d1-8b79-a62bdd2f508a","2025-08-25T16:00:00.000Z",{"_type":10,"current":469},"making-continuous-learning-work-at-work","Making continuous learning work at work",{"_id":472,"publishedAt":473,"slug":474,"sponsored":12,"title":476},"1b0bdf8c-5558-4631-80ca-40cb8e54b571","2025-08-21T14:00:25.054Z",{"_type":10,"current":475},"research-roadmap-update-august-2025","Research roadmap update, August 2025",{"count":478,"lastTimestamp":479},8,"2023-06-20T15:33:26Z",["Reactive",481],{"$sarticleModal":482},false,["Set"],["ShallowReactive",485],{"sanity-CYps9hEeWPYJ3idFkQzO8jxhoNK--3N18tppoQzWOCY":-1,"sanity-comment-wp-post-22178-1756804251747":-1},"/2023/05/17/keep-em-separated-get-better-maintainability-in-web-projects-using-the-model-view-controller-pattern"]