Even when adopting MVC or MVVM, why does code often turn into spaghetti? Why do ViewModels or Controllers evolve into massive "God Classes"?
The root cause is often a vague definition of the "Model".
"Isn't the Model just a data container (DTO) for the database?"
"Shouldn't the logic go into Services or ViewModels?"
If you have ever thought this, this article is for you.
Here is the definitive boundary of what a Model should be.
The Purpose of MVC (or MVVM)
Let's skip the complex academic theories. The purpose is singular:
To separate "Business Logic (Model)" from "Presentation (View / Controller)".
That's it.
What is "Business Logic"?
Many developers stumble on the term "Business Logic," so let's define it clearly.
Business Logic
The logic that represents the flow of business rules or domain processes.
Don't get confused by the word "Business."
- If you are developing a Game: It is the "Core Logic" (e.g., damage calculation).
- If you are doing DDD: It is the "Domain Logic".
Regardless of the name, the concept is the same. It is the core rule of the system—like "calculating taxes," "managing state," or "making judgments"—independent of application convenience (like "show a dialog when a button is clicked").
The Definition and Boundary of the Model
So, where is the boundary for the Model that houses this logic?
The Model is the place where you push all your Business Logic.
There is one ultimate "Litmus Test" to check if your class is truly a Model.
The Ultimate Test
Ask yourself this question:
"If I were asked to replace the GUI with a CLI (Command Line Interface) tomorrow, would this class survive?"
-
It Survives (Works without modification) ✅
- This is a Model.
-
It Fails (Compile errors, depends on UI libs) ❌
- This is NOT a Model.
A true Model must never depend on the View (GUI/CUI).
Example: A Calculator App
Let's look at a Calculator app to see the structure of a "Model that survives."
Pattern A: GUI App (WPF / React / etc.)
-
View:
MainWindow- Buttons, TextBoxes, UI components.
-
Controller (ViewModel):
MainWindowViewModel- The mediator between View and Model.
-
Model:
Calculator- Crucial: This class handles operands, calculation logic, and State Management.
Pattern B: CLI App (Console)
Now, imagine you have to port this logic to a Console application.
-
View:
Console- Standard Input/Output.
-
Controller:
MainConsoleController- Handles key inputs.
-
Model:
Calculator- You reuse the exact same class from the GUI version.
The Key Point
The most important takeaway here is that the Calculator class holds the "State".
The Calculator needs to remember the entered numbers, the current operator, and the running total. Do not let the ViewModel hold this state.
The moment you put this state into the ViewModel, it becomes "Logic for the GUI." When you try to make the CLI version later, you will be forced to copy-paste (re-implement) that logic.
Conclusion
The Model is the part of the code that survives even if the View changes.
UI frameworks (React, Vue, WPF) come and go every few years. However, the Domain Logic (The Model) should remain unchanged as long as the system exists.
Do not let your assets (Core Logic) depend on consumables (Views). This is the first step in solid architectural design.
Sample Code:
https://github.com/neo-potato/MVC-Sample
Top comments (0)