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).
ozim · 19h ago
From my experience patterns suffer from new devs thinking that patterns are guidelines for writing their code and that they should create code that is generic and implements patterns. (Yes if their job is implementing framework, not when their job is to use framework to build business app)
Like everyone totally forgot patterns are mainly for understanding existing systems like you use a framework - hey this looks like a factory let’s use one from the framework we built stuff with instead of implementing our own.
Besides of course every developer wanting to build framework so others adhere to what he built not the other way around ;)
ahartmetz · 17h ago
I think of design patterns as possible solutions to certain problems. They should be used when the corresponding problem arises and their mental overhead is less than whatever the alternatives may be. I use them quite sparingly.
I don't find them very useful (today) to understand existing systems that don't intentionally use the patterns. They don't occur very often in well-designed systems in the first place, even less so unintentionally.
ozim · 16h ago
First of all design patterns were not some god given solutions. Those are solutions gathered from existing implementations and authors just gave names to those solutions.
So they will be present in well designed systems just that they are not called by their „book name”.
Then I clearly see it in all new frameworks just that each framework has their own name for implementation of the pattern.
Patterns were mostly named so people can discuss easier about solutions that are there.
I will quote first sentence of foreword from my copy of the book „All well-structured object oriented architectures are full of patterns.”
bluGill · 12h ago
Everything about object oriented from the book was added by editors because OO was the fad at the time. Most of the patterns apply to other styles of code. The authors were not confined to studying OO programs
ozim · 9h ago
I don’t think we are writing about the same book: ”Design Patterns:
Elements of Reusable
Object-Oriented
Software”.
That’s the one I write about, it has foreword from Grady Booch and was tied to OOPSLA meeting with C++ and Smalltalk examples. To add OOP stuff to it sounds like a different book because that book is about OOP by definition.
bluGill · 7h ago
That book. The title was not from the main author.
ahartmetz · 7h ago
AFAIK, the title is never chosen by the author (except maybe for the most successful ones)
bluGill · 4h ago
Pretty much and so you need to be careful about what you state about any book based on title. Though most of the time the title doesn't mislead this baddly.
of course at the time they really thought nobody would question if oo was the best way to program so it didn't seem usefully misleading. Patterns apply to non oo programning as well
whstl · 18h ago
The original book completely failed to communicate that, though. This is common knowledge today, but wasn’t for a long time.
asalahli · 19h ago
> 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.
This is what happened with REST too, and it frustrates me more than it probably should.
The original pattern is such a good idea and not even remotely abstract. It's a well defined architectural pattern for a well defined problem yet people still managed to bastardize it to the point that the term REST barely means anything today
lmm · 9m ago
> The original pattern is such a good idea and not even remotely abstract. It's a well defined architectural pattern for a well defined problem yet people still managed to bastardize it to the point that the term REST barely means anything today
The original pattern is extremely abstract and a bad idea. There has been precisely one successful implementation of the original REST "pattern", the web, and only because the pattern was retrofitted onto it; most of the things in REST-as-originally-defined are bad ideas, as any apples-to-apples comparison will show.
I get oppositely frustrated because "REST" was adopted as a rallying cry for one or two good ideas (fitting your protocol to the GET/POST and 2xx/4xx/5xx distinctions from HTTP instead of treating it as a completely opaque transport layer; not wrapping everything in oodles of XML) and the term brought along a lot of bad ideas as baggage. But the meaning of the term shifted towards doing the things that are good because the original meaning was bad.
whstl · 18h ago
This is another good example. I remember people getting very angry here 10-15 years ago over HATEOAS.
In most discussions REST has come to mean “cute URLs” thanks to Rails.
mcny · 17h ago
I don't know who is to blame for this -- I think it should be the entire team, not just the developer for every time I click a link from a message somewhere on my phone and a website opens which opens an app and it says "congratulations, you have performed this operation (that isn't idempotent)". Like ok, but why are you doing stuff with a get action... Shouldn't you prompt people to ask in the app hey I am about to do something, do you want me to?
Pamar · 18h ago
What about "microservice", then?
whstl · 18h ago
This is perhaps a case where miscommunication saved an entire industry.
I once got a new hire from Uber and for months on end his complaint was that “the services are too big”.
mcny · 17h ago
I once wrote a small class at work and by the time I left it was like over 8k lines long. People jokes it was my fault I called it HelperUtil instead of something more descriptive. It was a dumping ground for all the stuff people didn't want to think about. I wonder if something like that is possible in the microservice world?
ssdspoimdsjvv · 14h ago
Of course, it just becomes the HelperUtilService!
mattmanser · 16h ago
It probably wasn't a joke. If you call something HelperUtil, it will become a dumping ground. That's a learnable lesson around naming, a mistake, but it's not learnable if it keeps getting described as a joke.
C# accidentally solved this problem with extension methods, these little helper utils at least get grouped by type and not in one humongous file. Or maybe that was part of the design team's intention behind them all along.
And because they're static you can easily see when services or state are getting passed into a method, clearly showing when it should in fact be some sort of service or a new type.
pjmlp · 17h ago
Same thing, a typical OS has tons of microservices talking over OS IPC.
Sun RPC was microservices.
But understanding they are several decades old concept isn't cool, doesn't sell conference tickets, books and consulting training.
rendall · 16h ago
Unfortunately, Fielding's dissertation itself alone gives inadequate guidance to construct or even fully identify a REST API, and so a over the subsequent years a mishmash of sometimes contradictory blog posts, discussion forums, militancy, angry flame wars, and books fleshed out the REST.
The 2007 book RESTful Web Services was wildly influential in popularizing the standard by clarifying and presenting a set of further guidelines or constraints that it called Resource-Oriented Architecture.
LegionMammal978 · 9h ago
As far as I can tell, he's not even describing an "API" as we'd normally use the term today. For all practical purposes, it's pretty much just "using an HTTP server and hyperlinks to let (human) users discover and interact with resources". (Or replace "HTTP" with another protocol like it in all but name.) If it has any structure past that, then it's not REST in his sense.
Personally, I think "API" is an unclear term for that kind of structure. The only actual interfacing is the HTTP protocol between the server and web browser. But the browser traditionally only acts as a proxy for the user, who is the one being served access to the resources.
whstl · 8h ago
> As far as I can tell, he's not even describing an "API" as we'd normally use the term today. For all practical purposes, it's pretty much just "using an HTTP server and hyperlinks to let (human) users discover and interact with resources".
Bingo.
Like Monads, it mostly later interpretations missing the mark.
maleldil · 6h ago
How does this relate to monads? They have a proper definition of the signatures required and the laws they must obey.
chrchr · 12h ago
The reason to use a factory instead of 'new' is that a factory can vary the return type, unlike a plain constructor. You need a factory when, based on the constructor parameters or the system configuration, different classes of object may be instantiated. I really have to disagree with your characterization of the GoF book. The premise is that it's a set of designs that can be applied when specific situations are encountered, though, granted, if you're reading the book before you've actually seen the situation where a particular pattern can be applied, it seems abstract. Certainly, popular conceptions of patterns taken out of context make that problem worse.
1718627440 · 7h ago
> a factory can vary the return type, unlike a plain constructor
That's a language limitation and has nothing to do with the design pattern. You can do this just fine e.g. in Python.
idontwantthis · 6h ago
You could do that more or less in Javascript by creating and initializing different member variables in the constructor depending on a constructor argument but I would hate you for it.
scrame · 9h ago
yeah, they say in the introduction even that it is to give a language to common patterns that emerge in C++ style OOP. It gives examples of what it might look like, but isn't an instruction manual on what to do. Its to give a common language "this is a gateway" as opposed to "this is where stuff goes in", or "front-door" or whatever tribal names / descriptions people come up with.
RossBencina · 19h ago
> Design patterns suffer because in most explanations the context is completely missing.
"most explanations"? Most crappy explanations on the web and in introductory courses perhaps.
The original GoF Design Patterns book and all of the Pattern Languages of Program Design books that followed it define and adhere to a pattern (form, template) for how to document design patterns. The main elements of this form are (GoF, p.3):
1. The *pattern name* ...
2. The *problem* describes when to apply the pattern. It explains the problem and its context [emphasis mine]. ...
3. The *solution* ...
4. The *consequences* are the results and trade-offs of applying the pattern. ...
I am guessing this form comes from Christopher Alexander, but I don't have a copy of A Timeless Way of Building at hand.
galaxyLogic · 17h ago
Don't overlook the "Forces". While "Problem" explains WHY you might need the specific pattern in question, the "forces" explain why some trivial solutions are not very good solutions, because you have to take into account the constraints, the assumptions about the context, why the problem is actually a problem.
MVC is a great, even proverbial pattern, but I don't recall having seen it presentewd in the "Patterns Format" anywhere.
Such a presentation would make it easier to understand no doubt.
Kerrick · 9h ago
It is presented in the patterns format in at least these places:
- Pages 125–143 of Pattern-Oriented Software Architecture (1996) by Buschmann et al. ISBN 978-0471958697.
- Pages 330–332 of Patterns of Enterprise Application Architecture (2003) by Martin Fowler. ISBN 978-0321127426.
bobajeff · 10h ago
>since Alan Kay invented the term, Smalltalk is weaponised against C++/Java-style OOP.
Often I see actual common practices of "OOP" being used as arguments against it. Which are then dismissed as 'not true OOP' by it's proponents.
Only recently did I see someone give a presentation talking about not just the historical meaning of the term and it's origins but also the common practices that are associated with it and detailed some issues with it. (I'm guessing because he was tired of hearing the same defenses over and over again.)
1718627440 · 7h ago
Care to provide the link for to that presentation?
bobajeff · 5h ago
Casey Muratori – The Big OOPs: Anatomy of a Thirty-five-year Mistake – BSC 2025
It’s the same kind of problem, where naming signals something the implementation is not.
(Am I allowed to link my own blog btw?)
whstl · 8h ago
> The problem is when implementations aren’t actually monads at all
Exactly, this was my point, it wasn't clear.
The original definition, and Haskell's implementation are good in itself. Monads in Haskell are not that difficult or too abstract.
It was Monad tutorials and partial implementations missed the mark, like in your example.
Myself, similarly, I've seen way too many Option<T> implementations in Typescript that are less safe than if (value !== null) {}, because they replace a static check with an exception in runtime.
lock1 · 14h ago
Unrelated with GP post: What's wrong with Java's Optional?
IIRC it doesn't fulfill monad axiom, but I don't think there's a huge problem with it. By the time you're using Option<>-like, I don't think you should use bare `null` at all in your project. Mixing Option<>-like and bare `null` sounds like playing with fire.
Also, if you're using Java 17+ (`record` in your example), you're probably better off writing your own Option<T> to support sum-type matching & product-type destructuring.
kriops · 12h ago
Optional::map returns an empty optional if the passed function returns null. This is incorrect and can be especially hurtful in intermediate operations. Allowing Optional::map to return an Optional<void> would have been correct.
Alternatively, just don't call it 'map'.
I agree implementing your Option<T> type is better. The problem is that people will use whatever is available in the standard library—I am not working in isolation.
ekidd · 13h ago
Yes, monads are abstract, but the definition is also very precise. Specifically (using C++/Rust notation for parameterized types), if we have a type "M<T>", we also need:
...where the mapping creates an "extra" layer of M<...>, and then we flatten it away immediately.
(There are other rules than ones I listed above, but they tend to be easy to meet.)
Once you have flatMap, you can share one syntax for promises/futures, Rust-style Return and "?", the equivalent for "Option", and a few dozen other patterns.
Unfortunately, to really make this sing, you need to be able to write a type definition which includes all possible "M" types. Which Rust can't do. And it also really helps to be able to pick which version of a function to call based on the expected return type. Which Rust actually can do, but a lot of other low- and mid-level languages can't.
So monads have a very precise definition, and they appear in incomplete forms all over the place in modern languages (especially async/await). But it's hard to write the general version outside of languages like Haskell.
The main reason to know about monads in other languages is that if your design is about 90% of the way to being a monad, you should probably consider including the last 10% as well. JavaScript promises are almost monads, but they have a lot of weird edge cases that would go away if they included the last 10%. Of course, that might not always be possible (like in many Rust examples). But if you fall just barely short of real monads, you should at least know why you do.
(For example, Rust: "We can't have real monads because our trait system can't quite express higher-order types, and because ownership semantics mean our function types are frankly a mess.")
skydhash · 21h ago
> *Design patterns suffer because in most explanations the context is completely missing. Patterns are totally useless outside very specific contexts.
It's hard to define how a pattern can be useful, because they're patterns, not recipes or snippets. They're supposed to fit in the reader's solution, not the author's examples. You're supposed to have the problem before reaching out for a solution and patterns are not solutions, they're models of solution, each with their own tradeoffs, costs and advantages.
procaryote · 19h ago
This is why reading a book on design patterns is more than most junior devs can handle without then spending years trying to shove extra complexity into everything to make it use said patterns
Every `new` can be a factory or builder, but usually `new` is the right thing
jimmaswell · 9h ago
I'd wager reading that book and spending years trying to shove the patterns into things is a net benefit iff you learn from it where and why the patterns were appropriate or not. I've never read one of these books, but a good one should give examples of when a pattern is counterproductive.
In a greater sense, in our profession we tend to learn about new hammers by forcing them into the next (work or personal) project that vaguely resembles a nail, and I think that's largely OK if the alternative is stagnating.
heisenbit · 10h ago
Point in case: A singleton holding a factory to create a single instance of a fairly simple class.
procaryote · 10h ago
I've seen so many singletons that just exist because java lacks conditional compilation, and you need either a test-something or a real-something.
Of course I've also seen heaps of singletons that exist for no reason and could just have been a static class, sometimes because of cargo cult, sometimes because "what if we want to make it configurable later?"
Such a waste of energy
asimpletune · 19h ago
I think OOP suffers in particular because patterns aren’t a mathematically defined thing like monads etc. They’re just a convention and I think that permits misunderstanding and dogmatism. It’s fine though.
RossBencina · 19h ago
Plenty of confusion around monads, at least that was the case last time I checked.
Design patterns are not "just a convention" they are practical solutions to often encountered problems. They are a way of extracting commonly applied, useful solutions and documenting them for reuse. If you go and read a properly documented design pattern I think it's pretty hard to misunderstand what it is, what it's good for, when to apply it, and when maybe don't. But it is definitely possible to misapply them. I'm still living in the shadow of implementing Observer, and then trying to implement undo as an Observer by translating observed events into Commands and placing them on to the undo stack. Messy.
aryehof · 22h ago
> Smalltalk's OOP is nothing like what existed either before or after, but since Alan Kay invented the term.
Rubbish. In terms of OO language constructs, Smalltalk is almost entirely derived from Simula. Let’s not revise history.
whstl · 21h ago
This is an oversimplification, and part of the problem I mention.
The "big leap" in Smalltalk was the idea that everything is an object and computation is message-passing, not just classes and instances. That’s not from Simula. Simula was more like an extension of Algol with OO bolted on. Smalltalk was a whole new conceptual model, which is not as simple to explain as Simula/C++/Java-style OOP.
jibbit · 9h ago
it is less of an oversimplification than 'nothing like what existed either before or after'
whstl · 8h ago
Clipping the quote like that completely changes the meaning of what I said. I didn't say that.
But I stand by what I actually said: Smalltalk's OOP is indeed very novel, even for today, especially compared to C++/Java, but it's also very different from Simula, especially the early Smalltalk versions.
It's not without lineage (Ruby and IO) or peers (Erlang), but it's still an incredibly different flavor of OOP than Simula. This is not a slight, this is a compliment to Alan Kay. But to compare it to C++ is to miss the mark. C++ is from a different branch of OOP.
mpweiher · 20h ago
Hmm...have you looked at Smalltalk-72?
Now Alan makes it clear that the inspiration for Smalltalk OO came from Simula (and a bit from the Burroughs 220 and 5000, from Sketchpad etc.), but to say that Smalltalk is just that is a stretch at best.
The more direct line goes from Simula (Algol with classes) to C++ (C with classes).
to11mtm · 21h ago
IDK I think it's still worth considering where certain languages 'got the right things right together' to be constructive...
That said as someone fairly unfamiliar with Smalltalk I'd like an example of what other parts of Smalltalk play well with it's OOP Sauce...
travisgriggs · 20h ago
For me, one of the examples was control flow. Smalltalk had none. Or, rather, it had one: send a message to an object/receiver. It was cool that ifTrue:ifFalse: was just a message you sent to a Boolean with a closure (or two) as an argument. And various iterations as well, so you could write your own. This you can do in Swift/Kotlin/etc as well of course. Where Snalltalk went next level was that closures were objects to. And you got them to run with messages like value/value:/value:value:, etc. In most languages with anon functions/closure, you just use argument list (i.e. parens) to invoke it. But you can’t send messages to the closure itself. Smalltalk had a host of introspection methods you could send to it, different flavors of evaluation, and of course any of the ones it inherited from Object. But where it got really cool (imo), was that you could add your own methods to BlockClosure. So you could implement all kinds of control messages on anonymous functions yourself. This was cool, it was all objects, all the way down. Where “object” meant things you can add behavior to, and send messages to.
arnsholt · 17h ago
To expand a bit on why this is cool: it lets you introduce new abstractions (for example wrapping some code in a database transaction or specialised exception handling) on an equal footing with the rest of the language, all without macros.
jibbit · 16h ago
unfortunately this is just not true though. there was nothing abstract or disconnected from real life about MVC in smalltalk.
while we're here.. this claim that 'Smalltalk's OOP is nothing like what existed either before or after' - hmmmm
ionwake · 15h ago
My whole life in the UK I spent a lot of time disappointing people with my complete lack of concern regarding OOP. Like I knew the gist of it and the patterns in similar types of code but no matter what I read I could never convince anyone I possessed the correct esoteric OOP viewpoint that they were hoping to be elucidated upon.
Makes sense decades on it was all just personal abstractions
frumplestlatz · 21h ago
> with monads
There are a set of three short laws that define what a monad is. I’m not sure that really fits in with MVC, OOP, or design patterns.
It’s weird that your comment seems to be downvoted. Monads have a precise mathematical definition. Comparing them to those other broad, handwavy things is delusional ignorance.
metanonsense · 19h ago
But isn’t that exactly what GP meant? There is an original, very precise but also very abstract definition (and what is more abstract than category theory). Then people come along who give a different definition that matches the original one in their specific context („three laws in Haskell“). After that people take these three laws and apply them (sometimes overly simplistic) to other contexts („just give it a flatMap in Scala to get a monad“). And at some point the original meaning got lost, and there are competing definitions out.
frumplestlatz · 19h ago
Those three laws are the mathematical definition.
Yes, they’re encoded in Haskell, but they’re the same monad laws from category theory.
charcircuit · 17h ago
Haskell doesn't encode them. And other concepts can be given a mathematical definition if someone wanted to formalize them.
antonvs · 10h ago
> other concepts can be given a mathematical definition if someone wanted to formalize them.
There's really no comparison. Monads have a very small and simple definition. That's not true of either OOP or MVC. Formalizations of OOP do exist - they're complex and messy and mainly serve to demonstrate how poorly motivated classic OOP is.
charcircuit · 9h ago
>they're complex and messy
Ultimately they are expressed in code by either the compiler or a framework. If you just want a high level formalization I don't think it would be that complex or messy.
frumplestlatz · 16h ago
That is pedantry. As for the other concepts, formalizations of OO have been done — generally to support formal verification of OO languages.
They are incredibly complicated, comprising papers of tens to hundreds of pages, and either extremely language specific, usually only formalizing a portion of the full language, or general but too limited to specify the behavior of real world languages.
The two are not comparable.
I’m also not aware of any general formalization of MVC or how one would even begin to approach a canonical definition, much less formalization.
charcircuit · 13h ago
>I’m also not aware of any general formalization of MVC or how one would even begin to approach a canonical definition
You define it by defining Model, View, and Controller and how they interact with each other. For example take a look at the diagram from the article.
No comments yet
antonvs · 12h ago
Can you cite an example of a competing definition of monads?
antonvs · 10h ago
> three laws in Haskell
The laws are mathematical ones, that can't be expressed in the Haskell type system.
> And at some point the original meaning got lost
This is false. The original meaning is a mathematical one, and its use in Haskell conforms to that.
That meaning is not "lost", it's the only valid and rigorous definition there is. People who think the meaning is lost are simply ignorant. All they would have to do to correct that ignorance is a minimal amount of research.
whstl · 10h ago
In the parent comment I did qualify my statement, in it I am talking about monads "once it gets 'explained' and migrated to other languages", not about Monad in Haskell and definitely not about Monad laws.
frumplestlatz · 5h ago
Perhaps you can provide a concrete example.
I’ve seen Haskell’s type class hierarchy successfully realized in all sorts of languages, from Scala to Swift.
As for monads themselves, the concept emerges everywhere, with or without Haskell’s particular type classes. Case in point is Swift’s handling of Optional and Optional chaining.
RossBencina · 19h ago
Mathematical rigor is only one type of rigor. I think you can make your point without framing attempts to tame real-world complexity as "broad and handwavy," or deriding people's struggle for understanding as "delusional ignorance."
frumplestlatz · 18h ago
Placing monads in the same category as MVC, OO, or design patterns is ignorance.
Mathematical rigor is also not comparable to whatever sense of rigor might apply to those concepts.
“Delusional” might be harsh; I’d go with “confused”.
whstl · 18h ago
Uncalled for. Try to follow the guidelines instead of calling people names.
frumplestlatz · 18h ago
Speaking authoritatively about subjects with which one is unfamiliar is likely to have one’s speech recognized as ignorant and confused. This is not uncalled for, particularly when coupled with a substantive explanation as to why.
antonvs · 12h ago
What about the guidelines for downvotes?
If factual comments are downvoted without consequences, then the site becomes nothing but a place for reinforcement of social fads.
frumplestlatz · 20h ago
I see that your comment is now downvoted into the negatives.
When people are confused by something, they will often blame the target of their confusion instead of admitting to themselves that they don’t understand.
hurril · 19h ago
You both have good points. But there is monads the mathematical and programmatic concept, and there is also something a little bit handwavy in how these things are incorporated into an application architecture. The latter is what is being used on the one hand in comparison to MVC, etc, on the other.
I.e.: a monadic architecture in Haskell is good, but one in Java is going to suck. A sort of half-way point is in The Elm Architecture, which is a sort of deconstructed IO monad.
(Writing this as someone with decades of experience in writing monadic architectures.)
yakshaving_jgt · 19h ago
What does a “monadic architecture” look like? I’ve been writing web applications in Haskell for the past decade and all of them are MVC.
frumplestlatz · 19h ago
I don’t really understand what a “monadic architecture” is supposed to mean.
Haskell’s `Monad` type class is hardly the only possible encoding of a monad. They’re just a simple mathematical construction with useful properties, and — like functors and applicative functors — they emerge everywhere.
koolala · 15h ago
Quaternions are monads since they are associative?
laszlokorte · 13h ago
No, because they dont define the >>= operator (which is the one to be associative) in the first place.
No comments yet
adityaathalye · 21h ago
MVC (and other OOP patterns) work around the fact that the language does not solve the Expression Problem.
You could say, the key to getting MVC correct is understanding the Expression Problem, and designing its solution into your programming language in the first place, so you don't need MVC, but if you want to do it, it becomes actually neat and clean and modular.
This makes no sense. MVC was created to use with SmallTalk and based on Alan Kay's OOP, of which late binding is one of 3 requirements, not static types.
adityaathalye · 6h ago
Well, I tried to walk back from MVC, to the smallest possible generally composable abstraction for web app architecture, and I kept coming back to X/Y shaped tables. They're everywhere at all levels of composition; dependency injection, routing tables, joining any two modules arbitrarily, laying out source code on disk etc. etc. etc.
So I built my own web app system (not a monolithic framework) to test out the opinion. And, I'd say it's working quite well---for me, at any rate.
See also, Polylith application architecture; a far more sophisticated and generalised form of what I'm doing in my system. https://polylith.gitbook.io/polylith/
---
The term "Expression Problem" was coined in context of statically typed languages, but the formulation has nothing to do with static typing per se. Its general form is polymorphic multiple dispatch (not Objects versus Functions, but Objects and Functions).
> The Expression Problem is a new name for an old problem. The goal is
> to define a datatype by cases, where one can add new cases to the
> datatype and new functions over the datatype, without recompiling
> existing code, and while retaining static type safety (e.g., no
casts).
i.e. He was trying to bring the solution from the dynamic / interpreted language space, to the difficult case of statically typed languages.
---
Anyway, I picture it as an "X/Y" problem. Something like this:
Understanding the Expression Problem in MVC and OOP
travisgriggs · 21h ago
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
“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.
mpweiher · 19h ago
Yeah, in my experience, just MV is largely just fine.
I like the term "GluePuppy". It absolutely is a crucial part, or parts.
Basically, it defines the architecture of the system, pulls all the objects together and connects them to create a useful system.
It doesn't help that we don't have linguistic support for "glueing things together", we only have procedure calls. So that's one of the issues I am addressing with Objective-S, actual linguistic support for "glue". And yeah, please don't put the rules/distinctions over designing a clean system.
That was a thing I discovered only recently when working with two teams, one Android and one iOS: developers in general, and particularly good architects, want to build these uniformly recursively decomposed modules.
You can't do that with a good OO architecture, because you absolutely need the GluePuppy to tie things together. That one is different from the other modules. If you don't allow for a GluePuppy, but instead insist on uniform modules, you inescapably get procedural decomposition instead of OO decomposition. If you are in an OO language, that means singletons everywhere, because every module needs to know about its dependencies. And that gets you into a world of hurt when you want to do what OO is supposed to be good for, handle variation.
Embrace the GluePuppy! It loves you and wants to make your life simpler.
The "lets-use-the-call-tree-to-mirror-the-view-tree" fashion also has to do with this, IMHO, because our languages only have support for writing down procedures. So if you can get your view-tree expressed by the call-tree, you get to write it down directly in the code, rather than constructing it indirectly, with the view-tree being a hidden side effect.
That is a clear benefit, but the costs are pretty horrendous.
How about we extend our languages so that we can also write down those view hierarchies directly in the code, and at the same time using a format that can also be read/written as data (down LISPers, down)?
layer8 · 7h ago
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.
neilv · 20h ago
Controller seemed fairly straightforward to me initially, when I was first learning Smalltalk (ParcPlace), and I took my simple understanding on faith.
My programs were simple, so M was data, V was presentations of the data, C was interaction on the M and maybe V.
It only got confusing when I got more experience.
mpweiher · 18h ago
> so M was data,
M is the Model. That means the data and all the things you might ever want to do with the data. So any interaction you might want to do from the view is (ideally) a single message-send to the model.
> V was presentations of the data
And editing the data.
> C was interaction on the M and maybe V.
> It only got confusing when I got more experience.
:-)
travisgriggs · 20h ago
Where “interaction” meant user interaction.
jerf · 22h ago
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.
catlifeonmars · 22h ago
> 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.
This is a really insightful way to frame it.
kqr · 21h ago
Oooh. Now I get it. I've been dismissive of MVC for nearly as long as I've known it but I realise I've only seen the bad versions. What you describe as correct sounds much more sensible.
RossBencina · 18h ago
> 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.
Could you give an example? I've never understood how one could possibly reuse a Controller independently of a View. At a minimum any kind of mouse-based direct manipulation requires the Controller having access to the displayed geometry in order to implement hit testing. E.g. how is a Controller supposed to update the selection range in a text editor without screen-coordinate character extent information from the view, or a drawing editor Controller accessing scene graph geometry for object selection, control handle manipulation, etc.
mpweiher · 17h ago
> I've never understood how one could possibly reuse a Controller independently of a View.
And you're absolutely right!
The problem you're seeing is one of the misunderstandings/misinterpretations of MVC, that the controller is for all interactions/editing. It's not. It's perfectly fine for the View to handle this.
skydhash · 21h ago
> 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.
That's sensible. But it's generally useful to split your core state from your presentation, and then you'll find strands of logic that belongs to neither, generally glue code, but some can be useful enough to warrant a module of their own. Also your core state can be alien from the view itself (think a game data (invisible walls, sound objects) and the actual rendering).
Maybe this architecture is not MVC, but MVC can be a first stab for a quick and dirty separation. Then once a cleaner separation can be done by isolating specific modules (in layers, graph, whatever)
hurril · 19h ago
I think that a big problem here is the fact that in OOP, everything is an object, i.e.: a class. And if all you have is a hammer, then .... But it is much better to picture the model, controller and the view as emergent. But implementing this in OOP is too challenging because some things in either of those three domains are going to be a process, or a piece of state or a role, etc.
And in implementing some process, what is it? As in: what is its encoding in $language and where does it go?
So you end up with the local stamp collectors in the office and get into an argument of: it is part of the model, so should be in the Model class. "Process, nah, that is totally a controller aspect. It does something." etc.
kybernetikos · 13h ago
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.
feoren · 4h ago
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.
jmkni · 10h ago
Yeah exactly, you can come up with all sorts of abstraction layers but your ugly code has to go somewhere eventually
vbezhenar · 13h ago
Sounds like very simple task to solve.
You have Table component.
You have TableData interface. Table component asks TableData interface for data to show.
You have TableCallback interface. When user interacts with Table, like click or whatever actions are implemented, TableCallback methods are called.
When you want to use Table with your data, you implement TableData and TableCallback for your data. That's about it.
I've seen this approach implemented in most UI frameworks. You might rename TableData to TableModel or whatever, but essentially that's it.
kybernetikos · 12h ago
I'm not saying it's not simple to write, I'm saying it's ugly and contingent and you can't really avoid that. It's exactly this reason that has led to a proliferation of MV* patterns, including the one you describe.
But to try to explain myself more clearly - in the architecture you describe, who is that it is implementing TableData and TableCallback? Is it your beautiful clean business logic classes that have no coupling to their representation - in which case that is weirdly coupled in an ugly way, or is it some other class that acts as the bridge between the two worlds, in which case, that's where your ugly code is living.
ndriscoll · 8h ago
Ideally a bridge (e.g. a typeclass) with nice language support like Scala has. I'm not seeing what's so ugly. In math, you have an abstract interface for e.g. monoids (your interface like a Table). Then you have e.g. complex numbers as a set (your business model). The you can identify how C is a monoid via addition. And how it's also separately a monoid via multiplication. Same idea. There's nothing "ugly" about having to write down what you mean.
mmahemoff · 23h ago
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.
hackrmn · 5h ago
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.
mikepurvis · 22h ago
Yes to all of this with the provisos that a) there’s enough meat in terms of business logic and validation to justify the indirection of a separate object and b) you’re under a language or CI regime that can validate the boundary between the two classes for basic flubs like function misnames or bad arguments.
andrewflnr · 21h ago
> If your conception of a "model" is perversely a user-facing widget
Do people really do this? That's mind-numbing.
RossBencina · 18h ago
I don't think people called it a "model" but back in Windows VisualBasic/Delphi/C++Builder days the path of least resistance was to set up your GUI in a visual editor by laying out all your widgets in the window. Classically Qt can also be used this way. So you have this UI, you can launch the application and the UI displays and basically works, but none of the buttons do anything. But all the widgets have a great API that you can use to set permissible value ranges, set and query state, etc. And the widgets would fire events when things changed. In other words, the widget contains a model, and implements the Observer design pattern.
If you wanted to implement MVC with a separate application data model you had to do work to set up a separate model, and keep it in sync with the UI. None of this class of old tools provided any built-in assistance for defining models separate from Widgets, except for some support for binding UI to database queries/results. Of course this was separate from the Smalltalk world, where there were frameworks for building up models out of pre-defined model "atoms" such as an observable number model that you could bind to various views.
layer8 · 7h ago
What I find confusing about the Smalltalk diagram of MVC is that the view can update itself from the model without help from the controller (and without the controller even necessarily being aware of it), but updates to the model based on view events apparently have to go through the controller. This raises the question of why if the view is capable of reading the model on its own, it is not likewise capable of modifying it, and/or conversely, if the controller is used to customize how the model is updated based on view events, why it can’t also be used to customize how the view updates itself from the model.
What would make more sense to me is to simply define the controller as an intermediary between model and view for updates in both directions. The controller would simply represent whatever needs to be specific to the particular combination of model and view in the particular application context. Depending on context, you might use a no-op controller when no customization is needed, as in the example of the article where a checkbox (view) is bound to a boolean property (model).
cpburns2009 · 2h ago
This is exactly how I conceptualize and use "MVC". The controller mediates all communication between the view and model, and in fact drives everything.
lukasb · 22h ago
Any implementation of MVC I've seen the V and the C are so tightly coupled the separation seemed artificial. Skill issue?
andrewflnr · 21h ago
Yeah, it's really hard to tease them apart in a GUI sort of environment, since the input is so tightly tied to the graphical view. Model and View have always seemed pretty obvious to me but I've never gotten a compelling answer as to what a controller is.
My best guess from this article, given then "associated by observer" link from View to Controller, is that the View is supposed to pass events to the Controller, which will interpret them into changes to the Model. But what's the format of these events that's both meaningfully separate from the View, e.g. could be emitted from different views to maybe different controllers, but doesn't just force the View to do most of the work we want the Controller to do?
skydhash · 21h ago
Controller is where your logic is. Your model is your state, and the view is presentation. Both are static. The controller is the dynamic aspect that update the view to match the state and update the state according to interaction or some other system events.
Splitting the logic from the state and presentation make the code very testable. You can either have the state as input and you test your presentation, or have the presentation as input (interaction and lifecycle events) and test your state (or its reflection in the presentation).
Also this decoupling makes everything more flexible. You can switch your presentation layer or alter the mechanism for state storage and retrieval (cache, sync) without touching your logic.
mpweiher · 18h ago
> Controller is where your logic is.
That's actually precisely the anti-pattern. Massive View Controller is an example of this.
The Model is where your logic is. Everything that is in any way semantically relevant.
Views handled display and editing (yes, also editing!). Controllers ... well ... I guess you might have a need for them.
sunir · 11h ago
The model is the source of truth. For almost all apps, it should always be valid. (For those apps that isn’t true such as massively distributed systems, it should present a projection of what can be considered valid to your locality and handle delta internally.)
There has to be a boundary that controls changes to the model. The confusion with MVC is where is the best place for this boundary. Well more than one place as it turns out because there are at least two models of reality trying to converge. The model itself and the view of the model (and the user’s mind).
The view’s job is to present a projection of the model and then collect change events to the model. Thus could be a UX or an API. Other events can also change the model like say sensor data.
The controller decides what view to show and retrieves model data to project and translates change events coming from the external world (views or events) into changes the model should interpret. This includes gatekeeping such as auth, and error handling.
That’s a lot for one class so it can get confusing very quickly. Why localize it in one place?
So viewtrollers come around where the controller is in the view class but in the onhandle methods. This also makes sense since each view has a mini controller to handle all the jiggling bits.
This works well when there are no orthogonal injections like auth or eventing. When those are added it makes sense ins viewtroller to extend the model with controller functionality to for eg control authorization or have a thin event receiver to fsm in the model.
This all works but three years later it’s hard to figure out when I read the code again. So I have learnt to treat the model as pure data as much as possible and the view as much about rendering as possible. Views can have little controllers for handling the jiggling. What the controller cares about is when a change to the system needs to happen.
Then I can put the system control fsm in one place. I can put all event handling in the same fsm to avoid race conditions.
The goal is to make it easier to reason about.
What I don’t want are multiple threads of fsms in conflict with each other.
mpweiher · 8h ago
> The model itself and the view of the model (and the user’s mind).
Trygve was very explicit about the model being a model of how the user thinks about the problem:
There should be a one-to-one correspondence between the model and its parts on the one hand, and the represented world as perceived by the owner of the model on the other hand.
> There has to be a boundary that controls changes to the model.
Yes. It's called the API of the model. The model makes available API for all semantically valid operations. As I wrote elsewhere, I usually have a facade that acts as the top-level API for the model.
The views can call this API. So can other entities.
> The controller ... decides what view to show
possibly. But views can handle their own subview.
> The controller ... and retrieves model data to project
Nope. Not the job of the controller.
> The controller ... and translates change events coming from the external world (views or events) into changes the model should interpret.
Nope, it doesn't. The views typically do that as well. Controllers may be get involved if there is a complex sequence of steps that doesn't really naturally fit into a view.
> That’s a lot for one class so it can get confusing very quickly.
Yeah, if you put all sorts of stuff in the controller that doesn't belong there.
> Why localize it in one place?
Indeed. Don't incorrectly localize all these things in one place. MVC, for example, tells you not to do this.
> This all works but three years later it’s hard to figure out when I read the code again.
Yes. MVC is much better than that thing you came up with.
js8 · 19h ago
I always thought that business logic, expressed in the language of your domain, should be part of the model. The controller is just a translator from the language of keystrokes and mouseclicks into the domain language, and the view is just a translator from the domain language into pieces of text and widgets to display to the user.
dsego · 19h ago
In the server-side web world the controller should ideally only receive http actions and call services or fat models. It should have no business logic, only validation and parsing. In the frontend UI world the controller is bound to UI events and communicates those from the view to the model objects. (1).
Though it's probably easiest understood in a non-web world.
The web makes it quite a bit more involved with a separation of client- and server-side state - plus you have a given frontend "framework" in the shape of DOM, which people often leave out of the picture.
This latter necessities the 'escape hatches' in React and alia.
to11mtm · 21h ago
At least in my head, the 'controller' is what can either take 0 or more parameters or input models as 'input' and the controller can either provide direction to the browser as to what to do next.
e.x. in a 'proper' ASP.NET MVC 4 project I 'inherited', the View took input data in and with a tiny bit of JS magic/razor fuckery around the query page etc, but overall the controllers would return the right hints for the Razor/JS 'view' to move the application flow along or otherwise do a proper reload.
grugagag · 24m ago
In ASP.NET MVC is a modified version of classical MVC adapted for the web.
The Controller in ASP.NET MVC takes on the role of both the classic Controller and part of the classic Model's role (orchestrating the retrieval/updating of data). The connection between the View and the Model is completely severed and mediated by the Controller.
mjevans · 21h ago
Agreed, but maybe my mental model is splitting the 'Controller' aspect into the client / server model. Everything must get validated server side, there's no other rational choice. Otherwise you cannot enforce any consistency or business logic.
That just leaves formatting the requested changes into a language the server model accepts.
Maybe model is more 'database', controller is API interface (server side + client request requirements), and view is end user render?
bitwize · 13h ago
Early Microsoft frameworks (the old Windows 3.x and 9x era MFC) suggested using a "document-view" model wherein the model was a "document" class serviced by a "view" class which handled presentation and UI, serving as both view and controller. There were wizards that would spit out skeletons for these classes and everything.
pixelworm · 23h ago
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.
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
165 points by adityaathalye 3 months ago | 39 comments
gundmc · 23h ago
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
frollogaston · 12h ago
Last time I cared about MVC was AP Computer Science. Model is fine, but there's no reason for view vs controller. The UIView vs UIViewController thing in ObjC/Swift was one example of the silliness, something for devs to waste time arguing over. React (Native) refreshingly had no such thing. Angular was all about MVC, but they recently slimmed it down.
Also, a while back it was way less common for UIs to have backend services. Nowadays those have taken away most of the "model" side in a lot of apps.
izackp · 11h ago
Hi, I've been making iOS apps for over 10 years now. I've experimented with many different styles, and even started doing android and web development. One thing I learned is that every abstraction or indirection makes things slightly harder to read and debug. Observers seem to have been a solution to 'callback hell' before async was a thing. However, it's rife with pitfalls.
With Observers:
We have hidden control-flow and lost intent. They subvert the readability of the developer's intention, in some cases they make you lose the callstack, and it has you searching the project on what code modifies a variable which is a lot harder than searching for a function call. Don't get me started on dealing with handling errors and out of order events. And oh man, is it easy to just avoid using encapsulation and creating a good interface/api for your piece of code.
Most of your code isn't re-usuable as you think:
A lot of things are naturally and forever tied together. Your UI is a reflection of _some_ model, The actions it can perform is based on it's current context, and if your UI changes then your business logic and model probably changes as well. This die hard need of separation and modularity only increases the complexity of the code with the majority of times the code not even being reused.
The only case that I've found somewhat reasonable to use observers is the database. What caused the database to change and effect it has is already pretty far removed from each other when a piece of UI needs to reflect the database.
Granted, It's possible to work around some of these issues, but please please I'm tired of debugging why a menu only opens 50% of the time because there is a piece of code several classes away from the context that doesn't fire correctly and looks like if (child.preferred.child.model.somethingElse.isFinished) { child.menu.child.openMenu = true }
zkmon · 20h ago
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.
anon7725 · 18h ago
MVC came from desktop applications. It was later repurposed to client-server apps when the web arrived, but it was always an impedance mismatch.
In desktop GUI apps, the delineation is much crisper: the model is the data that the application manages (the CAD geometry, the document, etc). The view is one or more renderings of the data to screen, and the controller is the input and command processor that updates both the model and the view.
Storage is not central to this architecture - it exists, of course, but it’s not really described as part of these core relationships.
dragonwriter · 10h ago
That’s a nice story, but completely ahistorical; MVC was not originally a distributed architecture but an architecture for local desktop GUI apps. While it was later applied to client/server apps, that isn’t its origin (and even in that context it was never equivalent to the 3-tier model, which you can easily note by all three of M, V, and C being represented by backend components in web frameworks like Rails—the View being the backend component that renders what is sent to the frontend in that version.)
mjevans · 19h ago
It makes so much more sense in that light.
It was __sold__ not to programmers, but to NON programmers.
The model is the underlying shape (the data in storage).
The Controller is like the security guard for the warehouse / building.
The View is what's presented to external clients (end users).
cheschire · 16h ago
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.
frollogaston · 12h ago
It's trust and also org chart. One team will make one service, two teams will probably make two.
ednite · 11h ago
I’m still an MVC fan, thanks in no small part to Scott Allen’s teaching (RIP).
I agree that if controllers stay tiny and boring, your model stays rich and your app stays portable, testable, and easier to evolve.
If you’re learning MVC, Scott’s OdeToCode/Pluralsight material still nails the fundamentals and the why behind them.
cheschire · 1h ago
The man that taught me C#. I had no idea he passed. Thanks for sharing this!
sureglymop · 18h ago
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.
PaulHoule · 5h ago
On the web there is a lot of confusion about "MVC" in that the heart of it is not the model, it is rather having the controller (router) which is able to look at the URL, form parameters and such and decide which view to show you.
This is in contrast to the 1990s model of web programming where you wrote an HTML page with a <form>, pointed the action to some URL, and that URL was a cgi-script that couldn't redraw the form so error handling was difficult.
In a lot of cases you could say the data fetching is a dependency of the view and not the other way around, for instance if it is a blog post you might have a model object for the actual blog post but then want to put arbitrary widgets into the view which in turn requires fetching whatever model objects are necessary to draw the widgets. From the viewpoint of a CMS user, for instance, they want to drop the widget into the template and have it "just work" (have the framework figure out the fetching.)
The first exposure a lot of people had to this paradigm was Ruby-on-Rails and since it had a rich model system people thought the model system was the important bit but I'd say the router is the most important bit and how you fetch the data and format it is secondary, in fact it's totally fair to use different fetching and templating paradigms for different pages that live under the same router.
avodonosov · 20h ago
Everyone knows what model is. Almost noone knows what is controller.
samtheprogram · 21h ago
> 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?
b_e_n_t_o_n · 22h ago
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.
js8 · 19h ago
That's what I call "single vortex principle". The graph of data dependencies (flow of updates) in your application should ideally have only one circle (which includes the user). The minimal circles of data dependencies are what I call "vortices". Every time you have two different vortices in your application, a potential ambiguity arises about sequencing the updates. These are difficult to deal with and can cause bugs.
As you say, in MVC, the vortex should be User -> Controller -> Model -> View -> User. Best if this is the only vortex (Flux pattern). This can be nicely expressed functionally. That's why I think beans (and mutable variables in general) are bad because each has it's own small vortex of updates.
ChrisMarshallNY · 16h ago
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.
aryehof · 22h ago
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.
hansvm · 21h ago
And what do you call the Model object holding those collaborating objects? Is that not still one object? The article explicitly supports your position that models can be complicated (see the paragraph starting with "to support more complex views").
mpweiher · 18h ago
I actually find it useful to have the Model represented by a single object, usually a facade that coordinates all the other Model objects.
Not sure why this would lead to anemic models, which I completely agree are a common anti-pattern.
In fact, to me it seems rather the opposite would be true: having the single object facade facilitates having a complex model that coordinates all the different pieces to represent a unified view of said model to the views, which can then be very simple and transparent.
In turn, when the models were coupled with views individually, that has tended to lead to exactly that View → DB Table mapping of dumb data objects you rightly criticize.
globular-toast · 12h ago
"Model" is an overloaded term. In Domain-Driven Design there is the domain model at the centre of the application. One model consisting of many classes (entities and value objects), functions etc.
Then there's the ORM thing, particularly active record ORMs, where "a model" means a database table. And things like serialisation libraries (e.g. Pydantic) where "a model" is one type.
Something that changed how I thought of it was in Robert Martin's Clean Code where the says the whole MVC lives in the outer layers of the application. So basically, "model" is context specific. It depends what part of your application you're talking about. MVC is about building GUIs, that's it. An application usually consists of a lot more.
frollogaston · 12h ago
ORM is a good example of why OOP doesn't really work
mpweiher · 20h ago
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...)
A hundred people with their own clearly self-evident truth as to whether models are thin or fat.
Absolute burning certainty as to where validation logic lives.
And maybe 3 or even as many as 4 mad hermits who claim to understand what a Controller is.
rubymamis · 20h ago
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.
ahartmetz · 16h ago
In Qt, have found that it's often best (much less and simpler code) to have simple editing actions right in the model, which has the data available anyway. The distinction between model and controller buys you nothing but a lot of extra code and complication in such cases.
As someone mentioned in another thread, a separate controller starts to make sense if there are several views or views + other parts that somehow access (some of) the same data.
Qt model-view initially didn't even involve QML by the way, and QTreeView etc still exist for the classic desktop look and feature set. I don't use them much anymore neither, but that's because I mostly do embedded stuff. I wouldn't want my e-mail client or text editor to be written in QML though.
jpalomaki · 21h ago
It would be interesting to read a case study of how the MVC was applied in some larger SmallTalk app.
mpweiher · 18h ago
My BlackBird reference architecture was inspired by a bunch of real world apps in Objective-C, which isn't Smalltalk but close enough for these purposes.
after 15 years refactoring poorly designed iOS apps i can guarantee you 100% of the problems come from an insufficiently designed model layer.
Apple is to blame, as they give absolutely O clue on that part (only demo apps with structures that don’t scale)
twelvedogs · 13h ago
MVC always seemed fucking pointless to me, like shit code isn't gonna get good if you put it in boxes
mkoubaa · 22h ago
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.
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).
Like everyone totally forgot patterns are mainly for understanding existing systems like you use a framework - hey this looks like a factory let’s use one from the framework we built stuff with instead of implementing our own.
Besides of course every developer wanting to build framework so others adhere to what he built not the other way around ;)
I don't find them very useful (today) to understand existing systems that don't intentionally use the patterns. They don't occur very often in well-designed systems in the first place, even less so unintentionally.
So they will be present in well designed systems just that they are not called by their „book name”.
Then I clearly see it in all new frameworks just that each framework has their own name for implementation of the pattern.
Patterns were mostly named so people can discuss easier about solutions that are there.
I will quote first sentence of foreword from my copy of the book „All well-structured object oriented architectures are full of patterns.”
That’s the one I write about, it has foreword from Grady Booch and was tied to OOPSLA meeting with C++ and Smalltalk examples. To add OOP stuff to it sounds like a different book because that book is about OOP by definition.
of course at the time they really thought nobody would question if oo was the best way to program so it didn't seem usefully misleading. Patterns apply to non oo programning as well
This is what happened with REST too, and it frustrates me more than it probably should.
The original pattern is such a good idea and not even remotely abstract. It's a well defined architectural pattern for a well defined problem yet people still managed to bastardize it to the point that the term REST barely means anything today
The original pattern is extremely abstract and a bad idea. There has been precisely one successful implementation of the original REST "pattern", the web, and only because the pattern was retrofitted onto it; most of the things in REST-as-originally-defined are bad ideas, as any apples-to-apples comparison will show.
I get oppositely frustrated because "REST" was adopted as a rallying cry for one or two good ideas (fitting your protocol to the GET/POST and 2xx/4xx/5xx distinctions from HTTP instead of treating it as a completely opaque transport layer; not wrapping everything in oodles of XML) and the term brought along a lot of bad ideas as baggage. But the meaning of the term shifted towards doing the things that are good because the original meaning was bad.
In most discussions REST has come to mean “cute URLs” thanks to Rails.
I once got a new hire from Uber and for months on end his complaint was that “the services are too big”.
C# accidentally solved this problem with extension methods, these little helper utils at least get grouped by type and not in one humongous file. Or maybe that was part of the design team's intention behind them all along.
And because they're static you can easily see when services or state are getting passed into a method, clearly showing when it should in fact be some sort of service or a new type.
Sun RPC was microservices.
But understanding they are several decades old concept isn't cool, doesn't sell conference tickets, books and consulting training.
The 2007 book RESTful Web Services was wildly influential in popularizing the standard by clarifying and presenting a set of further guidelines or constraints that it called Resource-Oriented Architecture.
Personally, I think "API" is an unclear term for that kind of structure. The only actual interfacing is the HTTP protocol between the server and web browser. But the browser traditionally only acts as a proxy for the user, who is the one being served access to the resources.
Bingo.
Like Monads, it mostly later interpretations missing the mark.
That's a language limitation and has nothing to do with the design pattern. You can do this just fine e.g. in Python.
"most explanations"? Most crappy explanations on the web and in introductory courses perhaps.
The original GoF Design Patterns book and all of the Pattern Languages of Program Design books that followed it define and adhere to a pattern (form, template) for how to document design patterns. The main elements of this form are (GoF, p.3):
1. The *pattern name* ...
2. The *problem* describes when to apply the pattern. It explains the problem and its context [emphasis mine]. ...
3. The *solution* ...
4. The *consequences* are the results and trade-offs of applying the pattern. ...
I am guessing this form comes from Christopher Alexander, but I don't have a copy of A Timeless Way of Building at hand.
MVC is a great, even proverbial pattern, but I don't recall having seen it presentewd in the "Patterns Format" anywhere. Such a presentation would make it easier to understand no doubt.
- Pages 125–143 of Pattern-Oriented Software Architecture (1996) by Buschmann et al. ISBN 978-0471958697.
- Pages 330–332 of Patterns of Enterprise Application Architecture (2003) by Martin Fowler. ISBN 978-0321127426.
Often I see actual common practices of "OOP" being used as arguments against it. Which are then dismissed as 'not true OOP' by it's proponents.
Only recently did I see someone give a presentation talking about not just the historical meaning of the term and it's origins but also the common practices that are associated with it and detailed some issues with it. (I'm guessing because he was tired of hearing the same defenses over and over again.)
https://www.youtube.com/watch?v=wo84LFzx5nI
The problem is when implementations aren’t actually monads at all. The same goes for other functional concepts. I wrote a blog about Java’s Optional::map here: https://kristofferopsahl.com/javas-optional-has-a-problem/
It’s the same kind of problem, where naming signals something the implementation is not.
(Am I allowed to link my own blog btw?)
Exactly, this was my point, it wasn't clear.
The original definition, and Haskell's implementation are good in itself. Monads in Haskell are not that difficult or too abstract.
It was Monad tutorials and partial implementations missed the mark, like in your example.
Myself, similarly, I've seen way too many Option<T> implementations in Typescript that are less safe than if (value !== null) {}, because they replace a static check with an exception in runtime.
IIRC it doesn't fulfill monad axiom, but I don't think there's a huge problem with it. By the time you're using Option<>-like, I don't think you should use bare `null` at all in your project. Mixing Option<>-like and bare `null` sounds like playing with fire.
Also, if you're using Java 17+ (`record` in your example), you're probably better off writing your own Option<T> to support sum-type matching & product-type destructuring.
Alternatively, just don't call it 'map'.
I agree implementing your Option<T> type is better. The problem is that people will use whatever is available in the standard library—I am not working in isolation.
(There are other rules than ones I listed above, but they tend to be easy to meet.)
Once you have flatMap, you can share one syntax for promises/futures, Rust-style Return and "?", the equivalent for "Option", and a few dozen other patterns.
Unfortunately, to really make this sing, you need to be able to write a type definition which includes all possible "M" types. Which Rust can't do. And it also really helps to be able to pick which version of a function to call based on the expected return type. Which Rust actually can do, but a lot of other low- and mid-level languages can't.
So monads have a very precise definition, and they appear in incomplete forms all over the place in modern languages (especially async/await). But it's hard to write the general version outside of languages like Haskell.
The main reason to know about monads in other languages is that if your design is about 90% of the way to being a monad, you should probably consider including the last 10% as well. JavaScript promises are almost monads, but they have a lot of weird edge cases that would go away if they included the last 10%. Of course, that might not always be possible (like in many Rust examples). But if you fall just barely short of real monads, you should at least know why you do.
(For example, Rust: "We can't have real monads because our trait system can't quite express higher-order types, and because ownership semantics mean our function types are frankly a mess.")
It's hard to define how a pattern can be useful, because they're patterns, not recipes or snippets. They're supposed to fit in the reader's solution, not the author's examples. You're supposed to have the problem before reaching out for a solution and patterns are not solutions, they're models of solution, each with their own tradeoffs, costs and advantages.
Every `new` can be a factory or builder, but usually `new` is the right thing
In a greater sense, in our profession we tend to learn about new hammers by forcing them into the next (work or personal) project that vaguely resembles a nail, and I think that's largely OK if the alternative is stagnating.
Of course I've also seen heaps of singletons that exist for no reason and could just have been a static class, sometimes because of cargo cult, sometimes because "what if we want to make it configurable later?"
Such a waste of energy
Design patterns are not "just a convention" they are practical solutions to often encountered problems. They are a way of extracting commonly applied, useful solutions and documenting them for reuse. If you go and read a properly documented design pattern I think it's pretty hard to misunderstand what it is, what it's good for, when to apply it, and when maybe don't. But it is definitely possible to misapply them. I'm still living in the shadow of implementing Observer, and then trying to implement undo as an Observer by translating observed events into Commands and placing them on to the undo stack. Messy.
Rubbish. In terms of OO language constructs, Smalltalk is almost entirely derived from Simula. Let’s not revise history.
The "big leap" in Smalltalk was the idea that everything is an object and computation is message-passing, not just classes and instances. That’s not from Simula. Simula was more like an extension of Algol with OO bolted on. Smalltalk was a whole new conceptual model, which is not as simple to explain as Simula/C++/Java-style OOP.
But I stand by what I actually said: Smalltalk's OOP is indeed very novel, even for today, especially compared to C++/Java, but it's also very different from Simula, especially the early Smalltalk versions.
It's not without lineage (Ruby and IO) or peers (Erlang), but it's still an incredibly different flavor of OOP than Simula. This is not a slight, this is a compliment to Alan Kay. But to compare it to C++ is to miss the mark. C++ is from a different branch of OOP.
Now Alan makes it clear that the inspiration for Smalltalk OO came from Simula (and a bit from the Burroughs 220 and 5000, from Sketchpad etc.), but to say that Smalltalk is just that is a stretch at best.
The more direct line goes from Simula (Algol with classes) to C++ (C with classes).
That said as someone fairly unfamiliar with Smalltalk I'd like an example of what other parts of Smalltalk play well with it's OOP Sauce...
Makes sense decades on it was all just personal abstractions
There are a set of three short laws that define what a monad is. I’m not sure that really fits in with MVC, OOP, or design patterns.
https://wiki.haskell.org/index.php?title=Monad_laws
Yes, they’re encoded in Haskell, but they’re the same monad laws from category theory.
There's really no comparison. Monads have a very small and simple definition. That's not true of either OOP or MVC. Formalizations of OOP do exist - they're complex and messy and mainly serve to demonstrate how poorly motivated classic OOP is.
Ultimately they are expressed in code by either the compiler or a framework. If you just want a high level formalization I don't think it would be that complex or messy.
They are incredibly complicated, comprising papers of tens to hundreds of pages, and either extremely language specific, usually only formalizing a portion of the full language, or general but too limited to specify the behavior of real world languages.
The two are not comparable.
I’m also not aware of any general formalization of MVC or how one would even begin to approach a canonical definition, much less formalization.
You define it by defining Model, View, and Controller and how they interact with each other. For example take a look at the diagram from the article.
No comments yet
The laws are mathematical ones, that can't be expressed in the Haskell type system.
> And at some point the original meaning got lost
This is false. The original meaning is a mathematical one, and its use in Haskell conforms to that.
That meaning is not "lost", it's the only valid and rigorous definition there is. People who think the meaning is lost are simply ignorant. All they would have to do to correct that ignorance is a minimal amount of research.
I’ve seen Haskell’s type class hierarchy successfully realized in all sorts of languages, from Scala to Swift.
- https://github.com/scalaz/scalaz
- https://bow-swift.io/
As for monads themselves, the concept emerges everywhere, with or without Haskell’s particular type classes. Case in point is Swift’s handling of Optional and Optional chaining.
Mathematical rigor is also not comparable to whatever sense of rigor might apply to those concepts.
“Delusional” might be harsh; I’d go with “confused”.
If factual comments are downvoted without consequences, then the site becomes nothing but a place for reinforcement of social fads.
When people are confused by something, they will often blame the target of their confusion instead of admitting to themselves that they don’t understand.
I.e.: a monadic architecture in Haskell is good, but one in Java is going to suck. A sort of half-way point is in The Elm Architecture, which is a sort of deconstructed IO monad.
(Writing this as someone with decades of experience in writing monadic architectures.)
Haskell’s `Monad` type class is hardly the only possible encoding of a monad. They’re just a simple mathematical construction with useful properties, and — like functors and applicative functors — they emerge everywhere.
No comments yet
You could say, the key to getting MVC correct is understanding the Expression Problem, and designing its solution into your programming language in the first place, so you don't need MVC, but if you want to do it, it becomes actually neat and clean and modular.
So I built my own web app system (not a monolithic framework) to test out the opinion. And, I'd say it's working quite well---for me, at any rate.
Have a gander: https://github.com/adityaathalye/clojure-multiproject-exampl...
See also, Polylith application architecture; a far more sophisticated and generalised form of what I'm doing in my system. https://polylith.gitbook.io/polylith/
---
The term "Expression Problem" was coined in context of statically typed languages, but the formulation has nothing to do with static typing per se. Its general form is polymorphic multiple dispatch (not Objects versus Functions, but Objects and Functions).
See: Philip Wadler's explanation (where he coins the term): https://homepages.inf.ed.ac.uk/wadler/papers/expression/expr...
casts).i.e. He was trying to bring the solution from the dynamic / interpreted language space, to the difficult case of statically typed languages.
---
Anyway, I picture it as an "X/Y" problem. Something like this:
https://www.evalapply.org/posts/clojure-web-app-from-scratch...
7.2. Solve The Expression Problem
Playtime:
- What if we frame everything in terms of the Expression Problem?
- Add a new Y, extend all Xs to it? Without cooperation of existing Xs?
- Add new X, extend all Ys to it? Without cooperation of existing Ys?
(edit: formatting)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.
I like the term "GluePuppy". It absolutely is a crucial part, or parts.
Basically, it defines the architecture of the system, pulls all the objects together and connects them to create a useful system.
It doesn't help that we don't have linguistic support for "glueing things together", we only have procedure calls. So that's one of the issues I am addressing with Objective-S, actual linguistic support for "glue". And yeah, please don't put the rules/distinctions over designing a clean system.
That was a thing I discovered only recently when working with two teams, one Android and one iOS: developers in general, and particularly good architects, want to build these uniformly recursively decomposed modules.
You can't do that with a good OO architecture, because you absolutely need the GluePuppy to tie things together. That one is different from the other modules. If you don't allow for a GluePuppy, but instead insist on uniform modules, you inescapably get procedural decomposition instead of OO decomposition. If you are in an OO language, that means singletons everywhere, because every module needs to know about its dependencies. And that gets you into a world of hurt when you want to do what OO is supposed to be good for, handle variation.
Embrace the GluePuppy! It loves you and wants to make your life simpler.
The "lets-use-the-call-tree-to-mirror-the-view-tree" fashion also has to do with this, IMHO, because our languages only have support for writing down procedures. So if you can get your view-tree expressed by the call-tree, you get to write it down directly in the code, rather than constructing it indirectly, with the view-tree being a hidden side effect.
That is a clear benefit, but the costs are pretty horrendous.
How about we extend our languages so that we can also write down those view hierarchies directly in the code, and at the same time using a format that can also be read/written as data (down LISPers, down)?
My programs were simple, so M was data, V was presentations of the data, C was interaction on the M and maybe V.
It only got confusing when I got more experience.
M is the Model. That means the data and all the things you might ever want to do with the data. So any interaction you might want to do from the view is (ideally) a single message-send to the model.
> V was presentations of the data
And editing the data.
> C was interaction on the M and maybe V.
> It only got confusing when I got more experience.
:-)
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 is a really insightful way to frame it.
Could you give an example? I've never understood how one could possibly reuse a Controller independently of a View. At a minimum any kind of mouse-based direct manipulation requires the Controller having access to the displayed geometry in order to implement hit testing. E.g. how is a Controller supposed to update the selection range in a text editor without screen-coordinate character extent information from the view, or a drawing editor Controller accessing scene graph geometry for object selection, control handle manipulation, etc.
And you're absolutely right!
The problem you're seeing is one of the misunderstandings/misinterpretations of MVC, that the controller is for all interactions/editing. It's not. It's perfectly fine for the View to handle this.
That's sensible. But it's generally useful to split your core state from your presentation, and then you'll find strands of logic that belongs to neither, generally glue code, but some can be useful enough to warrant a module of their own. Also your core state can be alien from the view itself (think a game data (invisible walls, sound objects) and the actual rendering).
Maybe this architecture is not MVC, but MVC can be a first stab for a quick and dirty separation. Then once a cleaner separation can be done by isolating specific modules (in layers, graph, whatever)
And in implementing some process, what is it? As in: what is its encoding in $language and where does it go?
So you end up with the local stamp collectors in the office and get into an argument of: it is part of the model, so should be in the Model class. "Process, nah, that is totally a controller aspect. It does something." etc.
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.
You have Table component.
You have TableData interface. Table component asks TableData interface for data to show.
You have TableCallback interface. When user interacts with Table, like click or whatever actions are implemented, TableCallback methods are called.
When you want to use Table with your data, you implement TableData and TableCallback for your data. That's about it.
I've seen this approach implemented in most UI frameworks. You might rename TableData to TableModel or whatever, but essentially that's it.
But to try to explain myself more clearly - in the architecture you describe, who is that it is implementing TableData and TableCallback? Is it your beautiful clean business logic classes that have no coupling to their representation - in which case that is weirdly coupled in an ugly way, or is it some other class that acts as the bridge between the two worlds, in which case, that's where your ugly code is living.
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.
Do people really do this? That's mind-numbing.
If you wanted to implement MVC with a separate application data model you had to do work to set up a separate model, and keep it in sync with the UI. None of this class of old tools provided any built-in assistance for defining models separate from Widgets, except for some support for binding UI to database queries/results. Of course this was separate from the Smalltalk world, where there were frameworks for building up models out of pre-defined model "atoms" such as an observable number model that you could bind to various views.
What would make more sense to me is to simply define the controller as an intermediary between model and view for updates in both directions. The controller would simply represent whatever needs to be specific to the particular combination of model and view in the particular application context. Depending on context, you might use a no-op controller when no customization is needed, as in the example of the article where a checkbox (view) is bound to a boolean property (model).
My best guess from this article, given then "associated by observer" link from View to Controller, is that the View is supposed to pass events to the Controller, which will interpret them into changes to the Model. But what's the format of these events that's both meaningfully separate from the View, e.g. could be emitted from different views to maybe different controllers, but doesn't just force the View to do most of the work we want the Controller to do?
Splitting the logic from the state and presentation make the code very testable. You can either have the state as input and you test your presentation, or have the presentation as input (interaction and lifecycle events) and test your state (or its reflection in the presentation).
Also this decoupling makes everything more flexible. You can switch your presentation layer or alter the mechanism for state storage and retrieval (cache, sync) without touching your logic.
That's actually precisely the anti-pattern. Massive View Controller is an example of this.
The Model is where your logic is. Everything that is in any way semantically relevant.
Views handled display and editing (yes, also editing!). Controllers ... well ... I guess you might have a need for them.
There has to be a boundary that controls changes to the model. The confusion with MVC is where is the best place for this boundary. Well more than one place as it turns out because there are at least two models of reality trying to converge. The model itself and the view of the model (and the user’s mind).
The view’s job is to present a projection of the model and then collect change events to the model. Thus could be a UX or an API. Other events can also change the model like say sensor data.
The controller decides what view to show and retrieves model data to project and translates change events coming from the external world (views or events) into changes the model should interpret. This includes gatekeeping such as auth, and error handling.
That’s a lot for one class so it can get confusing very quickly. Why localize it in one place?
So viewtrollers come around where the controller is in the view class but in the onhandle methods. This also makes sense since each view has a mini controller to handle all the jiggling bits.
This works well when there are no orthogonal injections like auth or eventing. When those are added it makes sense ins viewtroller to extend the model with controller functionality to for eg control authorization or have a thin event receiver to fsm in the model.
This all works but three years later it’s hard to figure out when I read the code again. So I have learnt to treat the model as pure data as much as possible and the view as much about rendering as possible. Views can have little controllers for handling the jiggling. What the controller cares about is when a change to the system needs to happen.
Then I can put the system control fsm in one place. I can put all event handling in the same fsm to avoid race conditions.
The goal is to make it easier to reason about.
What I don’t want are multiple threads of fsms in conflict with each other.
Trygve was very explicit about the model being a model of how the user thinks about the problem:
There should be a one-to-one correspondence between the model and its parts on the one hand, and the represented world as perceived by the owner of the model on the other hand.
https://web.archive.org/web/20090424042645/http://heim.ifi.u...
Second paragraph.
> There has to be a boundary that controls changes to the model.
Yes. It's called the API of the model. The model makes available API for all semantically valid operations. As I wrote elsewhere, I usually have a facade that acts as the top-level API for the model.
The views can call this API. So can other entities.
> The controller ... decides what view to show
possibly. But views can handle their own subview.
> The controller ... and retrieves model data to project
Nope. Not the job of the controller.
> The controller ... and translates change events coming from the external world (views or events) into changes the model should interpret.
Nope, it doesn't. The views typically do that as well. Controllers may be get involved if there is a complex sequence of steps that doesn't really naturally fit into a view.
> That’s a lot for one class so it can get confusing very quickly.
Yeah, if you put all sorts of stuff in the controller that doesn't belong there.
> Why localize it in one place?
Indeed. Don't incorrectly localize all these things in one place. MVC, for example, tells you not to do this.
> This all works but three years later it’s hard to figure out when I read the code again.
Yes. MVC is much better than that thing you came up with.
(1) https://github.com/madhadron/mvc_for_the_web
The web makes it quite a bit more involved with a separation of client- and server-side state - plus you have a given frontend "framework" in the shape of DOM, which people often leave out of the picture.
This latter necessities the 'escape hatches' in React and alia.
e.x. in a 'proper' ASP.NET MVC 4 project I 'inherited', the View took input data in and with a tiny bit of JS magic/razor fuckery around the query page etc, but overall the controllers would return the right hints for the Razor/JS 'view' to move the application flow along or otherwise do a proper reload.
The Controller in ASP.NET MVC takes on the role of both the classic Controller and part of the classic Model's role (orchestrating the retrieval/updating of data). The connection between the View and the Model is completely severed and mediated by the Controller.
That just leaves formatting the requested changes into a language the server model accepts.
Maybe model is more 'database', controller is API interface (server side + client request requirements), and view is end user render?
THE OG
https://web.archive.org/web/20090424042645/http://heim.ifi.u...
Enjoy!
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...
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
Also, a while back it was way less common for UIs to have backend services. Nowadays those have taken away most of the "model" side in a lot of apps.
With Observers:
We have hidden control-flow and lost intent. They subvert the readability of the developer's intention, in some cases they make you lose the callstack, and it has you searching the project on what code modifies a variable which is a lot harder than searching for a function call. Don't get me started on dealing with handling errors and out of order events. And oh man, is it easy to just avoid using encapsulation and creating a good interface/api for your piece of code.
Most of your code isn't re-usuable as you think:
A lot of things are naturally and forever tied together. Your UI is a reflection of _some_ model, The actions it can perform is based on it's current context, and if your UI changes then your business logic and model probably changes as well. This die hard need of separation and modularity only increases the complexity of the code with the majority of times the code not even being reused.
The only case that I've found somewhat reasonable to use observers is the database. What caused the database to change and effect it has is already pretty far removed from each other when a piece of UI needs to reflect the database.
Granted, It's possible to work around some of these issues, but please please I'm tired of debugging why a menu only opens 50% of the time because there is a piece of code several classes away from the context that doesn't fire correctly and looks like if (child.preferred.child.model.somethingElse.isFinished) { child.menu.child.openMenu = true }
In desktop GUI apps, the delineation is much crisper: the model is the data that the application manages (the CAD geometry, the document, etc). The view is one or more renderings of the data to screen, and the controller is the input and command processor that updates both the model and the view.
Storage is not central to this architecture - it exists, of course, but it’s not really described as part of these core relationships.
It was __sold__ not to programmers, but to NON programmers.
The model is the underlying shape (the data in storage).
The Controller is like the security guard for the warehouse / building.
The View is what's presented to external clients (end users).
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.
If you’re learning MVC, Scott’s OdeToCode/Pluralsight material still nails the fundamentals and the why behind them.
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.
This is in contrast to the 1990s model of web programming where you wrote an HTML page with a <form>, pointed the action to some URL, and that URL was a cgi-script that couldn't redraw the form so error handling was difficult.
In a lot of cases you could say the data fetching is a dependency of the view and not the other way around, for instance if it is a blog post you might have a model object for the actual blog post but then want to put arbitrary widgets into the view which in turn requires fetching whatever model objects are necessary to draw the widgets. From the viewpoint of a CMS user, for instance, they want to drop the widget into the template and have it "just work" (have the framework figure out the fetching.)
The first exposure a lot of people had to this paradigm was Ruby-on-Rails and since it had a rich model system people thought the model system was the important bit but I'd say the router is the most important bit and how you fetch the data and format it is secondary, in fact it's totally fair to use different fetching and templating paradigms for different pages that live under the same router.
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?
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.
As you say, in MVC, the vortex should be User -> Controller -> Model -> View -> User. Best if this is the only vortex (Flux pattern). This can be nicely expressed functionally. That's why I think beans (and mutable variables in general) are bad because each has it's own small vortex of updates.
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.
Instead a model is one or more collaborating objects.
Not sure why this would lead to anemic models, which I completely agree are a common anti-pattern.
In fact, to me it seems rather the opposite would be true: having the single object facade facilitates having a complex model that coordinates all the different pieces to represent a unified view of said model to the views, which can then be very simple and transparent.
In turn, when the models were coupled with views individually, that has tended to lead to exactly that View → DB Table mapping of dumb data objects you rightly criticize.
Then there's the ORM thing, particularly active record ORMs, where "a model" means a database table. And things like serialisation libraries (e.g. Pydantic) where "a model" is one type.
Something that changed how I thought of it was in Robert Martin's Clean Code where the says the whole MVC lives in the outer layers of the application. So basically, "model" is context specific. It depends what part of your application you're talking about. MVC is about building GUIs, that's it. An application usually consists of a lot more.
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...
A hundred people with their own clearly self-evident truth as to whether models are thin or fat.
Absolute burning certainty as to where validation logic lives.
And maybe 3 or even as many as 4 mad hermits who claim to understand what a Controller is.
As someone mentioned in another thread, a separate controller starts to make sense if there are several views or views + other parts that somehow access (some of) the same data.
Qt model-view initially didn't even involve QML by the way, and QTreeView etc still exist for the classic desktop look and feature set. I don't use them much anymore neither, but that's because I mostly do embedded stuff. I wouldn't want my e-mail client or text editor to be written in QML though.
https://blog.metaobject.com/2022/06/blackbird-simple-referen...
Apple is to blame, as they give absolutely O clue on that part (only demo apps with structures that don’t scale)
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.