36 comments

[ 3.5 ms ] story [ 48.8 ms ] thread
(comment deleted)
I think nearly every definition of MVC I've read has been different. At this point it just means you split something into three classes as far as I can tell.
A major advantage of pure models is testability. If your conception of a "model" is perversely a user-facing widget, congratulations, you'll need to write UI tests that simulate button presses and other such user actions, and maybe even inspecting pixels to check resulting state. Tests like that are a pain to compose and are fragile since the UI tends to evolve quickly and may also be vulnerable to A-B experiments. Juice ain't worth the squeeze in most cases.

In contrast, pure model components tend to evolve slowly, which justifies the investment of a comprehensive test suite which verifies things like data constraints, business logic, persistence. If automated testing were seen as a priority, this would be a no-brainer for any serious app. However, testing tends to be underappreciated in app development. This goes some way to explaining why frameworks carelessly fold in M, V, C to the same component.

Testing by "simulating" button presses and other actions like that, including inspecting pixels, is part of so-called "black-box" testing, and offers merit(s) of its own. At least because software is used by people who click buttons which may modify pixels, and these people are not concerned what your model is, they don't even know anything about the way you may have implemented the latter. In the end everything is run on a fairly RISC-y CPU, it either works or it doesn't (from user's perspective) -- replicating the user's workflow is useful in that it it uncovers issues that matter to users and thus normally affect your bottomline.
Getting totally lost in several different enterprise software implementations of MVC was a major contributor to my impostor syndrome early in my career. Glad to have some sort of vindication that I wasn't alone
This problem that MVC has is similar to the problem with OOP itself, with monads, or with some design patterns: the original/popular definitions were so incredibly abstract and disconnected from real life usage that they ended up being whatever the person implementing it wanted it to be.

And then, 10, 20 years after the fact, people will start attacking popular implementations that differ from the original using some "new canonic interpretation" that is either extremely recent, or an interpretation that is old but was lost in time.

This is especially common around Smalltalk and OOP for some reason. Smalltalk's OOP is nothing like what existed either before or after, but since Alan Kay invented the term, Smalltalk is weaponised against C++/Java-style OOP. Not that C++/Java OOP is the bees knees, but at least their definition is teachable and copyable.

Design patterns suffer because in most explanations the context is completely missing. Patterns are totally useless outside very specific contexts. "Why the hell do I need a factory when I can use new"? Well, the whole point is that in some frameworks you don't want to use new Whatever, you dummy. If only this was more than a two-sentence blurb in the DDD book (and the original patterns book totally glosses over this, for almost all patterns).

And monads became the comical case, because they are totally okay in Haskell, but once it gets "explained" and migrated to other languages they become this convoluted mostly useless abstraction at best, footgun at worst (thinking of the Ruby one here).

Any implementation of MVC I've seen the V and the C are so tightly coupled the separation seemed artificial. Skill issue?
The reason MVC got so abused was because of RAD frameworks: rapid application development. Most of this started with visual basic.

Basically, the thinking was to let the programmer design the view and then implement the code-behind. I'll spare you from my rants about this, but it was popular.

Nowadays, with vibe coding, there is no need to use obtuse design patterns for the sake of RAD. Sensible architectures can easily by used by LLMs without sacrificing engineer or designer agility.

I feel like MVC is trying to get at a core concept of having your application state in one place, your view objects/state in another place, and then having a third piece that updates the application state.

So like in React, you'd have your Redux store as the Model, React components (with useState etc) as the View, and then your Controller is the reducer which is called from UI code and updates the Model.

Maybe that's incorrect definitionally, but it makes sense to me.

One of the other markers of "true MVC" I look for is that you ought to have pervasive mixing and matching of the pieces. It is common for models to see some reuse in multiple "views" and "controllers", but if all or almost all of your controllers match up to precisely one view, then you're burning design budget on a distinction that is buying you nothing. If you've got strictly one-to-one-to-one matches for each model, view, and controller, then you're just setting your design budget on fire.

Another major aspect of the original "true" MVC is multiple simultaneous views on to the same model, e.g., a CAD program with two completely live views on the same object, instantly reflecting changes made in one view in the other. In this case MVC is less "good idea" than "table stakes".

I agree that MVC has fuzzed out into a useless term, but if the original is to be recovered and turned into something useful, the key is not to focus on the solution so much as the problem. Are you writing something like a CAD program with rich simultaneous editing? Then you probably have MVC whether you like it or realize it or not. The farther you get from that, the less you probably have it... and most supposed uses of it I see are pretty far from that.

This perpetuates the myth that a model is an object. One object. This has lead to todays common misconception that a model is one anaemic data-bucket representing typically a database table.

Instead a model is one or more collaborating objects.

As a former and long smalltalker who learned MVC from the ParcPlace crowd…

I used to say things like this. M and V were always pretty unambiguous, but “controller” was kind of like “Christianity”, everyone talks like it’s a unifying thing, but then ends up having their very own thoughts about what exactly it is, and they’re wildly divergent.

One of the early ParcPlace engineers lamented that while MVC was cool, you always needed this thing at the top, where it all “came together” and the rules/distinctions got squishy. He called it the GluePuppy object. Every Ux kit I’ve played with over the years regardless of the currently in vogue lets-use-the-call-tree-to-mirror-the-view-tree, yesteryears MVVM, MVC, MVP, etc, always ends up with GluePuppy entities in them.

While on the subject, it would be remiss to not hat tip James Depseys MVC song at WWDC

https://youtu.be/kYJmTUPrVuI?feature=shared

“Mad props to the Smalltalk Crew” at 4:18, even though he’d just sung about a controller layer in cocoa that was what the dependency/events layers did in various smalltalks.

There’s also a difference between the code needed to create and set up the combination, as opposed to the code that executes based on events after things have been set up. The way MVC is generally presented, it is only about the event-time code, but in practice the set-up code and the event-time code are not independent and are usually combined within a single class or module.
(comment deleted)
It would be interesting to read a case study of how the MVC was applied in some larger SmallTalk app.
> For example, in ObjC an int is an object, but it is not observable. However, an ObjC object with an int property is observable using Key-Value Observing

I haven't written Objective-C in a decade or so, but isn't this a pretty big mischaracterization of the language? NSInteger is a typedef to a C type IIRC, while there's NSNumber for the cases you want an object and/or are deserializing -- and which has observable propeties?

I used to be very confused about MVC and MVCC and what have you---I can't keep design patterns straight in my head (personal limitation)---I finally went down a rabbit hole of trying to figure it out from scratch.

Like, why do we even need any of that stuff? I blogged about it [1] and spoke about it [2] and the post even got some HN love [3].

The opening parable concludes with this...

   Multitudes of sworn "Rails developer"s, "Laravel developer"s, "Django 
   developer"s, "Next.js developer"s and suchlike throng the universe… 

   Why?

   ...

   ...

   Once upon a time, there was one.
   WebObjects.
   Now they are numberless.
The occasional email and DM gives me succour that I am not alone in my confusion. Even people who've "grown up" using traditional MVC frameworks took a minute to self-check and felt "huh, looks like I can look harder at this thing that I do".

Clojuring the web application stack: Meditation One

[1] blogged: https://www.evalapply.org/posts/clojure-web-app-from-scratch...

[2] talked: https://www.youtube.com/watch?v=YEHVEId-utY&list=PLG4-zNACPC...

deck: https://www.evalapply.org/posts/clojure-web-app-from-scratch...

source: https://github.com/adityaathalye/clojure-multiproject-exampl...

[3] discussed: https://news.ycombinator.com/item?id=44041255

165 points by adityaathalye 3 months ago | 39 comments

It was basically the 3-tier architecture hijacked by some authors with hyper sales-pitch who messed it up beyond recognition. The 3-tier model has 3 simple layers - Data, Business, Front-end. The MVC inventors called data as "Model" - I could not get my head around this weird naming. What does "Model" mean in plain English? And why do you need those dotted lines between Front-end and Data layers bypassing the Business layer? MVC is a fake that lasted for decades.
Everyone knows what model is. Almost noone knows what is controller.
Largely agreed, I wrote about the confusion around 10 years ago[1] and wrote 2 years later about the problem of concept capture we have with MVC[2] :

I'd like to add a couple of points to TFA.

Yes, it is absolutely paramount to understand what the model is. It is the abstract representation of the domain. The rest of the architecture serves the model and should be as minimal and transparent as possible. Particularly Apple-space code tends to get this very wrong by having very thin models and all the logic in the Massive View Controllers.

It is also important to understand that MVC is not about specific objects, but rather about roles and communication patterns. Different objects can have those roles, and they can actually be usefully combined at times (though do keep the model separate, please).

One crucial part of the communication patterns that TFA duly notes is that models do not know about views. That means that views only ever pull data from models, models never push data towards views. It also means that in an update, the model just says "I have changed". It does not send the data that changed. The "the model changed" notifications is also the only reason a view gets updated.

No, the controller doesn't poke the correct updated data into the view after it has notified the model, that leads to update chains and cycles. IIRC, that was one of the problems that React was trying to solve with "MVC", except it turns out that actual MVC never had those problems in the first place. Mis-application of MVC does.

Having the view always update itself from the model means that view state is always consistent, even if you miss or skip intermediate updates. Skipping intermediate view updates is important, because the model can potentially change a lot faster than the view can update, never mind the user processing those view updates.

Also, one common misunderstanding (that also leads to Massive View Controllers) is the mistaken belief that views must not edit models, that you must have a controller to edit it. That is actually not in the original MVC paper[3]. In the original paper, views can edit models and that makes things a lot more sensible.

Controllers are a bit of a catch-all for stuff that didn't fit anywhere else. The Views in Cocoa actually take over some of those roles, and that works absolutely fine. (Imagine my confusion when ViewControllers were introduced...)

[1] https://blog.metaobject.com/2015/04/model-widget-controller-...

[2] https://blog.metaobject.com/2017/03/concept-shadowing-and-ca...

[3] https://web.archive.org/web/20090424042645/http://heim.ifi.u...

I think what Qt is doing with the Model being written in C++ and notifying to a QML View using Signals & Slots is the best way to do it.
This doesn't just apply to MVC but also for example the Elm architecture. The thing is that they are quite simple in theory but in practice, programming them from scratch can be much more difficult, mostly due to the View.

For example, what if you have two widgets that need to be side by side? And the user needs the ability to use the keyboard to switch between them? What if now you have a third widget below them that is also tabbed?

At this point you need a state machine to track the state and where the user is currently at. It's easy if this is done for you but pretty difficult otherwise in either architecture.

Meh, I disagree. Models can represent data stores, models can represent data views, and models can be for data transfer.

This is necessary for zero trust in application design. Traditionalists really seem to struggle with this shift in mentality that, when you are designing a system, trust is where the problems come from.

Just as an example, collecting date inputs from a user might be three different fields in the view model and only one field in the data model, and be completely different data types (int vs datetime). If you are working with a client side application then you may not want to pass the entire object to the client because you don’t trust them with all the information, and you cannot trust them to maintain state, so you only transfer the date value in a data transfer object.

These are all models with wildly different intents. If you can’t understand the intent of this separation of concerns then you are designing insecure systems.

Eh. I’ve been writing Apple software for a long time (since 1986). Most of that time, it’s been some pattern that resembles MVC, and almost all was OOP (in a few different languages).

UIKit is explicitly designed for MVC. If you want to write the most concise, performant, maintainable, UIKit code, you do so, using MVC, and classic OOP. I have tried other models, but they end up as messy kludges.

SwiftUI was designed to be more flexible, and can employ other patterns. I find that OOP is sometimes useful (especially for things like observable models), but there’s no reason not to do it, using other methods. It doesn’t force you to use anything in particular.

The main issue with SwiftUI, is that it’s still quite “unripe,” and we are limited in what we can do with it. I am looking forward to this changing, over time (it’s already improved, quite a bit). Time will tell, whether or not it can completely replace UIKit. I haven’t really been able to use it for any of my shipping projects, yet. I know of a number of apps that have, but I’ve been unwilling to make the compromises necessary, to do it, myself.

Some tools were designed to be used in certain ways, and coercing them into methodologies for which they weren’t designed, can result in a mess.

If I want to bang nails into a board, a hammer is the best tool. I have banged nails in the past, by flipping a screwdriver around, and using the handle, but that damages the screwdriver, and doesn’t work especially well.

But maybe a nail isn’t the best way to join the boards. If I use screws, then the join will be much better. In that case, the proper tool is a screwdriver. I guess I could still use a hammer, but the results are unlikely to be satisfactory.

MVC always seemed fucking pointless to me, like shit code isn't gonna get good if you put it in boxes
I've become convinced that the real problem is probably impossible to get away from.

Ultimately we want a nice set of reusable UI components that can be used in many different situations. We also want a nice set of business logic components that don't have any kind of coupling with the way they get represented.

In order to make this work, we're going to need some code to connect the two, whether it's a 'controller' or a 'view model' or some other piece of code or architecture.

However we choose to achieve this task, it's going to feel ugly. It's necessarily the interface between two entirely different worlds, and it's going to have to delve into the specifics of the different sides. It's not going to be the nice clean reusable code that developers like to write. It's going to be ugly plumbing, coupled code that we are trying to sweep into one part of the codebase so that we can keep the rest of it beautiful.

These seemingly inescapable tradeoffs are almost always actually quite escapable if you look at it from a different perspective. You have to stop thinking about pages and button-clicks, and you have to stop using frameworks that try to do everything, box you in, and force you to architect your state-flow logic based on your visual hierarchy. This is the biggest problem with almost all UI frameworks: all your logic has to be partitioned along the lines that are set up by how your screen looks, or you're swimming upstream to prevent it. Instead, have your domain models declare how they work and interact using intermediate services, and consume those services to generate the UI as a consequence of those declarations. It's very hard, and I don't have all the answers yet, but I've tasted enough to know that it genuinely avoids this otherwise seemingly inescapable tradeoff. I'm not planning to ever build another UI (above some complexity) differently again.