MVC Isn’t MVC
collindonnell.com
collindonnell.com
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.
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.
The source is Trygve Reenskaug, the originator of the idea.
"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."
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.
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.
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).
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.
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.
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.
This sounds a lot like event sourcing.
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.
> 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.
> 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.
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.
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!
• 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.
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.
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 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.
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
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.
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.
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.
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.
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.
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.
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.
“You're very clever, young man, very clever," said the old lady. "But it's turtles all the way down!"
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.
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.
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.
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).
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.
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.
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.
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....
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.
- 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..
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?
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.
would you mind sharing your presentation?
But I’ll gladly upvote you everytime!
It is only recently that I started to realize this and once you do it makes a lot of sense.
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.
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.
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.
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.
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)
There is such a place: model
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...
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...
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.
• 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.)
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.
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.
What exactly do you think a design pattern is?
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.
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.
You know when something is that kind of thing when most discussion on it is about the definition.
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.
https://en.wikipedia.org/wiki/React_(software)#Unidirectiona...
edit: better link
- Dispatcher = Controller
- Store = Model
- View = View
And the actions are the messages sent from the user to the controller.
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.
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.
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.
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...)
[1] https://github.com/facebook/react/tree/015833e5942ce55cf31ae...
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.
> In December of 1979 _Tyrgve Reenskaug_, an employee of _Xerox PARC_
Later
>In 2004, a Danish man
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.
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...
"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.
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 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.
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.
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...
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?
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.
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.
> 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.
(plus, my thing is compiled to a single executable and not using JavaScript.)
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...
It’s just a design principle not a scientific law.
https://learn.microsoft.com/en-us/dotnet/architecture/maui/m...
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.
If you use any SDK and or framework you do not have MVC
you instead have
framework-SuperController | MVC
Buy them a very nice dinner, at least.