Introduction:
Countless framework designers and programmers have found that MVC (Model-View-Controller) is the most successful way of organizing code in web development. It has been used in various frameworks and projects guiding the arrangement of applications by developers. However, let’s be honest, MVC is not perfect. This means that it contains multiple weaknesses that make coding complex; maintainability can turn into a nightmare and may lead to disappointments among the developers. When we are building a web application, it will be important for us to learn from such weaknesses without completely dismissing MVC. In this blog post, we shall discuss some of these shortcomings associated with MVC.
The Problem with MVC:
According to its proponents, Model should manage data while View should render UI and Controller should act as an intermediary between both of them. At first sight, this separation seems reasonable enough but at practice it results in bulky controllers, thin models and tight coupling components.
Let's take a look at a typical MVC setup in PHP:
class PostController {
public function index() {
$posts = Post::getAll();
require('views/posts/index.php');
}
}
class Post {
public static function getAll() {
// Fetch posts from the database
}
}
// View
foreach ($posts as $post) {
echo "<h2>{$post->title}</h2>";
echo "<p>{$post->content}</p>";
}
It is the controller’s responsibility in this example to retrieve information from the model and pass it on to the view for rendering. Nonetheless, with the growth of an application, a controller tends to become overloaded with logic for handling various requests thereby adding onto its difficulty of maintainability and testing.
The Proposal: Going beyond MVC
Instead of blindly following MVC, we could adopt a more elastic architecture that addresses the inadequacies of MVC. One of them involves blending Domain-Driven Design (DDD) with CQRS (Command Query Responsibility Segregation).
DDD puts greater emphasis on what are referred to as domain-driven models rather than the technical frameworks used when building applications. By defining richly structured domains that include distinct entities, value objects, and aggregates, we can confine most business rules within applications.
CQRS helps split our application into read or write operations thus optimizing read and write models independently such that they improve performance and scalability.
Let's refactor our previous example using DDD and CQRS principles:
// Controller
class PostController {
private $postService;
public function __construct(PostService $postService) {
$this->postService = $postService;
}
public function index() {
$posts = $this->postService->getAllPosts();
require('views/posts/index.php');
}
}
// Service
class PostService {
private $postRepository;
public function __construct(PostRepository $postRepository) {
$this->postRepository = $postRepository;
}
public function getAllPosts() {
return $this->postRepository->getAll();
}
}
// Repository
class PostRepository {
public function getAll() {
// Fetch posts from the database
}
}
// View
foreach ($posts as $post) {
echo "<h2>{$post->getTitle()}</h2>";
echo "<p>{$post->getContent()}</p>";
}
So in this in example, we have introduced a service layer responsible for orchestrating business logic and a repository layer responsible for interacting with the data store. This separation of concerns makes our code base more maintainable and testable.
Conclusion:
So, the end point of this discussion will be that while MVC has been used as a model for web based applications for years, it is important to realise its inadequacies. By taking lessons from MVC’s flaws as well as embracing other flexible architectures like DDD and CQRS we can come up with web applications that are easier to maintain, scale up and follow clean code and good design principles. Thus let us go beyond MVC and embrace the changing structure of web applications.
Top comments (4)
You have good points, just be aware you don't drown yourself in design patterns just for the sake of it.
In your example the first code you wrote is good enough. The service is an extra function call that adds nothing. And you created another layer you have to debug.
If you want testable controllers and models dependency injection will do the trick without adding too much code.
The (test)framework you use will instantiate the object for you, as long as it follows the interface you are good to go.
About the controllers maintainability, that depends on how you structure them.
MVC is just one of the tools you can use to structure your code, it does not suck. If you hit your finger with a hammer , it is not a bad hammer.
DDD is good but it requires a lot of preparation and communication. If you create a domain on your own, you have to explain it to everyone else and that can be a lot of extra work.
CQRS is only needed if you experience a lot of deadlocks because the database can't handle all the manipulations. Most databases are fast enough for most sites.
What most developers want is the message bus architecture, which can be a part of CQRS.
Good thought provoking article, my experience has been that MVC tries to be a one-size-fits-all, and more discussion about the application can probably lead to a better custom approach.
I see that you've taken DDD as an inspiration, it's almost like M, V, and C are each a DDD domain, and that actually makes more sense sometimes. That way you can have two or more domains, and focus on what they do more than what they are.
Data locality and ownership can be a concern in the Controller as well as the Model and the View.
Adding service and api layer doesn't change anything about the MVC architecture you criticize. It only abstracts away certain kind of operations the controllers were doing themselves in your example, but doesn't change anything about the underlying architecture.
I'd also say that DDD doesn't have much effect on the MVC architecture. You can have MVC both with and without DDD.
But bro now a days views are now used...