Beyond MVC

By Joe Hicks, 2022-03-13

The Basics

The MVC programming paradigm means Model, View, Controller, and is an effective architechture for delegating code responsibilities.

Models are the data structures behind your application, things like Users or Products. They use Object Oriented Programming (OOP) abstraction principals to interface with the rest of your application and simplify work. Typical abstraction is to automate interaction with persistent storage, user permissions, or even the automation of admin tools.

Views are exactly that: a separation of the visual presentation from the programming logic. If you ever ask why something looks the way it does, you should be able to confidently refer to the view files responsible for that output.

Controllers are sometimes called Actions, and that's a good way to think of them. When you want to do something with a model, an application controller should be responsible for initiating this process. In web applications, controllers are usually mapped to respond to a given URL or URL pattern.

When You Need More

My study of Domain Driven Design (DDD) led me to believe that the MVC itself should be devoted to the domain problems (business logic), offloading work to the appropriate system as it grows large enough to benefit from additional components.

Here is a small collection of tips to help maximize code reuse and keep your app maintenance low:

Object Relational Mapping (ORM) - Your models shouldn't be bogged down interacting with your persistent storage, which is what usually happens. Instead, use an additional component (that I've taken to calling "Schemas") which implements an ORM. Now you can get back to thinking "Domain Models" that only deal with real business issues.
Case in point: When reading from a database you cannot control, this layer gives an opportunity for translation that may be irrelevant to your models.

Templates are not always implied by a view system, but reusable, customizable, templates are integral to modern web development. Many platforms are utilizing a "block" system where templates or themes provide blocks for admins or developers to customize. While this is very popular, it only seems like an efficient process when your application utilizes Server-Side Rendering (SSR), as most popular platforms do.

Services are the next step for controllers. My rule of thumb is that an action begins as a controller (typically an API endpoint), and evolves into a service as more and more similar actions need to be performed.
Case in point: You want to trigger an update process via a webhook or CLI. Have both controllers use a shared service that contains the update methods.

Independent Frontends - A full-featured frontend web application has the same architecture needs as a robust backend server. In many frameworks, like Vue.js, there are tools available to address additional pain points that arise when your application is so close to user interaction. These include things like a router, to help you designate components as "controllers", and a state management system (Vuex) to keep page reactivity coordinated between views.

Bringing It All Together

To express all of these concepts together, I have a personal framework codenamed "Bright" (a nod to my depth of experience with the LAMP stack).

Controllers automatically route to a URI logically similar to their file path, but have opportunities for internal rewrites from the database or core system rewrite functions. Application security is usually enforced here.

Services typically handle integrations with outside systems, sometimes as API helpers and sometimes existing only as consolidated business logic.

Models are domain models, and should contain important business logic that is specific to the given model. Typically this exists in the form of reading schemas in order to calculate model properties.

Schemas are very strictly limited to interfacing with persistent storage. They should contain methods to help with retrieval and updates of this data. Utilizing an ORM is recommended.

Values are nothing but representational structures, like models, but immutable once created. They assist in bringing complex structures to your models without adding complexity, and they help provide code completion in your IDE.

Views take variables and output them into text (usually HTML), with a couple standard features. Views can be nested inside each other, and views can extend any other view that declares blocks (making it a template with default block content).

System core files contain static helpers for critical application functionality (Request, Crypto, Filter, etc)

Vendor files are used for 3rd party scripts as needed by the project.

Looking to the Future

As I look to the future, I want extremely low maintenance of my applications. Having a clean, organized codebase with few dependencies is critical to this goal, and I believe I am achieving it with the architechture described here.

Yet every time a new level of success is reached, I can't help but push myself even farther. What is truly "beyond MVC"? Stay tuned to see what I build next...