What this note is really about
When people first encounter terms like:
- MVC
- MVP
- MVVM
- layered architecture
- clean architecture
- hexagonal architecture
- onion architecture
it can feel like software design is full of competing acronyms invented to sound clever.
That is not the right way to look at it.
These patterns exist because software becomes hard to manage when:
- UI logic, business logic, and data access all get mixed together
- responsibilities are unclear
- dependencies point everywhere
- testing becomes painful
- change in one part breaks unrelated parts
So this note is not mainly about memorizing acronyms.
It is about understanding:
- what problem these patterns are trying to solve
- why so many variants exist
- what the major patterns are
- how they compare
- where each one fits well
The point is to become fundamentally clear-headed about why structured software patterns exist at all.
The first principle: these patterns exist to manage complexity
The broad problem is simple:
As software grows, one big pile of code becomes hard to:
- understand
- change
- test
- reuse
- reason about
So developers try to separate concerns such as:
- presentation
- user interaction
- business rules
- data access
- external system integration
These patterns are all, in one way or another, attempts to answer questions like:
- where should UI logic go?
- where should business logic go?
- who talks to the database?
- how should dependencies flow?
- how can we make code easier to test?
So the right mindset is:
these patterns are organizational responses to recurring software problems
They are not laws of nature.
The second principle: no pattern is universally best
A common beginner mistake is to ask:
- which pattern is the best one?
That question is usually too vague.
A better question is:
- what problem am I solving, in what kind of software, with what complexity?
For example:
- a desktop UI app has different pressures from a backend API
- a CRUD admin tool has different pressures from a long-lived domain-heavy system
- a small internal tool does not need the same architecture as a platform product
So patterns are tools, not medals.
The underlying recurring concerns
Before naming patterns, it helps to identify the recurring concerns they are trying to separate.
These usually include:
- presentation: what the user sees
- input handling: what happens when the user interacts
- application flow: coordinating use cases
- domain logic: business rules and core concepts
- persistence: databases and storage
- infrastructure: external services, files, messaging, frameworks
Different patterns separate these concerns in different ways and at different levels of strictness.
A useful distinction: UI patterns vs architectural patterns
Not all of these patterns live at the same level.
UI or presentation patterns
These mainly organize interaction between:
- screen
- user actions
- state
- presentation logic
Examples:
- MVC
- MVP
- MVVM
Application or system architecture patterns
These mainly organize larger-scale dependencies and boundaries across the whole application.
Examples:
- layered architecture
- clean architecture
- hexagonal architecture
- onion architecture
This distinction matters because people often compare patterns that are not really at the same layer.
For example:
- MVC and hexagonal architecture are not direct substitutes in the strictest sense
One often describes presentation structure, while the other describes larger dependency boundaries.
Start from the oldest broad problem
Early software often became tangled because code for:
- screen rendering
- event handling
- business rules
- and storage
all lived together.
That made every change risky.
For example:
- changing the UI could break domain rules
- changing database code could affect view rendering
- testing business logic required spinning up screens
Patterns like MVC emerged to reduce this entanglement.
MVC: Model-View-Controller
MVC is one of the most famous software structuring patterns.
At a broad conceptual level:
Model
Represents the application’s data and often domain-related state or rules.
View
Represents what is shown to the user.
Controller
Handles input and coordinates what should happen next.
The controller receives user actions, interacts with the model, and decides what view or response should be produced.
This was a major improvement over dumping everything into one place.
Why MVC existed
MVC existed to separate:
- user interface rendering
- user interaction handling
- application data/state
The key benefit was not the acronym itself.
The key benefit was that it reduced direct coupling between visual representation and core logic.
This improved:
- maintainability
- reuse
- testing
- conceptual clarity
That is the core reason MVC became influential.
MVC in practice
In real systems, MVC has been interpreted differently across frameworks.
Examples:
- Ruby on Rails
- ASP.NET MVC
- Spring MVC
- Django’s structure, though not always described exactly the same way
In many web frameworks, “controller” became:
- the class or function receiving HTTP requests
“View” became:
- HTML templates or rendered responses
“Model” often became:
- a mixture of domain objects, validation, and database-backed entities
This is important because “MVC” in framework marketing and “MVC” as a clean conceptual pattern are not always identical.
Common weakness of naive MVC
MVC can become weak when the “model” or “controller” absorbs too much.
Common problems:
- fat controllers
- anemic models
- database entities treated as the whole domain
- business logic scattered between controllers and services
So simply saying “we use MVC” does not guarantee good design.
MVC helps, but it does not replace disciplined responsibility assignment.
MVP: Model-View-Presenter
MVP evolved as another way to separate UI and behavior, especially in environments where UI event handling became awkward.
At a broad level:
Model
Represents data and domain or application state.
View
Represents the UI surface, often kept relatively passive.
Presenter
Contains presentation logic and coordinates between model and view.
The presenter often becomes the main testable unit for UI behavior.
Why MVP existed
MVP became attractive because people wanted:
- thinner UI code
- more explicit presentation logic
- easier unit testing of UI-related behavior
Especially in older desktop or event-heavy UI frameworks, keeping the view passive and pushing logic into a presenter could be much cleaner than putting behavior directly into the UI layer.
So MVP is often a stronger “passive view” answer than naive MVC.
MVP in practice
In MVP:
- the view usually exposes an interface
- the presenter talks to the view through that interface
- user actions are translated into presenter logic
This can work very well when:
- UI logic is non-trivial
- testability matters
- the framework otherwise encourages code-behind chaos
But MVP can also become verbose if the app is simple.
You may end up writing a lot of glue code for small benefit.
MVVM: Model-View-ViewModel
MVVM became especially popular in UI frameworks that support:
- data binding
- observable state
- declarative UI updates
At a broad level:
Model
Represents domain data or application state.
View
Represents the UI.
ViewModel
Represents the state and behavior the view needs in a UI-friendly form.
The ViewModel is often not the domain model itself.
It is a presentation model shaped for the view.
Why MVVM existed
MVVM became useful because some UI platforms made it practical for views to bind directly to state exposed by a ViewModel.
That allowed developers to avoid:
- large amounts of manual UI update code
- tangled event wiring
- direct business logic inside the UI layer
MVVM is strongly associated with technologies such as:
- WPF
- Silverlight historically
- Xamarin
- .NET MAUI in some styles
- Android architecture patterns
- some JavaScript frontend frameworks in spirit, though not always formally labeled MVVM
The key advantage is that UI state and behavior can be exposed in a testable and bindable way.
ViewModel is not just “model but renamed”
This is a frequent confusion.
A ViewModel is usually:
- tailored to a screen or view
- shaped for display and interaction
- often includes derived display state
- often exposes commands or bindable properties
For example, a domain Order object and an OrderDetailsViewModel are not the same thing.
The ViewModel may include:
- formatted strings
- UI-specific flags
- loading state
- selection state
- validation messages
That is why MVVM can be very powerful for rich UIs.
Common weakness of MVVM
MVVM can become weak when:
- ViewModels become giant dumping grounds
- too much domain logic leaks into ViewModels
- bidirectional binding becomes hard to reason about
- hidden coupling through binding magic makes flow unclear
So while MVVM can be elegant, it can also become opaque if not kept disciplined.
PAC, HMVC, and other presentation variants
There are other UI-structuring patterns too, such as:
- PAC: Presentation-Abstraction-Control
- HMVC: Hierarchical Model-View-Controller
You do not need to memorize all of them deeply at first.
The important takeaway is that many UI patterns are variations on the same broad attempt:
- separate display
- separate interaction coordination
- separate state or domain concerns
The names differ because different frameworks and eras emphasized different pain points.
Layered architecture
Now move from UI patterns to broader application structure.
One of the most common system-level patterns is layered architecture.
A typical layered structure may include:
- presentation layer
- application or service layer
- domain or business layer
- data access or persistence layer
Each layer has a broad responsibility, and higher layers depend on lower ones.
This is one of the most widespread software patterns because it is easy to understand and teach.
Why layered architecture existed
Layered architecture exists because applications often need clear separation between:
- what handles requests or UI
- what coordinates use cases
- what enforces business rules
- what talks to storage or external systems
It helps teams answer:
- where should this code go?
- what should depend on what?
That clarity is very valuable in medium-sized systems.
Strengths and weaknesses of layered architecture
Strengths
- simple mental model
- clear separation
- familiar to many developers
- works well for many business applications
Weaknesses
- can encourage over-layering and boilerplate
- business logic may still leak into service or controller layers
- domain layer may become weak if everything depends on persistence models
- dependency direction is sometimes not strict enough for highly complex systems
Layered architecture is often a solid default, but not automatically a perfect one.
Three-tier architecture
A closely related term is three-tier architecture.
This usually refers to broad deployment or logical separation such as:
- presentation tier
- application tier
- data tier
This term is often more infrastructural or system-level than MVC or MVVM.
It is important historically, but in practice many modern systems use more flexible service boundaries and do not always fit the clean classic three-tier picture.
Still, the core idea is the same:
separate concerns so each area can evolve more cleanly.
Clean architecture
Clean architecture is a broader architectural style emphasizing:
- clear dependency direction
- strong domain focus
- independence from frameworks
- separation between business rules and infrastructure
At a high level, the idea is:
- the core business logic should not depend on outer technical details
- frameworks, databases, and UI should depend inward on the core, not the other way around
This is a powerful idea because it protects the most important rules of the system from incidental technology choices.
Why clean architecture existed
It existed because many systems became too dependent on:
- frameworks
- ORMs
- UI tooling
- databases
When business rules become entangled with those outer layers, changing anything becomes painful.
Clean architecture tries to preserve the core by making dependencies point inward.
This often leads to concepts like:
- entities
- use cases
- interface adapters
- infrastructure implementations
Even if you do not adopt the full terminology, the dependency-direction idea is extremely valuable.
Onion architecture and hexagonal architecture
These are closely related to clean architecture.
Onion architecture
Emphasizes concentric layers with the domain at the center and infrastructure outside.
Hexagonal architecture
Also called ports and adapters.
It emphasizes that the application’s core should talk through abstract ports, while concrete adapters handle:
- databases
- web frameworks
- messaging
- file systems
These patterns are all trying to solve a similar problem:
keep the core application logic independent from external mechanisms
The vocabulary differs, but the philosophical direction is similar.
Ports and adapters: the key idea
In hexagonal architecture, the application core defines what it needs through abstract interfaces or ports.
Examples:
OrderRepositoryPaymentGatewayNotificationSender
Concrete infrastructure then implements those interfaces:
- SQL repository
- Stripe adapter
- email provider adapter
This is attractive because:
- business logic can be tested in isolation
- infrastructure details stay replaceable
- external systems are clearly treated as dependencies, not as the core
This pattern is especially useful in systems with rich domain logic and multiple integration points.
Clean/hexagonal/onion are not UI patterns
This is worth stating explicitly.
MVC, MVP, and MVVM mainly organize interaction around the presentation layer.
Clean, onion, and hexagonal mainly organize dependencies and boundaries for the application as a whole.
You can combine them.
For example:
- a desktop app may use MVVM for UI and a clean-architecture style for domain/infrastructure separation
- a web app may use MVC at the HTTP layer and hexagonal ideas internally
So do not treat these pattern families as if only one may exist in a codebase.
Repository and service layers
While not always treated as whole-application architecture patterns on their own, these are very common structuring patterns.
Repository
Abstracts persistence access in domain-friendly terms.
Service layer
Coordinates use cases or business operations that do not belong directly in one entity.
These often appear inside:
- layered systems
- clean architecture
- hexagonal designs
They help answer:
- where do workflows go?
- where do storage concerns go?
But they can also be overused if turned into generic dumping grounds.
For example:
UserService,OrderService,DataService,EverythingService
without clear responsibility boundaries is not good architecture.
How many such patterns are there?
There are many named patterns, but you do not need to treat them as an endless list to memorize.
At a practical level, the major ones worth understanding early are:
- MVC
- MVP
- MVVM
- layered architecture
- three-tier architecture
- clean architecture
- onion architecture
- hexagonal architecture
- ports and adapters
You should also understand common companion ideas such as:
- repository
- service layer
- dependency injection
- domain model
- presentation model
There are more patterns beyond these, but these form a strong conceptual foundation.
Why so many patterns exist
Because software contexts differ.
Different patterns became popular in response to:
- desktop UIs
- web request-response systems
- enterprise backend systems
- domain-heavy business applications
- systems requiring testability and framework independence
Also, some patterns are really:
- refinements of older ones
- renamings of similar principles
- framework-era adaptations
So the sheer number of patterns should not make you think software design is arbitrary.
It means recurring problems were attacked from slightly different angles.
Framework examples, briefly
It helps to attach names without going deep.
MVC-associated ecosystems
- Ruby on Rails
- ASP.NET MVC / ASP.NET Core MVC
- Spring MVC
- Laravel in broad structure
MVVM-associated ecosystems
- WPF
- Xamarin
- .NET MAUI
- Android architecture patterns
- some reactive frontend patterns conceptually
MVP-associated ecosystems
- older WinForms practices
- older Android patterns in some teams
- various desktop UI systems where passive view testing mattered
Clean / hexagonal / onion usage
These appear more as architectural styles than as one framework feature.
You will see them discussed in:
- ASP.NET backend systems
- Java/Spring backends
- domain-driven design oriented codebases
- microservice and service-platform codebases
The key point is that frameworks may encourage patterns, but the patterns are conceptually larger than the frameworks.
Comparison: what each pattern optimizes for
MVC
Optimizes for:
- separating input handling, view, and model
- straightforward request/response or UI organization
Good when:
- you want a familiar structure
- the app is not unusually complex
- the framework already supports it naturally
MVP
Optimizes for:
- explicit presentation logic
- passive views
- testable UI behavior
Good when:
- the UI framework tends to create messy event code
- you want stronger testability of presenter logic
MVVM
Optimizes for:
- bindable UI state
- declarative UI updates
- rich desktop or app-style interfaces
Good when:
- the framework supports binding well
- screen state is substantial
- UI responsiveness and state synchronization matter
Layered architecture
Optimizes for:
- simple broad separation of concerns
- team clarity
- maintainable medium-scale systems
Good when:
- the system is business-oriented
- domain complexity is moderate
- you want a practical general structure
Clean / onion / hexagonal
Optimizes for:
- strong core-domain protection
- dependency discipline
- testability
- framework and infrastructure independence
Good when:
- the system has important business logic
- the system will live a long time
- infrastructure should not dominate the domain
- you integrate with many external systems
Common misuses
Patterns often fail not because the pattern is bad, but because it is misunderstood.
Common misuses include:
1. Cargo cult adoption
Using a pattern because it sounds professional, without understanding its purpose.
2. Excessive layering
Creating many layers with no meaningful responsibility separation.
3. Anemic domain models everywhere
Pushing all real logic into services and leaving models as data bags.
4. Framework worship
Letting the framework’s default file structure become the architecture without deeper thought.
5. Pattern mixing without clarity
Combining patterns in a way that obscures who owns what.
6. Overengineering small systems
Applying heavyweight architecture to tiny tools where it adds more ceremony than value.
This is why pattern knowledge must be paired with judgment.
How to choose a pattern
The right way to choose is not:
- which acronym is fashionable?
It is:
- what kind of software is this?
- where is the complexity?
- what kind of change do I expect over time?
- what kind of testing matters?
- how much UI state exists?
- how rich is the domain logic?
- how coupled do I want to be to the framework?
These questions guide better choices than pattern branding.
Practical advice by context
Small internal CRUD web app
Usually:
- simple MVC or layered architecture is enough
Do not force elaborate clean-architecture ceremony unless the complexity truly demands it.
Rich desktop or mobile UI app
Usually:
- MVVM is often attractive if the UI technology supports binding
- MVP may also work if you want explicit presentation logic
Large business backend with meaningful domain logic
Usually:
- layered architecture at minimum
- clean, onion, or hexagonal ideas become more valuable
Integration-heavy service or platform
Usually:
- ports and adapters or hexagonal thinking is very helpful
Why?
Because many external dependencies should remain outside the application core.
Short-lived prototype
Usually:
- prefer simplicity
- structure enough to avoid chaos, but do not overbuild
The architecture should fit the expected lifespan and complexity of the software.
Testing and these patterns
One major reason many of these patterns became popular is testability.
Examples:
- MVP allows presenter logic to be tested without real UI
- MVVM allows ViewModel behavior to be tested without full screens
- clean architecture allows use cases to be tested without database or web framework
- hexagonal architecture allows adapters to be swapped with mocks or fakes
So if you ask:
“Why are engineers willing to accept more structure and abstraction?”
one real answer is:
because isolated testing becomes much easier.
That matters a lot in systems that must evolve safely.
Dependency direction is one of the deepest ideas here
Many patterns differ in surface structure, but a very deep recurring question is:
what depends on what?
That question matters because dependencies shape:
- coupling
- replaceability
- testing
- change cost
Shallow design often lets everything depend on everything.
Stronger design usually tries to ensure that:
- UI depends on core logic, not vice versa
- infrastructure depends on abstractions, not the core depending directly on vendor details
- business rules are not trapped inside framework code
If you understand dependency direction, you understand one of the most important reasons these patterns exist at all.
These patterns are not substitutes for good thinking
A codebase can say:
- MVC
- clean architecture
- MVVM
and still be poorly designed.
Why?
Because naming a pattern does not automatically solve:
- unclear responsibilities
- bad naming
- overgrown classes
- weak domain understanding
- poor boundaries
Patterns are organizing tools.
They help good thinking scale.
They do not replace good thinking.
The most important conceptual thread
If you want one flow that ties the whole topic together, it is this:
Software patterns such as MVC, MVP, MVVM, layered architecture, clean architecture, onion architecture, and hexagonal architecture exist because growing software needs clear separation of concerns and disciplined dependency structure. UI-oriented patterns mainly separate what is shown, how user interaction is handled, and how presentation state is represented. Larger architectural patterns mainly separate business rules from infrastructure, frameworks, persistence, and external systems. The many named variants exist because different eras, frameworks, and kinds of software exposed different pain points, but the underlying goals are very consistent: reduce coupling, increase clarity, improve testability, and make change less painful over time.
What you should come away knowing
You should leave this topic with these ideas clearly separated:
- These patterns exist to manage complexity, not to create ceremony.
- MVC, MVP, and MVVM are mainly presentation-structuring patterns.
- Layered, clean, onion, and hexagonal are broader architectural patterns.
- A ViewModel is not just a renamed model; it is usually a presentation-shaped state object.
- Ports and adapters are about protecting the application core from infrastructure details.
- No one pattern is universally best.
- The right choice depends on software type, complexity, and expected evolution.
- Testability and dependency direction are major reasons these patterns matter.
- Frameworks may encourage patterns, but frameworks are not the same thing as architecture.
- The deepest recurring question is always: which responsibilities belong where, and what should depend on what?
Bottom line
Patterns like MVC, MVP, MVVM, layered architecture, clean architecture, onion architecture, and hexagonal architecture are different answers to recurring software design problems. They exist because software becomes difficult when presentation, behavior, domain rules, persistence, and infrastructure are tangled together. If you understand what concern each pattern is trying to separate, what level of the system it applies to, and how it shapes dependency direction, then the acronyms stop feeling arbitrary and become practical tools for designing clearer systems.