The Power of MVC Pattern Explained Simply

Get to know MVC. The simplest design pattern that helps managing demanding projects for years. Learn what the Model View Controller pattern is, how it can benefit you, and when it's best to use. In a simple way.

tl;dr

After working on high-stake projects with budgets of thousands of dollars I've learned that architectural decisions matter, even when many people claim they don't. So today I want to tell you my thoughts about the simplest architectural pattern that helped me handle those big projects: Model-View-Controller. Some see it as overengineering, often feeling there's no time for it in their projects. Others love it, while some don't understand what it's all about. In this episode, I discuss the basic ideas that play a role here.


Who might benefit from this?

Straight to the point - this episode is intended to the aspiring software engineers who want to improve the efficiency of the project and prepare yourself for more demanding projects and technologies. While that's the simplest architectural pattern, having basic experience in developing solutions will be highly beneficial. If you need some help I insist you to check out the episode about the Procedural vs Object-Oriented coding style, since it provides a great insight about the problems that this pattern can solve too.


What is MVC pattern?

It's a widely used architectural pattern that divides an application into three logically separated layers. Each layer has its specific role, with clear boundaries between each other, which helps maintain a correct data flow throughout the application. It's probably the simplest one and that's why it is worth knowing it well.

  • Model: Represents the application's data and business logic. Defines domain objects and their relations within the app, defines business rules, performs data-related operations like fetching or saving, and provides the results for other layers. That's the most important part since it defines what your application actually does.
  • View: Represents the application's interface. Takes care of presenting the data in a user-friendly form so it defines how the application looks (HTML + CSS), and what interactions the user can perform (HTML + JS). It communicates directly with the user, takes the input, and passes it to the controller to run specific actions.
  • Controller: Represents a bridge between a view and application business logic. It accepts requests from the view, verifies them, and talks with the model through its interface to change the application state, perform actions or get the necessary data, and send the received results back to the view.

We'll explore more examples later, it should already be clear that this separation makes the process more efficient. For example, frontend devs can focus on the view without getting engaged in the complexities of the model. Similarly, backend developers can focus on the backend details without needing to engage with the frontend aspects.


How to search for MVC patterns in real life?

Sounds hard? Let's think about this like a restaurant experience. The waiter sets up the table for a client who wants to eat and brings a menu for them to explore the options. The client informs the waiter of their choice, so the waiter can head to the kitchen and hand over the order to the chef, who starts cooking. After a while, the chef hands over the meal to the waiter who then brings it to the client's table.

Looks familiar, right? So let's try to assign a roles to a key elements of this story now.

  • Model: The kitchen managed by the chef. It accepts the orders from the waiter and takes care of preparing the meal.
  • View: The guestroom in the restaurant. A nice-looking environment with tables where clients can sit, order a meal, and enjoy eating.
  • Controller: The waiter who sets up the tables, takes and verifies the client's orders, passes them to the chef, and brings back the results to a client.

This is the most common workflow used in many restaurants. Is it ideal? I wouldn't say so, as clients sometimes have different needs. Is it efficient? Absolutely! To prove this,  let's imagine potential problems caused by an incorrect flow in a restaurant.

  • Breaking Data Flow: Can the clients go directly to the kitchen to make an order? They can, but the chef probably won't be happy about having to talk to guests.
  • Breaking Separation: Should the kitchen stop working fine when the vase breaks in the guestroom? Not really, since it's not their problem.
  • Performing Forbidden Actions: Can the clients try to order meals that are not on the menu? They can, but if it’s not controlled, it might lead to problems.

I could provide more examples, but by now, you should see how important correct data flow is in many aspects of life. Just as you, as a chef, would work more efficiently if you could focus on cooking rather than serving customers, the data layer works better when it doesn't have to deal with talking directly to the users.


How to search for MVC patterns in DEV?

The controller renders a view for a user who wants to interact with the app and provides an interface to explore the options. The user makes a request, so the controller can head the model layer and ask for the data, which makes magic inside (e.g. SQL queries). After a while, the model passes the response to the controller that updates the view.

I believe we don't need to describe layers since it should be clear that each has a specific purpose. So let's analyze the potential problems when MVC rules are not followed, for example when everything is integrated in one place (e.g. WordPress's functions.php).

  • Breaking Data Flow: Can the view make direct SQL queries? It can, but there's a risk of problems, when this infrastructure detail changes, for example, to REST API. The front-end developer will have to deal with this and change all the occurrences.
  • Breaking Separation: Should the notification mailing system stop working when the form component breaks? Not really, since this should impact only the view. It's better to break one thing rather than the entire system.
  • Performing Forbidden Actions: Can the view perform actions without verifying business rules, for example sending an email only as a registered user? It can but it will make the app not compliant with requirements. Controller should verify this.

Of course, these issues don't always occur. If you're a solo all-in-one developer working on a simple "20 hours" projects, you likely won't encounter these problems. However, in larger projects that require hundreds or even thousands of hours, the separation of roles becomes crucial. In such scenarios, having frontend developers working on backend mechanisms can be a big problem and slow down the development process.


How to use MVC pattern?

The concepts discussed here are stack-agnostic, meaning they can be applied no matter of the technology you use. To illustrate the basic idea, I'll create an abstract PHP feature that displays the titles of the 10 latest posts from the database.

#1 Defining Model

The first layer that we need to outline is the Model handling data logic and giving results. It defines a Post object describing business entity and store its data, and a repository for querying the database and retrieving results - Posts. By hiding infrastructure details like SQL queries there, the Model prevents complexities from spreading through entire app.

namespace FM\Domain;

class Post
{
    private string $title = '';

    public function __construct(string $title)
    {
      $this->title = $title;
    }

    public function getTitle(): string
    {
        return $this->title;
    }
}
namespace FM\Domain;

class Posts
{
    public function get(): array
    {
        $results = sql_load_results();

        return array_map(fn($result) => new Post($result['title']), $results);
    }
}

Why is this beneficial? If the project requires switching from an SQL database to a REST API, we only need to update the model. The rest of the application remains unaffected.

#2 Defining Controler

The layer that I often handle next is not the View as it might seem by the name. It's the Controller responsible for communicating with the model and rendering the results.

When a user opens the application and requests specific results, such as the 10 latest posts in the list, the controller handles this. It initializes the posts repository to fetch the data and prepares it for the view. It simply takes a several puzzles and connect them.

namespace FM\Controllers;

use FM\Domain\Post;
use FM\Domain\Posts;

class Homepage
{
    public function render(): void
    {
        $posts = new Posts();

        fm()->templates()->render('homepage.blade.php', [
            'title' => 'Homepage',
            'items' => $posts->get(),
        ]);
    }
}

One of the best aspects of this approach is that a developer working on the controller doesn’t need to know how the data is prepared. They don’t need to worry about whether SQL, REST, or another method was used. Their only concern is receiving an array of Post objects with the simple repository's function. That's a significant simplification.

And we can do even more! While the definition mentioned earlier states that controller prepares the data, does this mean it just passes the raw data received from the model to the view? It can, but it can also do more. The view only needs to display the titles, which are strings. So, is there a need to pass the entire Post object?

namespace FM\Controllers;

use FM\Domain\Post;
use FM\Domain\Posts;

class Homepage
{
    public function render(): void
    {
        $posts = new Posts();

        fm()->templates()->render('homepage.blade.php', [
            'title' => 'Homepage',
            'items' => array_map(fn(Post $post) => $post->getTitle(), $posts->get()),
        ]);
    }
}

While this might be useful in more complex scenarios, it's not for this simple element. We can use the array_map to map the results and return only the titles. We take the "more complex" results, and simplified them for a view. That's what is the controller for too.

#3 Defining View

The dirty work is done, so now we can move on to something more relaxing: preparing final part - the View. It's a simple Blade file that displays results received from controller. It contains no business logic; its sole responsibility is to render the data.

<html>
    <head>
        <title>{{ $title }}</title>
    </head>
    <body>
        <ul>
            @foreach ($items as $item)
                <li>{{ $item }}</li>
            @endforeach
        </ul>
    </body>
</html>

Imagine having to write raw SQL queries here as a frontend developer to display results - that would be far from relaxing. With this approach, you only request and receive what is needed, without worrying about infrastructure details and complexity. We can also create CSS and JS as part of the view, but for this scenario, Blade template is enough.


When to use MVC pattern?

I won't say that this is the only approach that you should use, that is best and solves all the problems. That's not the right direction. Like in the restaurant, even with the best intentions, and best process, something can go wrong and it often goes. However, I've been working with several big players on projects with budgets that would allow me to buy a few houses and do nothing for a few years and the one thing I learned is when more money is at stake, we can't risk "YOLO" work. Such decisions make an impact.

So why to use the MVC pattern? To reduce the risk of potential problems, not to prevent them entirely. Its main purpose is to create a structured environment that minimizes issues and maintains a clear, organized flow in the app.

Also, remember that sometimes simplicity is the best approach. If you run a food track that serves simpler meals and have a less demanding target group investing in a waiter doesn't make any sense, since you're the chief and waiter at once. The same situation is when you build simple WordPress websites or tiny features. You don't need to build up the whole MVC pattern to display one shortcode. It's overenginering.

On the other hand, if you plan to run a restaurant with tables, more demanding clients, and a diverse menu, investing in a good process is essential. Similarly, if you're building a long-term solution, medium-sized websites that handle more than just displaying a few articles, or plugins with more complex functionality, you should definitely try it out.

As with everything - the context matters. Knowing the rules you should be able to decide if that's the right approach for your project, and my role here is to make it simple.

Feedback

How satisfied you are after reading this article?