Hacker News new | past | comments | ask | show | jobs | submit login
MVC Isn’t MVC (collindonnell.com)
189 points by ingve on June 19, 2023 | hide | past | favorite | 162 comments



The M in MVC has come to mean “data model,” but it originally referred to the “mental model” of the user. What kind of thing are we trying to manipulate and what is the user’s mental model of such a thing?

How about a bank account? A mental model of a bank account would include useful operations like deposit, withdraw, transfer, checkBalance, and these would be the methods on the object. The data schema and the persistence would be necessary, of course, but an implementation detail. Models were smart and mapped to human perceptions of a thing, rather than dumb data persistence layers.

These kind of business logic operations have often been moved into controllers, which couldn’t have been further from the original intention.

MVC, smalltalk, OOP we’re all about stopping and thinking about the way humans think while interacting with computers. It was about designing nice interfaces for interaction based on human expectations, not database requirements. Internal object schemas and data persistence were implementation details of an object that could—if you did it right—be easily changed without changing the interface.

But we can’t help ourselves, and instead OOP today is a world of getters and setters with a little bit of data validation (if we’re lucky) and models are just a schema plus a generic data persistence interface (maybe an orm). And the business logic exists in the controller, the least important, least reusable component of the architecture.


> it originally referred to the “mental model” of the user.

Do you have a source for that? I agree that that’s a useful way to think about MVC (somewhere downthread someone wrote that the model should be like a headless version of the application, which is similar), but I’m curious about the original expressions of that idea.


Source: https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&d...

The source is Trygve Reenskaug, the originator of the idea.


Since the link won't open for some, here's the relevant bit (the first two lines of the abstract of the paper linked to):

"MVC was conceived in 1978 as the design solution to a particular problem. The top level goal was to support the user's mental model of the relevant information space and to enable the user to inspect and edit this information."


for some reason i couldn't open the link... maybe t got truncated?


It's a pdf. It opens for me when clicking. Are you on a browser that can't easily open PDF?


hmm when i tried again it worked,thanks!


Thanks for this insightful comment.

I think of the model as being the perfect API for your system that you would happily use if you were in a REPL or in a command line.

It's primary goal should be elegance to do the things that a user would want to do with your system, from a programmatic perspective.

The programmatic interface is a user interface that's not necessarily graphical.

Very few people treat OOP as interacting objects with message passing as in Smalltalk and I think we've lost something.


I agree, and I wonder if this is just what happened as an accident of history or so much a tendency of human nature that we couldn’t have done it any other way. Maybe simple data models is the mental model of computing that couldn’t be easily changed.


I feel interactions between multiple objects is more complicated than looping over SQL query results, ORM logic or object graph traversal logic.

Arbitrary message passing between objects is like an N-way network of communication. The number of participants in an object graph increases the complexity of OOP systems. Kind of like a distributed system since every object is in a different state.


Yes absolutely. Reenskaug has stated that MVC was designed for simple operations (and co-designed DCI as an architecture for more complicated ones). And a number of the early OOP people including Alan Kay, have said something to the effect of “Erlang is the only true OOP language.”

I did a deep dive reading the early papers and watching the lectures from the 70s, 80s and 90s on this a few months ago. The early Xerox employees developing smalltalk seem to have originally thought the idea of encapsulation would compose at all levels. That as object interaction got more complicated, you would simply group a few related objects together inside a larger object, and the rest of the application would use that encapsulating objects interface, and you could go infinitely deep that way while managing the complexity. Later, in the 80s, Kay would talk an about writing objects in smalltalk then gluing them together with a glue language (usually Mesa C), because he felt smalltalk worked well for programming in the small but not the large.

Again, I think erlang got a lot right here, using a different model for programming the small (functional) vs programming in the large (actors/otp).

But to hear the OOP pioneers talk about objects, they consistently describe the objects not in terms of data but in terms of behavior, similar to Erlang actors being processes. Each object is supposed to represent a “computer” and the network of objects is supposed to work like a distributed system.

I know which mental model I like better, but objectively, it’s a very different concept from what most developers think of as OOP today (although very similar to microservices).


This is basically Task Driven Development. You start from a list of user affordances and workflows, try to make them as clear, predictable, and robust as possible, and work backwards. It's top down from the user perspective, not bottom up from the developer/library perspective.

Apple's take on this is the reverse. It enforces UI conformity across apps because there are only so many UI objects in the library. You can build your own, but it's much harder than bolting together what's there already.

This is good for a unified look and feel, and fine for many common applications. But IMO it's not really MVC.

On the web you regularly see applications which are half task driven but not very robust, and break if the user does something a little unexpected.

Example: I got a 2FA code from Namecheap yesterday on my laptop, didn't have my phone next to me, closed the laptop, found my phone in the main office, logged in on the desktop, and it let me right in without the code.

TDD is really a kind of behavioural programming. Instead of tracing code paths you're tracking user behaviours and making sure the paths through the app match behavioural expectations with some sane leeway.

The original conception of MVC fits that nicely. What we have today - not so much.


This idea of "multiple objects" operating as "one object together" is some idea I really like too.

a) A good object orientated API is enjoyable to use, if it maps well to what you want it to do. Look at the developer productivity of ActiveRecord, Django ORM, SqlAlchemy or Hibernate. The object graph model is kind of fun to work with and many developers prefer it to working directly in SQL.

b) Where object orientated APIs fall down is where you want behaviour that the data underlying graph model does not support. I am thinking of OpenGL rendering pipeline or operating system APIs such as POSIX.

c) The Document Object Model in web browsers and the Component Object Model (Microsoft windows, word, Visual Basic, office suite etc) are both dreams that everything on the screen and on the computer was object orientated and could be interacted with with a simple API. Most cross platform GUI frameworks are object orientated even if the underlying graphical APIs are procedural. For example, win 32 API is procedural.

d) There is impedance mismatch of object orientation, procedural (C programming) and data structure driven (including data-driven or data orientated, relational tables, or matrixes)

e) UML entity relationship diagrams are another dream that people had to model objects and relationships in computer systems that didn't pan out completely.

I have a number of ideas in this space. I think graphical user interface development is in its infancy still and all the approaches we use have shortcomings of some sort and I say this as a devops/software engineer as someone who only did a small amount of frontend development in previous roles. I've been loosely following the Rust desktop development progresses.

I desire system behaviour to be trivially easy to transform from one architecture to another architecture. This is my dream.

Take for example Postgres' process orientated model or an imaginary system that uses threads per network socket that you want to refactor to be multiple sockets per thread. The idea of "Late architecture" means we should be capable of transforming this model from one to another slightly different model without dramatic destructive code changes.

a) How do you model behaviour without tying it to a mechanism, so that it can be refactored easily. In Java we have interfaces or Rust we have traits.

b) If you have an extremely rich data model structure, is it flexible enough for future behaviour to be supportable? I feel that introducing plurality (1 to many) (many to many) is a pain point.

One of my ideas is that if you were to log the behaviour of a program with timestamps and implement a program that implements the same log, then its behaviours are identical.


> Most cross platform GUI frameworks are object orientated even if the underlying graphical APIs are procedural. For example, win 32 API is procedural.

Well, win32 is kind of object-oriented, in a way, even though it doesn't always map cleanly to OOP languages.

Windows are basically objects whose methods you call through SendMessage. There is even inheritance by replacing the window procedure and delegating to the parent procedure. There is polymorphism in that many kinds of windows support the same messages (e.g. WM_PAINT) and can decide how to handle them.


> One of my ideas is that if you were to log the behaviour of a program with timestamps and implement a program that implements the same log, then its behaviours are identical.

This sounds a lot like event sourcing.


>> These kind of business logic operations have often been moved into controllers, which couldn’t have been further from the original intention.

Moving them out of the object model without putting them in the controller is actually a good thing IMO. I don't want to test controller plumbing or data persistence, but I do want to focus on the business logic, so simple controllers that route to smart objects with dumb data models helps. I agree the smart parts don't belong in the controller but I don't think it's as bad as you make it sound.


There’s no reason your model can’t have an abstraction layer that contains the business logic and a concrete layer that has the persistence (indeed, if it’s complex at all it should).


> Model updates View, View sees User, User uses Controller, Controller manipulates Model.

> This makes a lot of sense to me, and I think I understand it pretty well.

It looks simple at first glance, but it actually makes zero sense under closer inspection.

Let’s take a simple example. A table of users. You take a table from the database and put it into an html tag table. Wait, who is “you”? The model? Do you want your model to know about html? The view? Do you want your view to know how to connect to a db? The glue code for them? If so, then it is the model-view-controller-glue, not model-view-controller.

This is why what the author calls Apple-MVC is far more common. It is just hexagonal architecture. You take different components that take care of their own area of concern and glue them together to fit specific needs of your app. That glue code is called “controller” in that case. It is intuitive and simple; and thousands of junior developers probably reinvent that type of architecture every day.


> You take a table from the database and put it into an html tag table.

> Do you want your view to know how to connect to a db?

I think this is where there's a misunderstanding: you think of your model as "a database". A database is not a model, a database is just a storage layer. A model is an object that provides an api with high-level business operations and views on data.

At that point, your view needs only to receive from the model the pre-digested data it provides and translate it into html. Similarly, the controller only needs to translate user input in terms of high-level operations on the model, without ever having to interface with a database.


In fact the original MVC was done with Smalltalk which has a persistent image. So the Smalltalk system itself _is_ the storage layer in which model objects live.


This!

Also the original MVC (not the req-resp cycle Model2) was not for client-server split apps. It was for desktop apps, so the Model could update the view. Now with WebSockets we're coming full circle.


Exactly! No rows, tables or SQL. But instances of Models in a persistent image.


> A model is a ... view on data.

I think this is part of the reason the MVC names persist, they are so generic that you can describe the Model as a type of View!


MVC “view” is on the user level, the model is a “view” (facade/adapter/whatever) on the programmatic level.


The way I typically think of it is

• Model is “whatever you need to store persistently, how you represent that data, how should the data be structured and stored when it’s at rest (eg in a database, nosql environment, or big data query system), how you are expected to query that data when you need it, mediates common CRUD data query operations on the store, and broadly handles giving back the results of any stored data query. Intent of the model is to be a black box the controller can talk to whenever it needs something from “storage”. If you did it right, you can change the entire underlying data store and endpoint, and as long as the API for the model is preserved, the controllers don’t even notice.

• Controllers mediate “turning prepared or cached model data queries into something to give back to the user” and “listening for the user wanting to do something and responding appropriately by fetching needed model data or server resources”. Handles moving between pages, incoming API calls, last mile data filtration (where last mile is based on milliseconds to do it, >25ish or so and it probably belongs on the model) and cleanup if any is needed, and is first line of contact (and defense) for anything a user’s triggered.

• Views are whatever the user can visibly interact with and see, and provide buttons, links and interactibles which hook into controller calls to change things or give back data

I’ve found the above model typically has good separation of concerns, usually avoids most fights of “does this belong on the model or controller”, and you can usually cleanly parallelize work between multiple people on a small team if you use ~~waterfall~~ scrum to agree on a data model for each endpoint and needed functions before starting.


I more or less agree, the main issue with your "Model" is that it doesn't separate Data from Service (or Manager or Database or whatever your data-management layer looks like).

A lot of "MVC" has Model as a dumb Data-Model, and data is passed around between the service/manager/db and controller a lot. It's very rare (that I've seen) that you truly have so-called Model code talking directly to the controllers.

There is also the question of data-flow in larger "MVC" containers - where either numerous "views" must co-operate (e.g. in the case of complex nested lists), and must choose some combination of view-to-view communication (often in the case of layout-intense code), or view-to-controller-to-controller-to-view communication, or in some cases view-to-controller-to-service-model-to-controller-to-view communication.

Often the pattern applied to de-congest such a system is a service-oriented or micoservice, or event pattern, although all of these tend to introduce overhead and hence in-efficiencies compared to tightly coupled e.g. view-to-view communication, or tightly coupled controller to controller communication.

I suppose what I am arguing is that Model is a bit of a misnomer - you must always operate upon data, all code is simply transforming that data and the best way to transform that data is usually context specific to some combination of business and practicality needs... so the "Model" unless strictly defined as "the raw data structures" tends to somewhat overlap and envelope the other roles.


> You take a table from the database and put it into an html tag table. Wait, who is “you”? The model?

The model tells the view about itself. This is early O O. Everything is an object. And objects have power and self awareness. When the view receives the message telling it about the model, it does what it has to do to make the model viewable.

So the model and the controller don’t know about html. The view received a message (the current state of the model, or a delta thereof) and responds accordingly.


The model is the storage abstraction -- it knows how to load and save itself to the database.

The view is the output abstraction -- it knows how to generate the HTML from the model. It doesn't know how to load and save to the database.

The controller is what puts these things together. It interacts with the model to get it loaded and passes it to the correct view for display.


MVC is a frontend paradigm

When you're talking about a table of Users you are talking about an entity of User. This can contain extra stuff that's not relevant to the frontend - like the password or lastUpdatedDate, you name it.

In your example, when you talk about a Model, you talk about a user Entity representation. There's a transformation there. The frontend posts a Model (or a DTO) to the backend API, the backend does the transformations it needs (e.g. retrieving the user Entity based on the model user id, mapping updated field values from the model to the Entity, and saving the Entity to the database).

The view is the representation template, the model is what's needed to fill in the template, and the controller does CRUD operations to a backend API.

Edit: or is this MVVM? These days I mainly do backend stuff but I used to call this MVC


In Apple MVC, the view controller is IMHO actually primarily a way of avoiding the need to subclass views. After about 25 years with one or the other form of MVC, I am convinced that most of it is just born out of a weird OOP mental model.


There is a 5000 line controller in one code base I worked on that speaks directly to this. I argued for something more modular/structured like MVVM because every bit of code where these questions arose would just end up in the controller in a grab bag of helper functions, which is exactly what happened.


I don’t think MVVM solves 5k line in a controller issue. You can write monster code using any style, language or pattern.


Nothing can stop bad code but MVVM helps as it is a better mental scaffold than the "everyobne 'knows' what it is but no one can agree what it is" MVC


Hexagonal architecture is great when you learn to separate domain logic from application logic.

Domain logic can be in your entity classes or in separate service classes next to them, but it's still the model.

The controller then deals with application logic: get the data, apply domain logic, save the data, send out notifications, pass the update to the view.


Indeed. Th C in MVC must stand for carcinization, given how often completely independent parties reinvent this one.


Not sure why author got the impression apple’s models had to be « dumb data containers ».

Actually, having dumb models is the surest way to completely screw up your controllers design, since you’re now left with no other places to write your business logic in. This leads to either weird trees of controller inheriting each other for the sake of reusing some data processing piece, or singletons everywhere which makes the code super hard to reason upon, not to mention harder to unit test.

My take on the model layer is : write everything that’s not strictly specific to your UI there.


We really need to call it Model-View-Controller-Service-Repository and be done with it. That is actually what happens 99% of the time.

Logic is done within services and where the complex dependency graphs live. Repositories do the data retrieval. The controller is a traffic cop. The model is a data transfer object with maybe some calculated fields. The view makes things pretty.


No, please for the love of god stop writing services. Having random grab-bags of methods makes it infinitely harder to find what code lives where, instead of putting it into models where we have 20+ years of OO design principles (single responsibility, open to extension, liskov substitution, interface segregation, etc) to guide us. "Fat models, skinny controllers" is a Rails guiding principle for a reason


I'll throw a wrench in - I like heavy views.

In HTML, in desktop GUIs/mobile Apps, in Databases, maybe games.

HTML is founded on the principle that your data is semantic, it knows how to submit and modify itself. You have <form> tags. Every thing you can do in your view, is another thing you don't have to submit to some slow service or wait for a controller to do.

GUI frameworks are often like this as well, complex well-written UI components tend to remove a lot of supporting code from Controllers that just was there to half-support the UI having a slightly different formatting. The more you can push into your view, the more you can delete controller code, the more model code becomes a glorified "save" button. If your data is well-formed, and/or semantic, this model code can be very simple.

Games can be a lot like this as well - after you've loaded your geometry, pushing everything off to the GPU means your "game" code can be focused on simple things. Advanced things like physics and collision can be handled by actor-event logic. Controllers are still there to do some puppeteer-ing and models exist so save and networking code works.

Even databases can be view-heavy. A lot of database work amounts to building views of the same set of tables. Some analysis is done on the views, maybe, but keeping everything in normalized tables means losing out on view caching. Possibly this is more how vector databases work, as part of the data is where the data is not just what type the data is or which table it happens to be in.

Of course, none of this works great for heavy-networking cases (lots of net-code, lots of multiplayer events, lots of cross-database replication), but those are maybe less interesting to me - I prefer decentralized distributed models anyways. I generally value having more functionality at the local compute node, versus some monolithic service that may fail in a couple years time on the other side of the planet.

But that's just me.


that's fine! I'm happy with heavier views. The thing i'm arguing against is the Rails pattern of having FooBarService classes that are effectively just a single method and cobbled together in completely random ways instead of using actual model methods.


Until the moment that models have to call other models to get anything done.

The problem is that the models often get too tied with the database implementation. Then, you're separating business logic that doesn't make sense to separate.

The fat model ruins single responsibility because the database table is not a good proxy for one reason to change.


This is a specific problem with the Active Record pattern, rather than something intrinsic to MVC, and it's exactly why AR is fine for simple apps where the business logic looks a lot like manipulating a row in a database, but breaks down for anything more involved.

But also there's no reason you can't have non-database-backed classes sat next to AR models, if they make sense in the domain. Not everything needs to be a database row.


What, then, is a service other than a model without a database table?


Eh? It's a service. It represents a domain operation (or group of operations) that hasn't been coalesced into either a value type or a transient entity, both of which are also models without database tables, both of which are preferable to a service if they fit the domain.

None of this says that putting business logic into a separate class to the thing that happens to back onto a database row is correct because of single responsibility. That's a fundamental misunderstanding of the SRP.


> models have to call other models.

“You're very clever, young man, very clever," said the old lady. "But it's turtles all the way down!"


I use services, but they are not a random grab-bag of methods. What do you even have in mind when you say this?


I too use services and they are where the business logic lives. In the case of “create user” they’re basically no-op repository methods, in the case of “sync user profile data” they may invoke storage or external service calls in a certain business-logical order, but they map exactly to the applicable domain(s) - putting user-profile methods in the user service is when you end up with muddled service boundaries


You start with MVC, following "the fat-model, skinny controller" rule of thumb.

Now the Models become obese. So you split out the Repository logic.

Now you need a place to do "client onboarding" which creates a Customer, a User, a BillingInfo, a bunch of things to show the new client a non-empty environment... Where does this live? In the Customer model? Nah. In a repository? Certainly not.

No. That's when the OnboardingService comes along.

note: We use jOOQ so our "Model entities" are generated from the db schema and thus logic-less.


Fat models is the opposite of single responsibility: doing both business logic and data retrieval/storage logic inside the same class is already more 1.


You’re confusing fat models with the active record pattern vs repository pattern.


Lol, but MVCSR (MVCaeser) sounds like an architectural dictatorship where one must follow all rules or else be outcast from citizenship. Where some executive will stab you in the back and sell it to a private equity firm to be cut and squeezed by all the lands and your intentions and beautiful code structure is murdered by junior bootcamp devs. /s


The problem with the MVCaesar pattern is that sooner or later a Basic Routing Universal Transformer User Service stabs it in the back.


Ironically named Optimus.


Not a MCSVR (McServer)?


> Model-View-Controller-Service-Repository

Where would you put validation logic, though? I mean, some of it might need to just check whether a domain object has its fields filled out, but other validations might need to cross reference DB data to make sure that everything is valid in accordance to the business rules.

Ergo, we might have Model-View-Controller-Service-Repository-Validator

Even without being silly, it's interesting to see how different languages and frameworks handle common concerns, for example, with some you will have repositories, with other the model objects themselves will handle queries through some ORM.


Validation logic needs to be spread throughout the system because your validations are all context sensitive to the operations being performed. It's not always possible to front-load that into a simple schema in your transports and each component needs to be correct in its own domain.


> Validation logic needs to be spread throughout the system because your validations are all context sensitive to the operations being performed.

While it certainly isn't always possible, I've definitely seen it be aggregated in a single place in monolithic codebases that focused on CRUD.

Essentially, it was structured like so:

  +--------------+   +------------------------+   +-------------+
  |Resource (API)|---|Service (business logic)|---|DB Repository|
  +--------------+   +-------------------------   +-------------+
                                |              \
                     +-----------------------+  \ +-----------------+
                     |Validator (validations)|   \|REST clients etc.|
                     +-----------------------+    +-----------------+
The Resources were typically just API endpoints (HTTP), that passed data onwards to Services. Those could then call upon other services or service requests on their own: store/persist data with Repositories, deal with scheduled processes, or pass data on to other systems through REST clients etc.

Before Services did any of those, they reached out to a Validator to make sure that the data matches the business rules and constraints. Sometimes there was additional validation context passed in (essentially a map and some enums), sometimes there were certain constraints that an entity needed to always follow within the context of the business and so on.

In practice, it was good because you could see all of the constraints in one place, that an entity has to match before it can be persisted in the DB, or passed onwards to another system. But then again, that's not always viable in more modern architectures. Of course, it wasn't the most traditional approach to MVC either, even if those concepts translate pretty well to it.


Most MVC frameworks have some soft or form DTO notion, as a form does not map 1-on-1 to one db record. The form DTOs in our app also holds the logic to validate itself.

Basically, like sibling post mentioned: you put it where it makes sense.

Doing validation on db entities, is certainly not a place it makes sense (except in the most trivial cases, hence the common mistake).


There's two definitions these days for the term "model" in MVC:

1. An object that models part of the service/product. Meaning, not something related to the operation of the medium by which the service/product is delivered, but any operation that models the service/product itself, independent from medium. This could be a representation of a DB row, a service object, a decorator, or any other piece of logic that relates to what you do.

2. An object that reflects a DB row, but not anything that represents other parts of the service/product. I don't know the full history, but probably connected to the rise of ORMs and Rails, and when devs got in the habit of throwing almost all product logic either into controllers or Active Record models. And when you want to start putting the bulk of that code in dedicated, non-DB files, you have so many active record files that you don't want to muddy up the `models` directory with non-DB code too, so you create other directories alongside `models`, `views`, and `controllers` to house the other files.

The author may have been using definition 2, where models are strictly seen as DB-related code, and having them be "dumb data containers" is useful as it enforces extracting operational logic to classes better suited for that logic. But since they get put in other directories, they aren't seen as "models", despite technically still belonging to the "model" part of MVC.


Def 2 is actively bad and wrong, though. It happens to fit simple apps (and simple bits of complex apps) so it looks reasonable, but what's really needed is an off-ramp to let the model actually be what it should be, which is a domain object.

Active Record is an optimisation that by rights ought to be premature, but the downsides don't start to bite until a certain complexity threshold which a lot of apps might just never hit.


> Def 2 is actively bad and wrong, though. It happens to fit simple apps (and simple bits of complex apps) so it looks reasonable, but what's really needed is an off-ramp to let the model actually be what it should be, which is a domain object.

I somewhat agree. The problem, I think, is that MVC is great at explaining the types of contributing code to a software project, but it doesn't lend well to a directory structure in a project. There are just so many more models compared to controllers/views that it's logical to make each type of model object be its own top-level directory, along with controllers and views (well, top level within an `app` directory, as is custom in a Rails project).

So you end up in situations where people either have a "Model(record)/Service/Decorator/OtherModelPattern/OtherModelPattern/OtherModelPattern/Controllers/Views" system, a system where all the domain logic is put into ORM classes, or a system where all models are organized within the `models` directory, but possibly interfering with the intuitive file lookup of the framework (eg, if you tried putting all active record models in an `app/models/records` directory and having a huge break from Rails tradition).

If Rails simply used "Record" instead of "Models" for the directory name, a lot of this could be a lot clearer, but we're now what, 18 years deep into the muddying of the MVC waters? As much as I'd love to see a return to using MVC properly, I'd rather concentrate on doing more productive things.


> apple’s models had to be « dumb data containers ».

They don't "have" to be, but that's very much the result of what Apple has been advocating, what the community tells itself and what happens.

> Actually, having dumb models is the surest way to completely screw up your controllers design, since you’re now left with no other places to write your business logic in.

Exactly. And the coordination logic. Alas, that is exactly what happens, and I've been in enough of these codebases.

It's actually a bit more complex than that, as what people seem to aspire to is write self-contained widgets for their functionality, essentially slicing the application vertically most of the time. And that appears to have a bunch of causes, for example the PM->Design->Engineering process chain that also appears to be standard (and broken).

> My take on the model layer is : write everything that’s not strictly specific to your UI there.

YES.

UNDERLINE.

BOLD.

BLINK.

Repeat 100 times. Say it loud. Sing it. Shout it.

The "Model" is an object that is the headless application. It implements and coordinates all the functionality. You put a thin GUI on top of that. Or a different GUI. Or an API. Or a WebUI. Or a CLI....


> The "Model" is an object that is the headless application

Wow, that's a great mental model (wink) to have.

I never thought of it in those terms, but I think I do follow it, mainly because I tend to follow John Ousterhout's guideline of "Deep Modules, Small Interfaces".

This way the model ends up being exactly what you're talking about. It is testable, issues are easier to reproduce, it's easier to reason about, it is portable to other contexts: server-rendered web app, JSON API, GraphQL API, desktop app, mobile.

I think the biggest obstacle for people using popular frameworks is realizing that a "Model" doesn't have to be necessarily one single class. The important part is having a model-like API that is good and self-contained. Previously I didn't have a mental model for what is "good" but with your comment I think I can put that into words a little better.

One of the biggest problems I have with Services and other patterns such as Operations, Interactors is that developers often couple them to Controllers and Views. Also often I see duplication happening when new patterns are added because they're seen as separate. If you think of them as "part of the model", however, and follow the "the model layer is its own headless application" those issues have a smaller chance of happening.


that's also my tip for devs working in my team when they ask me where they should write a piece of logic :

- would you need to change that code if the UI is now a command line application ? No ? then it's in the model layer.

EDIT: i've also been in those codebase a LOT, and actually decided to create a blueprint of a mobile codebase with an emphasis on the model layer. I'm not going to post it here because i want to remain anonymous, but i've been using it in my projects (and my friend's have been doing the same) for the past 6 years..


Cool. And great to see I'm not alone.

I've written about mine here:

https://blog.metaobject.com/2022/06/blackbird-simple-referen...

Maybe we should form APSMA, the Association for the Promotion of Sane Mobile Architecture?


Actually let me share a story :

I usually never ever advise my customers to rewrite their app from scratch. And yet here i was, doing it for the 4th time in a row for 4 different customers, in just 2 years, simply because the codebase were a total mess each time.

So i thought "well, there's got to be a problem with iOS developers here in Paris". And so i decided to give a talk to the largest iOS dev group in Paris to show my model layer template.

I started with a poll : who has ever heard about "layered architectures" ?

Well, there was about 100 people in the room, and only 2 people raised their hands. I talked to them afterwards, they were both backend developers originally, and were absolutely stunned nobody else in the room knew the term.

So, that was just to tell you that the problem is a cultural one. Apple never ever mentions architecture in their documents / talks. So most mobile devs that start in this path start by coding fancy animations, and only much MUCH later, after a lot of projects turned ugly, do they start to realize there is another world for them to discover.


my story is almost identical to yours + I was backend engineer many many years ago :)

would you mind sharing your presentation?


I think, I’ve already agreed on this with you, in one of the previous threads.

But I’ll gladly upvote you everytime!


This times 100 :)

It is only recently that I started to realize this and once you do it makes a lot of sense.


> My take on the model layer is : write everything that’s not strictly specific to your UI there.

My take is this, except with the caveat that the larger a model gets, the closer to declarative it should become. You touched on this by saying the model layer rather than just the model, but it's worth emphasising.

You can achieve this by abstracting out the messy bits, e.g. moving some hooks to a task queue (processing an image) and others to a declarative helper (validation as a schema instead of functions), turning a massive querybuilder jumble on one model into a library that lets you declare valid search and ordering by fields and operators for all models, etc. When a model becomes too big to fit in your editor window is probably a good time to start getting rid of most of its procedural code.

One of the old Rails aphorisms was (and may still be) "fat models, skinny controllers", and while this was better than the alternative it still led to a lot of 500-line monsters.


Heh. "500”. I have had monsters ten times that size.

And yet it wasn't obvious that refactoring was really an improvement. Sometimes the program is just big and does a lot of things.

It's not good. It's a disaster waiting to happen. And yet until it does the code smell can continue to smell without strangling anybody.


Yes, there's definitely a confusion in the meaning of the word "Model":

All objects belonging to the model layer don't have to match a data type. For an online shopping app, you may have an "Item" object, but your purchase logic may be in a "Purchase" service object, which needs a "PurchaseRestAPI" to perform HTTP calls, etc.. They all belong to the "Model" layer.

Correctly structuring your model layer is the key for a maintainable application. The problem with most mobile developers is that Apple has been demoing a lot of features on the view layer (because of the cool UI effects the user sees), but it's something that has to come in the end. First you need to model your domain logic properly. Then you can see how to represent it on a given user interface.


But dumb, passive data structures still have their place in that "Model", and unfortunately, those are usually referred to as model. Note how I picked up your quotes and capitalization for the former, that's very helpful here. This is really where I see the root of why MVC is such a useless term: the "view" feels intuitively unambiguous, there's always something that is very tempting to declare model and everything else will either be declared controller in one broad sweep or assigned to one of the other two in an elaborate act of design-by-committee (even if you're all alone and it's only the different positions in your head).

Personally, I've made it a pet heuristic to blanketly assume the worst whenever I hear MVC. Perhaps there's good code out there despite its makers trying to find salvation in those troublesome letters, but I assume the false positive rate to be acceptably low.


Personally I kind of like dumb models. Dumb models means I don't have to think too hard about "a.affect(b)" vs "b.be_affected_by(a)". I can just write functions that do what I want right now. "do_my_thing(a, b)". No reason to be doing anything else when programming. If you have people making controllers inherit each other just to share functionality then they're probably brainwashed by OOP. Stuff like inheritance and mixins only exist to solve fake problems that make no sense outside of C# or Java. To me, singletons and plain-ole-functions are pretty easy to deal with in most languages - I just ask the editor where a function gets used or where it's defined. If your language doesn't support that kind of thing (looking at you, Ruby) then again that's another problem caused by tooling.


data objects have to be dumb, the managers don't.

both C# and Java can be used in a sane way, but ... at what cost, right!? C# is tied too much to the regular MS circus, and it's just a few more years and Java will finally be great! oh wait.

of course, actual, real-world walking-talking problems can be solved in both of these ecosystems, but ... almost always the real constraints are not the actual language. (... trying to do HFT with a JIT-less language, or some other faux pas)


The end of this rabbit hole is probably something like “actually DDD is best!” or whatever comes/has come after DDD (domain-driven design)


>Actually, having dumb models is the surest way to completely screw up your controllers design, since you’re now left with no other places to write your business logic in.

There is such a place: model


There is a lot of truth in what the Author wrote, however he completely missed the MVVM pattern and confused it with MVC.

The MVVM pattern, originally designed by MSFT for Silverlight, showed a way that basically cleaned up a lot of the issues with MVC.

That is what SwiftUI uses today, MVVM, as you can compare with his diagram of what he calls "Apple's MVC" and this diagram from MSFT: [1].

In fact, I'd go out on a limb and say that no one really uses MVC today - in its pure original form.

[1] https://learn.microsoft.com/en-us/dotnet/architecture/maui/m...


I like the Model-View-Adapter[1] architecture, which is basically what you get by reversing the arrows between the View and ViewModel in MVVM. The view and model are oblivious to each other and to the adaptor directly (but interact with it indirectly), and the adaptor depends on both the view and model.

My use case for this a bit different than regular UIs though. I've used it in an MMO server, with the view representing the network protocol which clients interact with, and the model being the state of the game world. The adapter receives messages from game clients asynchronously and updates the world, then sends messages back to the clients affected by the change in the world state.

IMO it makes sense to have the inverted dependency in this case. If the view depended on the adaptor, then changes to the game server behavior would impact the network protocol, which would also force changes to user clients each time the server gets updated. Ideally we want to be able to make regular updates to the server without impacting clients.

[1]:https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93ada...


Funnily enough, the 1990s VisualWorks UI moved on from the original MVC implementation into a pile of complexity involving adaptor classes and automated event processing, so even the Smalltalk guys knew the original implementation wasn't enough for most GUI problems.


Yes, because what is called MVC today is really MVP (originating at Taligent), which was a simplification/derivation of Xerox MVC intended for loosely coupling business logic to user interfaces.

This is just my speculation but I think the mixup between MVC/MVP was largely the fault of Rails' popularity coupled with a general lack of awareness of Xerox MVC, and after its popularity other frameworks have rolled with the corruption/redefinition.

That said, whether you call it a Presenter or Controller is largely irrelevant in practice. It's just a particular application of the mediator pattern (requiring some form of observer-like pattern) with the goal of decoupling the Model from the View.

The presenter/controller's job is to mediate between the model and view. The P/C references and can directly call both M and V, but also observes M and V such that neither M not V knows about P nor about each other. The key benefits here are that the M is now testable in isolation, and it's much easier to swap V out for V' (with an accompanying P').

MVVM is another story, but it's in the same MVP derived family. Xerox MVC is largely a relic, though as some other commenters have pointed out there are some echoes of it in some React/Vue architectures today.


So, the original MVC was designed in the context of green-screen terminal systems. In that context, this “design pattern” really isn’t a design pattern per se; it’s just a formalism over the code you’d inevitably write to make such a system work. In a green-screen system:

• the “view” is the terminal buffer state — not even a “virtual DOM” version of it, but literally the buffer state itself (under 3270 protocol, properties like “editable” are basically bits in the TTY buffer applied per-cell — you can think of them like ANSI TTY-cell background colors and not be far off.) Think of it as an mmap(2)ed file in backend memory, holding structs of almost the same type as the view, save for OS-syscall-time remapping to prune out the form labels.

• the “model” is the ADT over the record-oriented mainframe datafile backing the terminal (How does the model know how to change the view? Because it’s a very thin abstraction: fields in the view-state, and fields in the DB state, are 1:1, with no parsing or serialization — leading to things like passwords only being stored in the DB at the size of the form-field for them.)

• the “controller” is the serial line-discipline that interprets the key codes sent from a TTY into editing actions like “overwrite a character” or “backspace”, and applies those actions to the data in the model (how does it know what part of the model is active? “Spatial databases” have something like a hierarchical navigation-path DB cursor as part of their descriptor state. Switching the active view is a model state change!)

• you need an “editor” separate from the “controller” because otherwise every character edit action sent to the TTY would provoke an instantaneous commensurate change in the DB data. If you need to store and validate live edits before committing them to the DB, then you need an editor “proxy” object to hold that “draft” state.

All in all, pretty different from anything we’re doing these days. Or is it? Depends on where you draw the line of abstraction! In modern terms:

• the classical MVC “controller” is the frontend or per-component view controller — observing the client’s actions and feeding them into the system as events.

• the classical MVC “view” is the API query result set, databound through the client DOM components. If your app is server-side rendered, it’s the HTML DOM itself as loaded into the browser!

• the classical MVC “model” is the entire backend, through its API

• the classical MVC “editor” is the changeset — a CQRS Command-in-draft. (In a HATEOAS context, an HTML form is also this.)


> So, the original MVC was designed in the context of green-screen terminal systems.

Not at all: this was designed at PARC in the context of the Alto and the D machines running systems like Smalltalk. It even mentions the mouse. It talks about querying the type of a field, that might for example contain text.

You reasonably mapped it to a serial character terminal but this is not at all what it was designed for.


I said “in the context of”. As in, Smalltalk itself was designed in an environment where green-screen terminals were a recent and popular paradigm. As such, in some ways, they’re reflections of one-another: isomorphic solutions to problems the same people were having around the same time.

Also: IBM 3270 protocol is not a “serial character terminal” protocol. It’s essentially an X11-like client-server bidirectional state synchronization protocol for what are essentially pure-text HyperCards.

If you can understand that, then you can probably see how Smalltalk is — in the abstract! — a green-screen terminal system. (So is X11 itself; and so are modern “framework display server protocols” like QML and XAML; and Mozilla’s XUL; and of course HTML.) Smalltalk just happens to have the client and server in the same address space. (But they’re still separate objects — a Smalltalk display window is a “control” that doesn’t share state with its backing delegate that’s telling it what to display.) One could think of this as the “client - backend responsibility separation” paradigm. It originated in the offices of people trying to horizontally scale out interactions.

This is in contrast to the libtermcap/ Windows GDI / bitmap-buffer bit-blit approach to view-state management, where there is a presenter that is a kind of “driver” for the model, living subordinately embedded inside its address space and synchronously “rendering out” whatever the model decides to render. This paradigm originated in single-tasking microcomputers.


> In that context, this “design pattern” really isn’t a design pattern per se; it’s just a formalism over the code you’d inevitably write to make such a system work.

What exactly do you think a design pattern is?


> really isn’t a design pattern per se; it’s just a formalism over the code you’d inevitably write to make such a system work.

I feel like that's what all the original design patterns were, though, pretty much! Although maybe not "inevitable", people could write all sorts of spaghetti messes, but the original design patterns were basically formalisms of the sane ways to handle a given problem in a concise elegant way.


"Inevitable" was the crucial distinction I was trying to make. Something isn't a design pattern if, no matter how you write it, the shape of the problem domain means that the solution looks a certain way. What you have in that case is just a formalism for describing the reflected problem domain.

A design pattern is a highlighted, reified tactical choice in implementation. It's something that you, as a programmer, want to give a name to, so that you then have words to communicate the choice you made at design time, to talk about where that choice is valid/proper/important to make.

These patterns can then be shared with people who haven't worked on problems where that choice is relevant before, where knowledge of the pattern then acts as a form of crystallized professional experience. Rather than looking at your individual problem, and then evaluating the pros and cons of certain designs on a case-by-case basis, you can recognize that the problem fits one or more patterns you've learned, and then merely evaluate (or rely on prior evaluations documented by others) the pros and cons of applying the pattern.

When there's only one inevitable way to do things, a formalism is just there to allow software-engineering academics to model what you're doing; but you yourself, when coding, don't need those words to talk (or think!) about what you're doing.

---

An analogy:

A design pattern is like a martial-arts movement form. Rather than having to solve for the optimal movement to block/parry/return an attack on a case-by-case basis, learning the forms of a given art gets you to think in terms of which well-known, high-level movements would be best to apply in a given situation — which massively decreases cognitive overhead, allowing you to actually fight someone in real-time.

But there's no martial-arts movement form you need to know or learn for "taking a drink of water." If there's a glass of water on a table in front of you, then there's only one natural way to pick the glass up and hold it to your mouth such that you can drink from it. "Taking a drink of water" is not a movement form, because there's no cognitive load placed on you for determining how you're going to do it. If you formalize "taking a drink of water" as a movement, that would only be for academic purposes, not practical ones.


MVC is one of those things (like Agile) that doesn't actually mean anything and people just nod their heads and gloss over it when someone mentions it.

You know when something is that kind of thing when most discussion on it is about the definition.


It does mean something specific and useful. (both mvc and agile)

Only that people who are productive take it and try to use it and stop caring about nonsense details.

Where bunch of other people try to dissect it to find “true way” which is bunch of time wasting and annoying for people who need to get job done.

Then these people also blame flawed use of “pattern/process” for whatever went wrong because only if they could do it perfectly as described in some blog post everything would be perfect.

Meanwhile while they were arguing if daily standup should be 10 or 12 people and maybe 23 minutes instead of 19, and if controller is really a controller and should have less or more logic - productive people shipped two versions of app to test server and presented it to the customer already.


If you squint a bit, the "original" MVC looks eerily similar to the one-way data flow pattern popularized by flux/redux.

https://en.wikipedia.org/wiki/React_(software)#Unidirectiona...

edit: better link


If you squint almost of all software looks like MVC because it's essentially just, input(data)/processing(controller)/output(view).


If you squint just a little more you'll notice a monad is just a monoid in the category of endofunctors.


Software is just a giant ORM.


It's all just one big state machine.


In many ways it's really just MVC (at least the fat model / skinny controller style) except they renamed the elements:

- Dispatcher = Controller

- Store = Model

- View = View

And the actions are the messages sent from the user to the controller.


> MVC looks eerily similar to the one-way data flow pattern popularized by flux/redux.

I'm not seeing it.

From TFA:

> Views: A visual representation of a model. Views are updated by querying the model directly, and are notified by the model of updates. A view can update it’s model directly. A view should never know about things like keyboard and mouse events directly.

I'd like to emphasise the "a view can update its model directly" part - which is exactly what Redux and React are designed to prevent: when logic in a view can mutate the model (especially when the view _also_ depends on update notifications from the model to update itself) things get impossible to reason-about.

Instead, Redux/React is designed to force the view to "update" the model indirectly via actions and reducers.

Another tenet of Redux+React is that the model ("state" in Redux/React) is an immutable object-graph which is replaced on every update, whereas "classical" MVC requires a mutable, long-life'd model.

I feel the similarities only exist if you squint - and if you're lumping TFA's idea of MVC with React (and MVVM (groan...)) as ideas in opposition to how most people wrote/write RAD/VB6/WinForms by directly subclassing controls and overriding message-pump event-handler methods, which (I hope) everyone will agree is an approach that doesn't scale beyond building throwaway UIs.


It may be a matter of explaining it differently. Maybe just the names! But I never "got" MVC, and I got the one way data flow pattern straight away. Maybe I was just old enough to get the point that time around.


I'm glad I'm not the only one that doesn't get MVC. I can sort of understand it if I study a real example for a long time, but to doesn't stick. Sometimes I want to think that learning to code in the days of 8-bit computers is somehow incompatible with a lot of modern programming concepts, but maybe I'm just old and tired.


MVC is one-way. Some people think MVC is two-way data binding. That's MVVC actually.


I think flux/redux is generally cleaner imo. But have had a tendency to think of it that way. MVC usually feels like spaghetti in contrast. And while Redux/Flux takes some mental overhead, I see so many fewer side effects in UIs that use the approach.


If I squint, Redux is like a controller. Handles requests and response. It calls the DB, gets a response, updates state, then a view (React component) renders it.


the purest mvc demo is pong.


Indeed, what the various “reactive” frameworks do is in the end the idea behind MVC, although as they are web-based the implementation is ridiculously complex and the abstraction is very leaky.


One question which always preoccupied me is how those MVC components interact with nesting/recursion. For example, a view may include a scrollbar, which has state, and can be interacted with, sometimes independently from the model displayed by that view. So the scrollbar has it’s own model (containing e.g. the scrollbar position) and it’s own view (responsible for how the scrollbar is rendered) and controller (allowing the user to operate the scrollbar). This nested mini-MVC for the scrollbar however is primarily or exclusively part of the larger view. The scrollbar’s model, however, may have to refer to the larger model, for example when the scrollbar size depends on the size of the larger model’s data. Or maybe its the scrollbar’s controller that queries the larger model (or gets notified by it) and then updates the scrollbar’s model. In any case, it’s not obvious how exactly the nesting could or should work.

More complex cases are for example a combo box, which consists of a text input, a dropdown button, a dropdown list, which in turn may have a scrollbar. And this is just one relatively basic control within a potentially much more complex UI.

I’ve never seen a systematic treatment of that topic. Given that UIs use nested controls, one would think that this would be widely addressed by architectural patterns for UIs.


The approach Elm uses is every event goes through a main "router", no exceptions.

If you want to do sth special on the account page modal scroll, you issue a specific event for that, and deal with it in the same way as any other.

It is a very simple and clean model, but not complicated enough for some people :) of course it helps that with a good type system you can catch missing or incorrect cases.


This is what MVVM is for. Last two letters mean View Model - a model of the view - data structures for each user control, and code that synchronizes these structures with Model structures.


But that’s not a recursive pattern. I would disagree that there is only a single level on which MVC, MVVM, or whatever, is relevant. For example, the controller or View Model of a view containing a combo box should not have to manage the internal mechanics of the combo box. So it makes sense for the combo box to have its own internal MVC/MVVM. Similarly, the view may be embedded into a larger dashboard, where the dashboard should not have to manage the internals of the view, but would have its own MVC/MVVM.


I've always tended to think these analytic frameworks are tools to aide discussion not actual proscriptive ground truths.

I do believe CAP. I don't think you can optimise for all 3. But that aside, I never yet found a compelling "this is how we do it" which didn't come with exceptions, which turned out to be anything BUT "exceptional" when it came to what I observed in the system at large. Atomic ops which were dependent on subsequent calls. Databases which said they implemented transactions but it turns out people code around them because of the barriers in parallelising outcomes. Systems which say they depend solely on update in the browser but which make persisting calls back into the server to update state, and fail if that doesn't work.

Views and Projections, Message Buses, Eventual Consistency, they all turn out to have "mostly" tacked on the back.

"its CRUD, Spock, but not as we know it" -Mostly. ("it's life jim": octopuses, coral.. deep earth mineral digesting worms, bryophytes, mitochondria... prions...)


Fun fact: React was originally designed to be used as the 'V' in MVC [1].

[1] https://github.com/facebook/react/tree/015833e5942ce55cf31ae...


It still can be! A client-side app that talks to an API is still the V. Obviously many apps get elaborate with client-side state, but you don’t have to!


That API itself my use MVC too!


Fun article.

Just a note, MVC requires the Observer Pattern. If the Observer Pattern isn't present, it's not MVC. And it's fine if it's not MVC. It can be called something else. It's fine.

RoR doesn't implement the Observer Pattern; thus, not MVC. It can just be RoR. AspNet MVC doesn't implement the Observer Partner; not MVC. It can just be AspNet.

Here's another reference to Tyrgver's explanation of the MVC language. Although, it's lack of code (and details) is probably one reason why people misunderstand MVC ;)

https://folk.universitetetioslo.no/trygver/themes/mvc/mvc-in...

Woohooo! Fun times.


https://github.com/hotwired/turbo-rails/blob/main/app/models... is designed to close that loop with automated view updates from ActiveRecord changes.


Is it weird that the inconsistent citation scheme really jumped out at me?

> In December of 1979 _Tyrgve Reenskaug_, an employee of _Xerox PARC_

Later

>In 2004, a Danish man


I found it really funny, it actually made me laugh out loud. It seemed very deliberate to me, though, so that's why.

Edit:

To clarify, I think DHH would probably laugh at this as well, which is why I feel it's not so much laughing at anyone as it is laughing with them.


I took that as a slight dig at the (famously self-publicizing) Rails creator, or an in-joke for the Rails crowd - which seems to be the world the author lives in.


The author is being cute in the second citation and is referring to David Heinemeier Hansson who is famous in the web dev world for creating Ruby on Rails.


Interesting article. I didn't know about JSP Model 2.

Another detail that's not mentioned, but very useful for understanding the original pattern, which is described in [1], is that controllers had a specific and unique responsibility in the original system where the MVC pattern was defined: The system gives the input focus to a controller, not to a view, so you can't accept keyboard input without a controller. So, unlike pretty much every other GUI toolkit, it's not a matter of having the view delegating input to a controller as a matter of good design, but rather that the controller is simply the way that the system provides the input to the application – you have no choice in the matter. This also meant that there was one controller per widget that could receive focus (corresponding to each HWND in win32), not just one controller per screen.

The same document also contains answers to some frequently debated questions about the true, proper and orthodox way to do MVC: Should the view subscribe to model or should the view updates be mediated by the controller? Should the model represent the pure domain logic or also contain view-level state? Should the model be a simple value object or also contain domain logic? It turns out the answer is: All of the above! The original MVC in Smalltalk apparently used all of these options in different applications depending on the circumstances.

[1] https://web.archive.org/web/20100921030808/http://www.itu.dk...


For OpenStep/NeXTSTEP style MVC, which is very similar to the original MVC, is well explained in the book "OpenStep for Enterprises", https://www.amazon.com/OpenStep-Enterprises-Nancy-Knolle-Cra... . I just cannot understand why the author says it's different from ST80 style MVC.


Is the explanation in that book better/different than the one in https://cdn.preterhuman.net/texts/computing/nextstep-openste...? Just curious.


Just going to leave this here:

"There is a View, which only knows how to tell you something. There is a Controller, which only knows how to manipulate something.

This is a crack team of a quadriplegic and a blind man, each in separate rooms, and you have to delegate your UI to them? How, in the name of all that is holy, are you supposed to do that? Prisoner dilemmas and trolleys? No really, if the View is a TextField, with a keyboard cursor, and mouse selection, and scrolling, and Japanese, how is it possible to edit the model backing it unless you are privy to all that same information? What if it's a map or a spreadsheet? ばか!

This implies either one of two things. First, that the Controller has to be specialized for the target widget. It's a TextController, with its own house key to the TextView, not pictured. Or second, that the Controller doesn't do anything useful at all, it's the View that's doing all the heavy lifting, the Controller is just a fancy setter. The TextModel certainly isn't supposed to contain anything View-specific, or it wouldn't be MVC.

Wikipedia does helpfully tell us that "Particular MVC architectures can vary significantly from the traditional description here." but I would argue the description is complete and utter nonsense. Image-googling MVC is however a fun exercise for all the zany diagrams you get back. It's a puzzle to figure out if any two actually describe the same thing."

https://acko.net/blog/model-view-catharsis/

MVC is a cargo cult, and at the end of the day separating your view from your controller isn't going to accomplish anything if the code can't be easily upgraded to multiplayer with undo/redo, which is what users want.


> This implies either one of two things. First, that the Controller has to be specialized for the target widget. It's a TextController, with its own house key to the TextView

Yes, this appears to be the way it worked in Smalltalk-80 [1]. You had StringHolderView and StringHolderController which used a StringHolder as the model.

> Or second, that the Controller doesn't do anything useful at all

The controller in Smalltalk-80 controlled input focus. The view wouldn't be able to receive any input without a controller:

"Class Controller does include default scheduling behavior. It takes the point of view that only one controller is to be active at a time; that is, only one controller at a time mediates user input actions. Other views could be displaying information in parallel, but the user's actions are to be interpreted by a single controller. Thus, there is behavior in class Controller for determining whether a controller needs to receive or maintain control."

[1] https://web.archive.org/web/20100921030808/http://www.itu.dk...


It’s a topic I’m very interested in! A very eye-opening comment I have seen on HN was this: https://news.ycombinator.com/item?id=4190252

> It helps me to remember that in Smalltalk the idea was to always have <view|controller> pairs. They were always the same object split in two; each view had its own controller and vice versa. The controller is supposed to be input-oriented and the view is supposed to be output-oriented

Another commenter here put it nicely: your “whole application” is the model. The view-controller pair over could be one for a terminal, or a website, or a desktop app, the model should be reusable. A controller isn’t, handling a click on something doesn’t make sense in a terminal.


Somehow applications running on a single machine (classical desktop apps) cannot be the same class of patterns as a browser-webserver combo where two different machines may persist different pieces of data, respond to different types of events etc. The browser receives human user input and displays model information. For the web server the "user" is the browser.

Effectively you might want to start with an abstract stacked MVC-MVC pattern and see if anything is actually redundant.

I dont think clearing those things up is an academic pursuit. The confusion about those patterns is an important data point about the quality and accuracy of our communication about architectural designs.

If you thing of the MVC discussion as a sort of metacode, its full of metabugs.


> Model updates View, View sees User, User uses Controller, Controller manipulates Model.

Yeah, not really. The View is actually also used to manipulate the model, most of the time.

The Controllers are there for more complex situations, and stuff that isn't really relevant any longer, like handing the physical input devices. We've abstracted most of this away now and it is packaged in the toolkits in such a way that you don't have to translate between the mouse controller and the UI/Model.

It is also important to note that these are roles not objects, meaning a single object can have multiple of these roles. As an example, Apple's views have had a lot of controller functionality in them since forever. Which is why the appearance of "ViewControllers" really confused me.

At best I can tell there were two reasons for them: first, to implement all the "remaining" functionality that couldn't be handled by the hooking up widgets to CoreData-generated entities using bindings in Interface Builder. Alas, that turned out to be quite a bit. Second, as a memory optimisation in early iOS, as placeholders for Views that could be discarded and recreated on-demand.

Neither of these is relevant today.

See also:

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

https://blog.metaobject.com/2015/04/reactnative-isn.html

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

https://blog.metaobject.com/2022/06/blackbird-simple-referen...


MVC discussions makes me miss state machine minimization and linear algebra.


for simple web application backends, why does one need more than just "models" (structs, that describe database tables/rows, using something like "an ORM" but more lightweight, so as to provide query building, caching, etc.) and "routes" (procedures, mapped to URL patterns, that execute logic such as accessing models (cached or queried) and then responding with output from a simple HTML template system, or possibly a cached template rendering, or JSON)?

I was never trained/educated in the MVC pattern but back when Rails was new and I was trying it out, it just seemed like a lot of unnecessary boilerplate, and it was cool that Rails helps you scaffold it out and all—but why?

I'm working on building a framework that is more or less what I describe above, and so far it's going great. I can make simple CRUD apps with it pretty dang easily. I don't really have an interest in using React or whatever the newest frontend framework is to do fancy front-end stuff. is there something I'm missing, or does my approach seem reasonable? I just don't really see the need for having separate Vs and Cs when it comes to web backend things.

this article helped give me a bit more context as to where these ideas came from, and I appreciate it, but as of right now, as the article itself and some other commenters have mentioned, it doesn't seem to make sense to take this MVC pattern as-is and apply it to contemporary backend web development. but, I've never built websites from scratch that were bigger than "small to medium", so it's possible there's something I'm missing?


You don't need more than what you have. You have essentially built a minimal MVC. Everything else you add is just stuff that other people added to other MVC projects to make life easier for them in some way. Web MVCs are basically just opinions on the way to handle HTTP requests and are meant to speed up the dev process if you agree with all of its boilerplating/tooling.

My only recommendation is to just keep yourself aware of url/form encodings, path handling, and common pitfalls related to handling HTTP requests in general.


cool, thanks for the insight. are there any materials you'd recommend for reading up on this kind of stuff? I have a fair bit of insight from having done things in various frameworks over the years, but if there's any resources you have in mind, I'd be more than happy to take a look.


I'm using Sveltekit and doing pretty much what you're doing, and never touched Rails before. I'm equally confused about the MVC pattern I hear so much, and why it was so big back then, and why it isn't very big now. Was this more of a design / architecture "fad" or a design around the constraints of the time?


MVC offered a formalized separation of concerns for data, ui, and business logic at a time when many developers were cramming all of their application code into massive php or C# files. The pattern was delivered via frameworks (RoR, Cake, Asp.net MVC) that handled a lot of the boiler plate for you and made for an easier development experience than what most of the people switching to it were used to at the time. As web applications became more focused on the client code, the template driven server side rendering capabilities of these frameworks was less useful or no longer necessary.

Initially when the mindshare began to move to the client code, front end tool chains actually did do a lot of hemming and hawing of what variant of mv* they were. Eventually tools like react came around and whatever variant of mv* they were wasn’t what was interesting about the building paradigm. More or less that’s where we are now.


one my wonder about what could this be:

> routes" (procedures, mapped to URL patterns, that execute logic such as accessing models (cached or queried)

A "route" that executes logic such as accessing models responding with an output based on what mime type was requested, can set a layout or template for the view ...

changing the name to "route" does not change the pattern that you are implementing here.

Rails Controllers are doing exactly everything that you describe here with the only exception that there is a big table that keeps references between a URL and the controller method associated with that URL.

I am not saying Rails is better than what you build, but just noticed that a lot of what you describe as a "route" is matching what a controller is doing.


it's been a very long time since I did anything with Rails… but I recall there being Ruby code in the "Views", which is something I'm choosing to avoid entirely. all logic is executed in the "Route" (or whatever it's called in the end) procedure, which is where, if you want to output HTML, you invoke a "Render" macro that takes in the template name and optionally any variables you want to pass in. the HTML templates have a simple, Mustache-like syntax (insert parameter, insert nested template/"partial"), but it's just plain HTML with some {{placeholdery-looking stuff}}. maybe I'll add a JSON "renderer" if I need it, and I still need to get static file loading to work, but other than that, this framework does pretty much everything I want to do as far as website backends go, and it compiles to a single executable. it's not going to be The Future Of Web Development, but it will be nice (for me at least) to make small-to-medium-sized websites with, in large part because it's much easier for me to reason about "Models (structs), Routes (code), Templates (HTML)" than "MVC", or at least, how I remember "MVC" feeling in Rails, and Django.


If you are building a framework to do that, you are probably just reinventing React


how so? I was under the impression React is a frontend framework, and I'm exclusively talking about the backend here.

(plus, my thing is compiled to a single executable and not using JavaScript.)


React is just a way to procedurally generate templated HTML in the frontend. You can do the same thing in the backend but it just makes rerenders more expensive which could matter depending on how input heavy your application is. For example, if you update a row in the table, do you have to reload the entire page or just the row?


my bad, I wrote "web application" above—I meant to say "website", like, year-2008-level-of-interactivity stuff. I'm neither interested in nor proposing a solution for anything on the frontend side of things at all, because I'd suspect you're correct, I would probably just reinvent React et al.


In the referred paper the Controller sends messages to the View but never interacts with the Model, so the diagram in the article incorrectly represents that concept.


As a side note, taking the model out of the equation, the split between controller and view is very similar to command-query separation [0], in the original MVC.

[0] https://en.wikipedia.org/wiki/Command–query_separation


I did long forgot the MVC stuff...


The Rails-style MVC is very very similar to the WebObjects MVC, which came from NeXT then was at Apple (and may still be used internally). Wikipedia says the first public demo of WebObjects was 1995.

Original WebObjects tried to ignore the web's statelessness by keeping state on the server, and encoding state in a path param in every URL. It was not exactly designed to fit a stateless environment as designed to let the developer ignore the fact that the environment was stateless, put an abstraction on top that made it appear stateful. But maybe that's semantics. But later WebObjects, towards the end of when it stopped being a commercial product, grew out of this -- it was in fact a very bad fit for the web. Encoding an opaque identifier in every URL to connect to back-end state is bad for cache-ability, idempotency, persistent URLs, etc., it's not what Tim BL or Roy Fielding wanted from URLs at all.

BUT, despite all that (differences in "routing", URL-to-app mapping), as far as the MVC break-down, the original WebObjects was still very much like the architecture of Rails, to the extent that I've often wondered if the Rails originator(s) were exposed to WebObjects.

But, then, I'm not sure that this architecture, actually was motivated by, as OP says: "but in a way that makes sense for the web. The components don't have references to each other, because and nothing is alive. Each object is only around long enough for the page to be rendered" -- because the earlier WebObjects, as far as MVC split very much like Rails, in fact did it's best to keep the objects alive after all on the back-end, and provide abstractions that hid the statelessness of the web from the developer, connecting each HTTP request to the kept-alive-in-back-end objects, where things did have references to each other, or appeared to, that ought to have not been possible. But still had the same MVC breakdown that could be diagrammed the same way.

Actually here's a 2001 version of the WebObjects manual, note they are pointing this out as different than classic MVC, but it is very much like Rails too.

> Note: WebObjects uses the term “model” differently from MVC. In WebObjects, a model establishes and maintains a correspondence between an enterprise object class and data stored in a relational database. In MVC, model objects represent the special knowledge of the application.

https://developer.apple.com/library/archive/documentation/Le... page 28

More on MVC in WebObjects there too

> Acting as a mediator between Model objects and View objects in an application is a Controller object… Because of the Controller's central mediating role, Model objects need not know about the state and events of the user interface, and View objects need not know about the programmatic interfaces of the Model objects.

Familiar, right? And different from what the OP is saying was original MVC; maybe consistent with both what OP calls Apple-style as well as Rails-style MVC?

I haven't found an earlier dated WebObjects manual online (such as actually from NeXT days!), but they definitely existed, would be interesting to see how they referred to MVC.

You can see a little bit about the weird ways original WebObject tried to encode state in URLs, mapping to persistent back-end state, in this other legacy WebObjects doc I found on the web, see "Component Action URLs" on page 12, and following through page 17. https://wiki.wocommunity.org/download/attachments/1049043/We...


A bit pedantic.

It’s just a design principle not a scientific law.


It’s often unclear (and people have different opinions on) what exactly that principle actually is, though. You can’t just say “MVC” and have everyone be in agreement on what that will mean.


That's kind of my point though. It _should_ be a high level thought pattern and not a strict interpretation.


I guess that’s fine, but it’s certainly not what I would call a principle, because I wouldn’t be able to phrase it as one.


He does say who cares at the end but I def agree was feeling this the whole time


The problem with MVC is not MVC, it's the problem with documentation. Who's the caller for the dependency chain ?


My take on MVC is that it's an architectural pattern to decouple state, state changes, and state usage.


Responsive UI-driven apps these days use MVC backend => frontend MVC app context loader => frontend MVVM application layer.

https://learn.microsoft.com/en-us/dotnet/architecture/maui/m...


MVVM uses two-way data binding. By now it is widely acknowledged that one-way data binding is better.


I wish I was smart enough to understand what this really means, I often feel very much like a code monkey when these discussions come up.


In the old days there was just MVC. Then Microsoft announced successor to Windows Forms... a new technology called WPF. WPF uses MVVM which automated more of the work that a developer has to do: it automatically updates the UI when data objects change, and it automatically updates data objects when the user inputs something through the UI. Sounds cool, right? The problem is that in large projects it becomes hard to understand how data is flowing. Because of excessive automation things are happening behind the scenes. Developers began to understand that this is not as cool as it initially looked. React re-introduced one-way data binding, which updates the screen when data objects change, but not the other direction. People found that they are significantly more productive with this technology even though it does less on behalf of the developer. Now it is widely acknowledged that one-way data binding is better.


... WPF has built-in one-way databinding. You choose whether every control is one-way or two-way...


And REST isn't REST.

Words and phrases change, no semantics survive first contact with the enemy.

Literally used to literally mean literally, now it doesn't.

Grieve, and move on.


convoluted nonsense


TLDR: So what, indeed? Does any of this matter? I mean, not really.


To get anal

If you use any SDK and or framework you do not have MVC

you instead have

framework-SuperController | MVC


> To get anal

Buy them a very nice dinner, at least.


What MVC refers these days is not of MVC from 1979 but from the Design Patterns by Gags of Four and they did not mean the patterns should be applied strictly as is. So.. what? What is the point of the article? He does not seem to really understand the purpose of design patterns and it in practice


I would upvote for 'gags of four'.


A visitor walks into a bar...


MVC isn't a Pattern in GoF.




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: