
Maria: An MVC framework for JS - shawndumas
https://github.com/petermichaux/maria
======
ericingram
I've been developing on my own MVC patterns for 6 years and have come to a
similar position regarding controllers. The primary advantage of a controller
is to decouple request/response/ui "logic" from the view, but I've only found
one or two cases where this felt useful, and even then it was mostly to test
out the use case. In reality, interoperating multiple views and controllers
can result in a very confusing view code, and perhaps negates the benefit.

For almost a year I have been writing web apps with no controllers, and it
feels right. So far so good. It simplifies routing and a couple other things.

That's not to say you can take the avg MVC framework and drop the controller.
A couple new patterns were really important to keep things sane. For example,
there is still technically a default controller that does the default request
handling for views. Happy to share more if anyone is curious.

~~~
nicklovescode
I'm curious

What are patterns you mentioned?

~~~
ericingram
Perhaps the most significant new pattern is that domain models communicate
with a REST interface to the views. Usually you justify procedural coupling
when writing controllers that interact with models, and it seems to make sense
because controllers and models are often written in similar style and syntax.
But still, it caused me a lot of pain in the past.

So now, the models only expose a REST API beyond the "class wall" (so to
speak), which feels rather natural when called from views in template syntax.
Client-side frameworks do it this way already, but I'm talking server side.

A great side effect of models speaking REST natively is, it becomes trivial to
create a public API, just add authentication.

There's a few other things, like automatic request view routing.

------
fzzzy
When I first became interested in mvc, it wasn't quite as buzzwordy as it is
today, and so I went and read the original smalltalk source. What I discovered
was that the role of the controller in smalltalk was to _poll the mouse and
keyboard_ in a tight loop. When I wrote webmvc (which became woven, which
became nevow) I found, as I think many people have found, that the controller
doesn't really have as much of a place on the web, and nevow ended up not
really having controllers associated with views but rather the logic
associated with a controller was placed in methods on the Page object.

I'm curious what role the controller objects play here.

~~~
fzzzy
Answering my own question: the controller object contains a bunch of on*
methods which seem to handle the raw browser event objects and manipulate the
model and view accordingly. This makes sense.

However, the way the methods are connected appears to be the uiActions
attribute of the view object, which seems unnecessarily tight coupling between
the controller and view. Also, the example code makes the view code
responsible for querying the form, when it seems this should be the
responsibility of the controller.

For some reason I still have a gut feeling that the view/controller split can
allow generic views with custom controllers or generic controllers with custom
views, but in the real world it seems the view/controller separation is still
extremely fuzzy.

Morphic, the ui framework used by squeak, does away with the controller
concept, leaving input handling up to the Morph (view) which is an interesting
data point.

~~~
petermichaux
fzzzy,

Thanks for taking a look at Maria. You definitely got the gist of how things
work with Maria but there are a few subtleties that might change your
perspective on the relationship between views and controllers in an app built
with Maria.

If a view has the following

    
    
        maria.ElementView.subclass(myApp, 'MyView', {
            uiActions: {
                'click .alpha'   : 'onClickAlpha'   ,
                'mouseover .beta': 'onMouseoverBeta'
            },
            ...
    

That auto-generates two methods on the _view_ that forward handling to the
controller.

    
    
        myApp.MyView.prototype.onClickAlpha = function(evt) {
            this.getController().onClickAlpha(evt);
        };
        myApp.MyView.prototype.onMouseoverBeta = function(evt) {
            this.getController().onMouseoverBeta(evt);
        };
    

So it is actually the view that handles the DOM events.

If you don't want the controller to handle the raw DOM event or you don't want
a controller involved in handling the event at all, you can define the handler
methods on the view yourself and then the auto-generated methods will not be
created. For example,

    
    
        maria.ElementView.subclass(myApp, 'MyView', {
            uiActions: {
                'click .alpha'   : 'onClickAlpha'   ,
                'mouseover .beta': 'onMouseoverBeta'
            },
            properties: {
                onClickAlpha: function(evt) {
                    // not sending evt to the controller
                    // and controller method name is different
                    this.getController().onSomething(1, 2, 3);
                },
                onMouseoverBeta: function(evt) {
                    alert('no controller involved here');
                },
                ...
    

Also about who should query the form data: the view or the controller. I
believe the view should be the only player in the game that knows about the
DOM. If the way that the form data has to be retrieved from the DOM changes,
then only the view needs to be updated. This makes sense to me as the view is
the one that creates the form so the view should encapsulated all access to
the form.

In the real world, the view/controller separation can be fuzzy. With Maria you
can easily live without any controllers and have the views handle all user
events _or_ you can go wild with controllers and have all kinds of
interchangeable view behavior thanks to the flexibility provided by
controllers and the strategy pattern.

~~~
fzzzy
Thanks for the excellent reply!

I'm still interested in hearing a convincing argument for decoupled
controllers. Your statement that the view should be responsible for DOM
interrogation rings true to me, and seems to reinforce the idea that the view
should just handle input instead of splitting it out into a controller.

~~~
petermichaux
A controller and the strategy pattern allow a view to behave differently by
plugging in a different controller into the view. This interchangeability of
behavior is not something I've leaned on frequently in my own programming.

I am finding, while programming example applications for Maria, that splitting
the event handling out to the controller is forcing me to make better APIs on
my views to keep the DOM encapsulated. Allowing the controller to get the form
data, without exposing any DOM information, for example. The view code ends up
being very satisfying in a way I haven't experienced before.

What I'm hoping will happen is that after using strict view and controller
separation for a while, I'll start to get insights where the strategy part
could be used more. I think its the kind of thing that will sneak up on me
over time and I'll start to see more uses for it.

~~~
fzzzy
Are not the event objects technically part of the DOM, though? It seems to me
the view should unpack the event objects, and then delegate to a controller
method, passing only non-DOM-related data. The controller then contains the
"business logic" which is what everyone seems to put in controllers anyway.

~~~
petermichaux
You're right about the event being part of the DOM. If that is a concern, you
can define your own view handlers that unpack the DOM event and forward the
data to the controller handler. There is no way to autogenerate view handlers
that know what needs to be unpacked in what way.

------
nicholaides
There are so many variations of MVC and this is a good thing. The important
point of MVC is to separate concerns properly. One reason for the many
variations is that there are different concerns when in the browser vs on the
desktop vs in Silverlight/Flash.

For example, @fzzy mentioned the controller in smalltalk being used to poll
the mouse & keyboard. In the browser, you don't have the same issues-- you can
listen for events to be fired. Also, for example, in the browser, you have the
DOM, which is communicated with and manipulated differently than UI widgets on
the desktop.

You'll find that all the MV* patterns (like MVC (in all its variations), MVP,
MVVP, etc) have the concept of the domain model, and some object that's
tightly coupled to the UI/DOM, and usually some middle layer. As long as
you're using one of these types of patterns you'll least be able to take
advantage the benefits that this family of patterns provides.

------
jamesladd
I have been using Maria on my production App for many months and it has made
life easier. It is simple, tight and well written.

I use controllers to implement the strategy to be applied to user events, and
this keeps my code focused (SRP/DRY).

I think removing the controller is a bad choice as it would require the view
to know too much, and besides having the controller in the mix is made easy by
Maria. Any time I have diverged from using the Controller the code started to
deteriorate with views having to handle increasing responsibility.

Yes - MVC is part of a larger set of patterns and the thing I like most about
Maria is that it focuses on being just MVC. Now I'm free to look for
implementations of other patterns as I need them. Hopefully from Peter as
well, as he really understands a pattern before he implements it.

\- James.

~~~
Osiris
Have you used other MVC frameworks like Backbone (MVVC), Ember, or Angular?
How does Maria compare?

------
DanielRibeiro
Cool. But MVC is part of a large family of patterns (which may be more
appropriate to specific domains):
<http://martinfowler.com/eaaDev/uiArchs.html>

~~~
gfody
This is a nice link. Would be nice if they also included naked objects and
MVVM.

------
Sakes
Looks very clean and true to JS. Quick question, why is your casing different
on your file names?

~~~
petermichaux
Which differences? You can use any file system hierarchy you like with Maria.

~~~
dangrossman
SetView.js vs borrow.js, Controller.js vs borrowGrail.js. Your capitalization
of the first character of a filename is inconsistent.

~~~
petermichaux
SetView.js contains a class called "maria.SetView". borrow.js does not contain
a class but a function called "maria.borrow". Regardless these files all are
concatenated to become a file simply called maria.js.

------
NanoWar
WHAT a title... :) I just had to click it.

