MVC is a famous architectural pattern for building software applications. It was first used for building desktop GUIs and now it’s one of the most popular ways for building web applications and mobile applications.
MVC decouples a software application into three components:
Now that we have had a brief refresher about the MVC framework, in this article we will be zooming in on a component of the MVC that is underused or rather not being used to its full potential in most use cases.
Although the MVC architecture is known to a lot of web developers, using each component to their full potential and architectural intent is still not quite understood and one of the most underestimated or underused of these components in MVC is the model.
Most definitions say the model represents the data in your application. However, the model which represents the underlying data structure of your application, by design, should also contain:
This part is the easiest part to grasp as most web frameworks emphasize defining properties here. For example, let’s take a web application using Sails, an MVC Node.js framework, we can have a user model defined like this:
// api/models/User.js module.exports = { attributes: { id: {}, name: {}, EmailAddress: {}, } } }
You can see that our model is represented in Sails as a JavaScript module exporting an object. In this object, there is an attributes
key in which we define the specific data this model represents.
The model in MVC should also be able to implement business logic on the data it represents. For example, common business logic for web applications includes validation rules.
Validation rules enforce that data coming into the application meets the constraint of the design of the system required. A misconception is to do this validation on the controller level. However, this is the job of the model.
Taking our Sails model above, let’s see how the model should address business logic like validations:
// api/models/User.js module.exports = { attributes: { id: { type: 'string', required: true isUuid: true }, name: { type: 'string', allowNull: true }, EmailAddress: { type: 'string', isEmail: true, unique: true }, } } }
You can see that our model following the principles of MVC provides validation properties for the data that it will be representing. Let’s look at all of the logic implemented:
type
– Enforces that the data type (in this case, it should be a string)required
– Enforces that when creating new instances of this model, the data with this value set to true should always be providedallowNull
– When this is set to true for a particular data, the model will know to let instances be created with this particular data value set to nullunique
– Setting this to true informs the model to make sure that only one instance should be allowed to have a particular value for that data. That means the value for that data must be uniqueisEmail
– Most times we do checks like this in the controller, but in using the model to the full capacity, we set this in the model to make sure a particular data is an emailisUuid
– If this is set to true for model data, the model ensures the data is of a UUID form (v1, v2, or v3)Models should contain logic that deals with manipulating data. For example, say we have a post model, the model should contain methods that get the comments of a particular post.
In Sails models, each model has static methods like find
, findOne
, update
, etc. This helps in data access which you can call from the controllers.
In a well-implemented MVC architecture, the models normally host the bulk of the business logic because this logic is closely coupled to data manipulation and models are responsible for that data level.
Utilizing models to their full capacity encourages lean controllers, controllers that are more focused on connecting the models to the views and vice-versa than actually implementing business logic.
Although we used Sails as an example of an MVC framework to see how to use the model in MVC to its full potentials, these principles are framework agnostic and should apply to your chosen MVC framework of choice. Here are the key takeaways:
The AHA stack — Astro, htmx, and Alpine — is a solid web development stack for smaller apps that emphasize frontend speed and SEO.
Explore what Hattip is, how it works, its benefits and key features, and the differences between Hattip and Express.js.
React Shepherd stands out as a site tour library due to its elegant UI and out-of-the-box, easy-to-use React Context implementation.
Cookies are crucial to web development. This article will explore how to handle cookies in your Next.js applications.