I agree with the author that components are the true innovation of React, because it encourages reusable building blocks by default. Contrast with classic desktop and mobile UI toolkits which, while often also using or encouraging MVC, do not require the developer to not subdivide their own codebase into reusable widgets. Instead, they allow composing an entire screen (window, page, form, whatever) from the built-in widgets. Making a reusable widget is possible but extra work and therefore not done. In React, it's the only way to go.
This is awesome about React, but it has nothing to do with data flow architecture, which is what MVC is.
The mistake webdevelopers made for years was trying to shoehorn DHH's backend remix of MVC into the frontend, throwing away decades of UI building architecture knowledge. I'm happy the Facebook people rediscovered MVC and I'm even happy they gave it a new name (Flux) because MVC frankly has gotten way too many definitions.
But saying that Flux/Redux killed MVC is like saying Clojure killed Lisp.
Then, user actions are handled in the Controller. In our case it's just a bag of functions. The Controller fetches and pushes data from/to the backend, and sends appropriate actions to the store. This allows us to keep all the backend data fetching/manipulation logic out of the View, which keeps the View clearly focused on the UI and the UX, and nothing else.
Our controller does not have read access to the store: any data the controller functions need to do their work is passed in from the view (which has the data anyway because it used it to query the store appropriately).
This works great, it's total unidirectional data flow, it's also pure classic MVC, and I haven't seen anyone describe it elsewhere. We came up with it 2 years ago when React was pretty new and I had little else than MVC to be inspired by.
If you have data molding code all over your views (and a little bit elsewhere too, for good measure), consider a controller.
For what it's worth, I think, we can define the whole Flux architecture in somewhat-skewed MVC terms. Not trying to downplay the usefulness of Flux here but if I'm not all wrong, it would've helped many people if we kept at least some of the terms from MVC or MVVM.
Since redux largely defines the design of the "Model" part and React largely defines the design of the "View" part, most of the variation when using React and redux is how to design your "Controller".
Not quite. Let me quote from "A cookbook for using the model-view controller user interface paradigm in Smalltalk-80" by Glenn E. Krasner and Stephen T. Pope, 1988, which according to Wikipedia defined the term MVC originally:
> In the scheme described above, views and controllers have exactly one model, but a model can
have one or several views and controllers associated with it.
All that said, they mean that you should use separate controller-view pairs for entirely different pieces of the application. A nice example would be MS Word's document editor (1 view with 1 controller) and MS Word's Print Preview view (same model, but a separate view and a separate controller).
What I was getting at is that in backend-MVC (and backbonejs-MVC), you usually build a view, a controller and a model for each domain entity (i.e. for each database table). This is fundamentally different from what Krasner and Pope describe, and it probably won't work well for the kinds of dynamism and interactivity most single-page apps are single-page apps for. I believe this is the flavor of MVC the author is talking about.
In our case at TalkJS, we don't have anything like a print preview - essentially our entire application is a pretty unified piece of UI (much like MS Word's document editor which, while complex, is pretty much inseparable), so we have only one controller and one root view. I made the thinking mistake that most UIs only have one root view, and of course that's not true. So thanks for that.
He tells the story on his homepage:
Sad because there are too many definitions? Nothing a new name can't fix!
If a new name catches on and puts the plethora of older names in the dust, then it CAN fix the "too many names" problem.
And Flux seems to have latched on well.
i still remember the MFC class library for Visual C++. it needed MVC for MDI applications where you have multiple document windows within one application window. Later MDI (and MFC) went out of fashion.
Later MS had ATL and WTL and here they had no MVC. i think not all C++ GUI class libraries were model/view/container, borland's wasn't
Also java class libraries for GUI did not force MVC. Awt and Swing don't do that.
Therefore MVC is not the only paradigm used in GUI frameworks.
So sure you can put all data management right inside your Delphi form, and plenty of coders did this. You can do that with React too. But larger applications often chose to go with MVC, where the appropriate fields inside the VCL components were updated triggered by changes in the model.
It's nearly entirely the same model. The view classes (Delphi components, forms, etc) can render (by setting attributes on widgets) and have event handlers (eg onButtonClick) which in turn invoke the Controller, which makes appropriate data changes in the model.
Nothing in Delphi enforces this, but it matches really well. Thne only big thing they missed was something akin to a Virtual DOM and shouldComponentUpdate, making figuring out which fields to update when cumbersome. But there's been plenty older solutions to this problem (such as damage/repair).
There were complex components that did transformations on the data for reporting or complex grid displays, etc, but generally you didn't see an abstract formal model behind forms/views. If you did model outside the DB, you did it in such a way that the model looked like a database table or query (subclassing abstract DB proxy components the VCL gave you) and used components directly bound to that.
So, I guess in MVC terms, what you had was a form implementing View logic with components directly working from DB or DB-like data, and controller code behind the form doing event-driven manipulation. The controller code could hook data events too, and in that way reacted to "model" changes as well, but the "model" was actually hidden in DB bindings and components. For any model business logic more complex than you could handle this way, middleware was usually pushed.
This may have progressed since the early 2000s when I lost track of Delphi, but the idea of directly-bound display components was typical of the dominant DB-driven 4GLs of the time, including Powerbuilder and VB.
People compose an entire screen from built-in widgets because usable widgets and layout algorithms make this possible. If you are trying to build more complex screens, people in "classic desktop and mobile UI toolkits" also build own widgets.
Projects like Material-UI are finally dragging frontend development into the 1990s of "classic desktop UI toolkits", only the layout functionality is still sorely missing.
I just think that as opposed to the original idea, people started to stuff way too much into their controllers, which is what makes things messy. I agree that Flux helps enforce the way it was supposed to be.
Classic MVC is really MVc, with controllers only handling a small set of interactions that are not directly between the Model and View, for example dialog boxes and such.
One problem with a "Big-C" approach to MVC is that whereas models and views are at least potentially reusable, the controllers are dependent on both M and V, and thus both proliferate and are not reusable.
Glue code. The dark matter of programming.
Everything right now is just MVC-like. Yes you have some unidirectional PUB/SUB here or some immutable data structure there.
So what ? In the end, you still have the separation between the data, the way you manipulate the data, the way you display the data and a canal of communication for those.
This is the essence of MVC, and what everybody is doing right now is just a variation of it.
You could say Redux is a more constrained subtype of the 'single data flow MVC' you describe, which in turn is a subtype of 'all definitions of MVC which have existed'. In Redux actions are serializable, which opens up a bunch of other possibilities. Not to say Redux invented that idea by any means (it's the FP principle of separating data and behavior, also the OO command pattern), but it's useful to have a name specific name when talking about a more specific combination of ideas/constraints, especially if those constraints make different things possible (undo/redo, replay, logging/analytics for free).
Secondly, MVC was certainly not invented in 1988. It was a product of the work at Xerox PARC in the late 70s.
It's worth pointing out that Dan Ingalls and Alan Kay and other pioneering people involved in Smalltalk ended up 'repudiating' (to use a strong phrase maybe) MVC in later versions of Squeak (which is a direct descendant of the original Smalltalk-80 implementation) uses the "Morphic" GUI system which was built for Self at Sun in the 80s.
Morphic is a prototype oriented, direct manipulation system.
None of this has historically translated well to the web, where the HTTP request cycle and the nature of the DOM and JS execution model changes things significantly. MVC was never a good model for the web.
But i agree you could fit React in the MVC pattern. Only the view is purely functional.
That's basically what Backbone was and yes it led to a fn disaster.
The point is to not munge all these things into one monolithic horrible grim mess. As long as you are separating the concerns of showing something to a user, allowing them to control it and backing it all with potentially remote service who cares what the pattern is precisely called? Its still flavours of this original desire that we named MVC.
All these presenter/unidirectional patterns are _just_ the underlying desire of MVC and the only reason that people seem to talk about how "MVC isn't right" or "is dead" is because they've followed MVC like dogma instead of just a guideline of separating your presentation, control and service logic. I had exactly this debate back when everyone was talking about MVP as if it was some revolutionary new thing.
Its not, its all the same thing and GOF was never supposed to be a template for software but a way of talking about specific ideas that architects could then riff on. They're chords, not one specific tune that you _must_ play in a very specific way.
Perhaps the problem is that rules like 'no SQL in your views' is now so ingrained, a lot of juniors have never seen a monolothic mess of SQL mixed with HTML. So MVC is almost ubiquitous, and we then try breaking it down into different sub-categories.
"Unidirectional architecture" is a weird name for what is a fairly standard abstraction. Every front-end at the top level is:
f(my_entire_state, some_event) -> my_entire_state'
Front-end development has been plagued by unclear patterns w/ weird names (MVC, MVVM, MXYZ...) since forever; everytime the patterns are criticized you hear "you did not understand it"; and new names keep popping up. It seems the industry is stuck remixing reasoning around nouns, and is unable to step back and reason around data.
Sprinkle some CSP to get elegant concurrency, throw away callback-hell.
Sprinkle some React to get fast DOM manipulation, throw away Flux (heresy!) - it has way too many names to worry about (action creator, action, dispatcher, callbacks, stores, store events, views) and encourages some bad practices around use of stores.
My $ 0.02
For examples of that sort of thing, David Nolen and James Long have written some really great articles on the subject:
There was also an article recently that described a flux-like architecture using channels that I found pretty interesting, but I can't track it down right now.
In particular, I have yet to see any examples of integrating the CSP approach with React or similar architectures. For example, can you hook a process-based autocomplete widget into a React view? Is anyone doing this or have we rejected CSP for UI logic? I am interested if you can find article you mentioned.
For example, I regularly see subscriptions deeply nested in the component hierarchy, instead of more 'pure' components that just accept data at the top level. This leads to controller-style logic spread all over the app. I see the same issue with events - dispatch being called from inside view components, and despite the event abstraction, what you basically end up with is mutable state spread all over the app.
Happy to admit these apps probably aren't shining examples of react/reagent/re-frame architecture, but I'd be interested to see some canonical architecture guidance.
I'm still not completely convinced by React, and I may very well be wrong, but the same skepticism that sometimes makes me feel out of touch, also provides some sanity in this madness.
For some reason that I can't quite point out yet, Vue.js feels like the nicest one yet, though I've only played with it briefly.
Most projects I start these days tend to use React. I've looked into Vue, and liked what I saw. I also maintain a 5 year old project built with Backbone.js (by someone else) and I'd say it checks all the boxes for maintainability, stability, and testability. It has its flaws -- as any old codebase will -- but I'm happy they went with Backbone. I'd probably be saying the same thing for the other frameworks mentioned.
Its easy to point out all the flaws from our past. Much more difficult to predict how thing will be in the future. Making a choice now, even the wrong one, is almost always better than clinging to things that are known to not work.
First time I saw jQuery: Crazy easy and widely compatible, Ajax, selectors and animation - sold!
Managing UI state can be complicated, but to me, not enough to justify the amount of complexity and abstraction added by React.
If you organize your code so that only a single function can act on a specific block and emulate OO/namespaces in CSS, instead of attaching functionality to every click and overwriting stuff with !important, you'll be fine. It's not that hard, but maybe that's just me.
It's nice that the Virtual DOM is really fast (even thought I don't usually need that much speed) and that I can render it on the server (even though you can probably kiss your semantic markup good bye) and one way data binding seems a good idea, but again, I don't think it's a problem I have.
Most all web developers of that day were new to programming (experienced folk didn't take JS seriously), so naturally they did not know how to write well-designed software. The benefit of Backbone was not Backbone itself – it was the community push for thinking about code structure in general.
It even came to the point that some people reject using any frameworks or even a helper like jQuery and "take everything under their control" because they don't want to deal with "deprecated" libraries.
Meanwhile, a small development team I know keeps using Knockout.js successfully in huge projects.
Vue-router is what really got me into it. Again, it's similar to UI-router for ng1, but destroys it in simplicity and readability. I'm using their vue-webpack template to rebuild my site and it's going great.
Personally, the best thing for me is that a component gets nearly everything it needs in one file. Template, script, and scoped styling? Exactly what I've always wanted. Forgive the gushing review, but honestly, I haven't ever been this excited about a technology. Not ng1, not webpack or es2015, or [fancy new tech]. This shit just works.
does weird stuff, they don't know why or how.
The abstraction gives you hope that you won't have to worry about the low level details, it will be so easy.
But almost invariably, the abstraction breaks down or acts weird, or some low level error or limitation bubbles up, or the abstraction has horrible performance for some edge cases.
So now, where you had 1 problem - the low level thing - you have 2 problems - the low level thing, and the abstraction.
You don't have to be a uber-expert in the low level thing but it's really helpful to be comfortable with it.
I see it with ORMs - 'wow this is magic, I never have to do SQL again' - 'oh crap, a weird edge case, I have to do SQL' - 'oh crap, why is this so slow, guess I have to look at the generated SQL, and maybe hand-roll my own'.
(Not saying you shouldn't use abstractions, they can save a lot of work for the central cases where they work right.)
I remember the complex UIs I and others wrote a decade ago with jQuery, now those aren't complex at all compared to what is common now. I feel that it isn't quite fair to say that back in the day we got along fine with just jQuery.
Speaking of Backbone, I was already more or less organize my code like Backbone for a year when it came on the scene. Then there is the question, can I really program a custom framework better than an established framework?
I can guarantee that MVC is still used on the fronted as on the backend. People are still generating value from MVC apps.
Perhaps it's not held up as the holy grail, since the new holy grail is pronouncing it dead. Perhaps it is dying. Dead it is often not.
After building out some front-end MVC work, I can see the value in Flux architecture and React components. I'd have a hard time justifying a rewrite though. It's still very much alive there for me. That's also the case for many others.
DOMStream = view(model(intent(DOM)))
model() takes streams of interactions, wires them up with data retrieval and mutation streams and returns these data-streams
view() takes the data-streams and uses them to create DOM mutations-stream, which it returns.
The nice thing is that these observable streams are really nice to filter, map, debounce etc. Also, it helps with fast realtime data stuff, because you can easily wire up these fast streams with a tiny part of your app and the rest of it won't even notice (which is a bit ugly if you got a big state-tree that represents your whole app state).
The not so nice thing is, that controlling them completely declaratively has a steep learning curve.
I think that's called a controller or presenter in other patterns.
Actually your view and intent also do what a controller would do. The DOM is your actual view, and your data your actual model.
In every function the same is happening, streams are created/wired up with other streams.
The differentiation here seems to be, that the Model function just wires up streams for the data, the View function just wires up streams for the display and the Intent function just wires up streams for the interactions.
Model = the state,
Controller = the update function changing the state based on action.
View = declarative, need to send actions to change the state. Gets redraw on state change.
The idea of MVC, as far as I'm concerned, is to separate the View (Declarative as much as possible), the State (Just data, no logic) and the Controller (Business logic receiving commands/actions/called/whatever which changes the states and let the view knows that it needs to update).
Is that pattern dead? Far from it.
One thing I didn't mention is that I use MVC per component, not for the full application. (I use something else for the global Application level).
So, every major component has its own MVC. I.e. One page on mobile listing a items with a bunch of interaction would have its own MVC.
And why it's robust and efficient:
Very easy to reason about the data and write unit tests. The whole data is in the State, and only the controller can alter that state.
The view is completely decoupled because it can't change the state, it can only declaratively render something (we use React with immutable). And this is also very easy to test and mock with fake data.
As for performance, I've tried various strategies over the past few years, and I find Immutable data structure + virtual Dom pretty damn amazing. I personally use React.js + Immutable.js, mostly for the great documentation but this is definitely not the only libraries.
And if the view becomes that much more complex, then it's time to spawn a new component with its own MVC.
As the components don't hold state, only display the state of the model (part of the redux store state), components only need to be re-rendered if the state has changed.
If you combine this with reselect (https://github.com/reactjs/reselect), then this allows you to only re-render components when the sub-set of the state tree is changed that can affect your component.
So to a certain extent you're registering your component's interest in a sub-set of the state, and then only re-rendering when that sub-section changes. The trick is then to ensure that as you build your app you don't build a single component that depends on everything and pass state down as props, but lots of smaller components that depend on a small sub-set of the tree.
But to answer your question, it shouldn't matter if subcomponents reference the state in a random-access way. Our workflow is like this:
a) Main components fetch data from an external module. (That module either queries the server, gets the data from the client database or uses something already in the memory cache).
b) That main component generates a ModelView state. Basically, it transforms the fetched data into something it can use. It could mean joining models together, etc.
c) The view uses the ModelView state to render itself. Each component generates a virtual dom representation and then renders themselves in the DOM.
d) Now, there are two different ways this ModelView state can be altered:
1) From a global event (eg. New changes came from the server. Or a component elsewhere in the app sent a global event.)
2) From the component itself (eg. An event/action is sent from the view)
C, in the front-end world, has less of a direct equivalent, but can be thought of as your router, which React also has as a separate entity.
What MVC does not have an opinion on, is that the M is really one giant object, where each component receives a sub object of it. Where MVC can go wrong is when many objects have dependencies between their state. React comes along and says that two objects with a dependency should derive this shared state from something above them, not inside them. This is a useful pattern. It is still merely an opinion on how to organize Ms in an application. It is not something "different".
In functional programming, in its purest sense, there is no M. That's different.
When I was growing up coding native UI apps, MVC was all about front-end. UI toolkits were traditionally MVC or M(V+C) going all the way back to SmallTalk, and "server-side" typically meant apps without "V" or a "V" that was so small and hardcoded in without separation...
I think the XAML approach was ideal, where you had a design view that showed a rendering of your view and allowed to make property changes and D&D controls, but also had the source to properly use grids and other layout managers to align and resize components.
although I'm not sure how many more there are that aren't hosted directly in the browser, as opposed to a standalone IDE that runs on a desktop OS.
I am not saying I don't want to write a single line of code, just saying that the UI components can be assembled and "configured" with a design tool.
I started calling it Model-View-Store recently as I think that best describes it. There are a few unique things here that I think are valuable.
Starting with Models: Models all return observable values. So if I query for a single record I get back an observable object, or a list, observable array. I define `@query` decorators on the models to set up these queries. Model's include prop validation, normalization, consistent operation names, and more.
Views come in two types: application and presentational. App views are all decorated to automatically react to mobx observables, so you can literally have a Model `Car` and in a view call your query `Car.latest()`, and your view will trigger the query and react to it accordingly. One line model <-> view connections!
Then you have Stores: they are just logical containers for views. Any time a view needs to do more than some very simple logic for a view, you can attach a mobx store to it with very little code. Stores also can manage the data from the Model (and because the Model returns observables, this is usually a one-liner). But they don't have to. Stores are located side-by-side with the views they control (and are be passed down to sub views when needed).
I've been working on this system for a bit now along with our startup and we've been able to deliver some pretty incredible stuff very quickly. Having a consistent model system is crucial, I can't imagine programming without having prop validation and a single place to look for my model queries.
Going to be releasing pieces of it over coming weeks and hopefully a full stack example thats really legit soon.
MartinFowler DDD blogs: http://martinfowler.com/tags/domain%20driven%20design.html
You have Model, View, ViewModel, and whatever auxiliary libs those need.
"The unidirectional data flow approach that’s in the spotlight right now thanks to Facebook is actually pretty darn close to what “real” MVC (as in the design pattern introduced first in Smalltalk decades ago and not the “server-side” appropriation that frameworks like Ruby on Rails popularized) is."
People have noticed that views and controllers tend to come in pairs, and experimented with how to set up the controllers to receive events without creating excessive coupling.
People have noticed that you often want some sort of intermediate state derived from your model to support your view rendering, instead of regenerating expensive data from the model itself each time you render.
People have noticed that rerendering everything can be a bottleneck and developed tools for identifying only the changed areas to render selectively.
Tune in next week for: Immutable data structures are relatively slow. Large-scale declarative rendering is relatively slow, even if you use diffs. Fine-grained publish/subscribe models are relatively fast, and flexible enough to cope with both computing derived state and triggering UI updates, but you need good tools and a clean design or the edge cases will overwhelm you. Small-scale declarative UI updates triggered by a fine-grained publish/subscribe model is a useful approach in a lot of cases. And everyone hates temporary/transient state in forms.
If a UI has light to moderate rendering requirements, React's approach might be fast enough on its own. In that case, a lot of the other ideas are just extra complexity for no real benefit. I'm speculating here, but I'd guess this actually accounts for a large majority of the web front-end work that is being done today.
I haven't found that React alone scales very well to more demanding environments, though. If your model has significant constraints between the data points that need to be enforced or you need non-trivial view-state between your model and your rendering code, you're back to having dependencies in the data that need to be implemented somehow. That is outside React's scope, so something else needs to bridge the gap.
I find React's design itself also struggles with scaling up beyond a certain point, though it's a high bar that I expect most UIs running in browsers wouldn't reach. However, if you're implementing something like a complicated dashboard with many data dependencies and interactions, you start needing shouldComponentUpdate almost everywhere to achieve acceptable speed. That has profound implications for how other parts of your app are structured because you need some way to make shouldComponentUpdate fast, and it can also lead to more and sometimes artificial subdivisions of your rendering components so you can use shouldComponentUpdate at the necessary level of granularity.
Overcoming those scalability issues usually seems to bring me back to the approach I mentioned before for larger, more complicated UIs: data dependencies are handled through some sort of observer model and/or lazy queries, but a library like React is still useful for declarative rendering of each smaller part of the UI so you don't have to worry about transitions most of the time.
The web projects I'm working on are in a slightly different field. We also seem to be pushing the practical limits of browser-hosted GUIs with some of our interactive visualisations, but they tend to use SVG. WebGL is one of the technologies that is definitely on my "could be interesting/useful" radar, but I haven't tried to do anything serious with it yet, so I'm wondering how different a real-world-scale project would be.
You can also however use MVVM with kind of a spaghetti data flow or apply unidirectional data view without MVVM.
I personally like MVVM a lot, and it also fits very good to the latest UI frameworks (Angular2, Aurelia, etc.). I would also always try hard to achieve a purely unidirectional data flow. I however don't care about whether to use Flux, Redux or anything else for that. In fact traditional services (aka implementations of some interfaces) that encapsulate a specific set of state work very well for me.
It espouses a (I believe?) slightly more traditional MVC interpretation where your Controllers are usually extremely light and in most cases completely optional. It encourages a MVVMC (Model View View-Model Controller) approach to encapsulate view-state.
That being said, I don't know how to feel about Angular 2's approach to components. Decorators are useful but can diminish the benefits of component based architecture when misused.
OP is painting with an overly broad brush: Angular is not representative of all client-side MVC. I maintain an app where the view handles the browser events - as it should, and the only data that gets passed between the view and the controllers are the (business) models.
Saying MVC is dead is like saying "CPU" is dead, no it's not, but it will always keep on improving.
Other abstractions are designed similarly to allow tests against pieces of the internal API in total isolation, though separated on different lines. Maybe you have transient state stored in view models while persisted state is stored in models. The isolation only helps as size grows, and it can keep size under control mostly by having a good data model for what each component / layer / aspect actually does.
It makes it easier to rewire everything when you start with a switchboard. UI changes are either "Oh my god we're going to have to re-write so much stuff if we do it that way!" and you wind up not making drastic UI changes or "Sure, we can make that thing tickle the controller instead of that other thing."