{"id":2968,"date":"2024-08-27T08:05:38","date_gmt":"2024-08-27T07:05:38","guid":{"rendered":"https:\/\/contentlabstudy.com\/soft\/?p=2968"},"modified":"2026-09-24T21:16:29","modified_gmt":"2026-09-24T20:16:29","slug":"mvc-mvvp-and-mvp","status":"publish","type":"post","link":"https:\/\/contentlabstudy.com\/soft\/mvc-mvvp-and-mvp\/","title":{"rendered":"MVC, MVVM, and MVP"},"content":{"rendered":"<p><strong>MVC, MVVM, and MVP<\/strong> split UI from domain so you can test and change one without the other. They are presentation patterns, not system architecture. If you need service boundaries, see <a href=\"\/soft\/architectural-patterns\/\">Architectural Patterns<\/a>.<\/p>\n<h2>MVC \u2014 Model\u2013View\u2013Controller<\/h2>\n<p>The <strong>model<\/strong> is state and rules. The <strong>view<\/strong> renders. The <strong>controller<\/strong> accepts input and updates the model (and often chooses the next view). In original Smalltalk MVC the view <em>observes<\/em> the model. In server MVC (ASP.NET, Rails, Laravel) a request hits a controller, which loads a model and returns a view; there is no long-lived observer. Do not mix those two stories.<\/p>\n<ul>\n<li><strong>Use<\/strong> request\/response UIs and simple pages.<\/li>\n<li><strong>Pain<\/strong> when the controller becomes a dumping ground (HTTP, SQL, and HTML in one method).<\/li>\n<\/ul>\n<h2>MVVM \u2014 Model\u2013View\u2013ViewModel<\/h2>\n<p>The <strong>ViewModel<\/strong> is an adapter the view can bind to: properties, commands, validation state. The view should not call repositories. Two-way binding is common on desktop (WPF) and in some SPA stacks; it is a technique, not the definition. Test the ViewModel without the UI toolkit.<\/p>\n<p>The code-behind calls the repository. The view-model takes the repository instead, and a test calls it without the toolkit.<\/p>\n<pre><code>void OnLoad()\n{\n    Name = repo.Find(id).Name;\n}<\/code><\/pre>\n<pre><code>sealed class CustomerVm\n{\n    public string Name { get; }\n    public CustomerVm(ICustomers customers, Guid id) =&gt; Name = customers.Get(id).Name;\n}<\/code><\/pre>\n<ul>\n<li><strong>Use<\/strong> rich client UI, or SPA screens with a lot of local state.<\/li>\n<li><strong>Pain<\/strong> when the ViewModel is a second copy of the database row plus <code>INotifyPropertyChanged<\/code> noise, or when you name it MVVM and still put SQL in code-behind.<\/li>\n<\/ul>\n<p>There is no \u201cMVVP.\u201d That was a typo for MVVM.<\/p>\n<h2>MVP \u2014 Model\u2013View\u2013Presenter<\/h2>\n<p>The <strong>presenter<\/strong> talks to a passive view through an interface the presenter owns (<code>ILoginView<\/code>). The view forwards clicks; the presenter sets fields. Easier to unit-test than MVC with a concrete view. The presenter tends to be chatty and coupled 1:1 with a screen \u2014 that is expected.<\/p>\n<h2>How to choose<\/h2>\n<ul>\n<li>HTTP page\/API: MVC (or endpoint-centric Minimal APIs \u2014 still not MVVM).<\/li>\n<li>WPF \/ MAUI \/ rich SPA form: MVVM.<\/li>\n<li>Legacy WinForms or Android with awkward binding: MVP is still a clean seam.<\/li>\n<\/ul>\n<h2>Example<\/h2>\n<p>The action loads a model and returns a view.<\/p>\n<pre><code>public IActionResult Show(Guid id)\n{\n    var model = customers.GetSummary(id);\n    return View(model);\n}<\/code><\/pre>\n<h2>What breaks it<\/h2>\n<p>The action runs SQL and builds the HTML. The next change to the markup and the next change to the query land in the same method.<\/p>\n<pre><code>public IActionResult ShowBroken(Guid id)\n{\n    using var cmd = db.CreateCommand();\n    cmd.CommandText = \"SELECT name FROM customer WHERE id = $1\";\n    var name = (string)cmd.ExecuteScalar()!;\n    return Content(\"&lt;h1&gt;\" + name + \"&lt;\/h1&gt;\");\n}<\/code><\/pre>\n<h2>Pitfalls<\/h2>\n<ul>\n<li>Calling every frontend \u201cMVC\u201d because there is a folder named <code>Controllers<\/code>.<\/li>\n<li>Putting domain rules only in the ViewModel \u2014 then the API and a batch job disagree.<\/li>\n<li>One-way vs two-way binding as a religion; pick what the platform does well.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>MVC, MVVM, and MVP split UI from domain so you can test and change one without the other. They are [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[4],"tags":[],"class_list":["post-2968","post","type-post","status-publish","format-standard","hentry","category-software-design"],"_links":{"self":[{"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/posts\/2968","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/comments?post=2968"}],"version-history":[{"count":4,"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/posts\/2968\/revisions"}],"predecessor-version":[{"id":3250,"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/posts\/2968\/revisions\/3250"}],"wp:attachment":[{"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/media?parent=2968"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/categories?post=2968"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/contentlabstudy.com\/soft\/wp-json\/wp\/v2\/tags?post=2968"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}