Shiny: MVC Architecture

Created on 30 Sep 2013  ·  5Comments  ·  Source: rstudio/shiny

Could I've an application developed with Shiny following Model View Controller pattern.
For example...
My Views -- > HTML5/ CSS/ JS.
And my Models and Controllers programed in R language using Shiny framework.
Mapping actions in some configuration file like this.
{
url: /mysite/path/action1?id=data1&n=1000
controller: ShinyController
action:Action1
}

I have file:
ShinyController.r

Action1 = function(id, n){
...
}

Most helpful comment

So, this merits a much larger discussion which is how Shiny as a web app framework is situated in and fits into the rest of the web app world. @jcheng5, I feel like at some point, we should write something along these lines... I'm just going to try my not-completely-well-formed-yet two cents here.

The two frameworks

MVC (and the subsequent idea of Separation of Concerns, SoC) has been the most popular web app framework until fairly recently. Nowadays, I think it is very much a tie between the traditional, imperative MCV frameworks and the more declarative, functional alternatives made possible with things like ReactJS (complemented by Flux/Redux) and Shiny's reactivity.

With Shiny, we've mostly decided to commit ourselves to reactivity and a declarative render style, because we feel like that is better suited for the kind of apps that our users want to create. There's some exceptions to this, like the brand new insertUI and removeUI functions, which completely break away from the normal reactive workflow and use a 100% imperative style. Still, Shiny is 99% reactive.

In the reactivity world, the controller becomes a blackbox

In traditional MVC, the controller is essential and explicit: you have to specify what to do when you receive user requests and what resources you are going to mobilize to carry out the necessary tasks outlined in the model. In the reactivity world, the controller becomes a blackbox: you no longer have to worry about the sequence of imperative mutations to the DOM. The framework takes care of that for you. You only need to specify how to render the view given a particular state. When that state changes, the framework will do the work of calculating the most efficient set of mutations that take you from where you are to where you want to be. So the controller is effectively hidden from the app author, who can rest assured that -- given the right "recipe" for rendering -- the frameworks knows how to process any user request.

This results in code that is more predictable. You just have to know the state of your application at any given time -- you don't have to worry about a series of imperative statements and how all those DOM mutations translate into a new state.

One-way data binding

Shiny has one-way data biding in a similar way to the single directional data flow that Flux has. On the other hand, MVC typically uses two-way data binding (between the model and the views) which does not necessarily scale very well. You can end up with hard-to-spot cascading and nested actions, which again makes code less predictable and harder to debug. Because of this difference, the very concept of a "model" is not as relevant or important in the reactive world as it does in the imperative or MVC world. I feel like "rendering instructions" or "recipe" make a lot more sense for reactivity.

But if you want to stretch this terminology, you could kind of say that the model is whatever is placed inside the render functions in the server code. When that code changes, that is reflected in the UI (for example, when an input$z - that is used in a render function - changes value, the app will re-render the corresponding UI element for that render function). But you cannot update the content of the render functions based on new UI.

For Shiny, what this means is roughly the following. In your server code, you can (and should) have:

output$y <- renderXXX({ 
  ## code that uses input$z 
})

But you can never have:

input$z <- renderXXX({ 
  ## code that uses output$y 
})

So inputs inform outputs and never the other way around. If you need dynamic inputs, you have to use either uiOutput and renderUI which keeps you safely in the reactive framework, or (if you know what you're doing) the imperative-style insertUI and removeUI.

SoC revisited

SoC is a concept that makes a lot of sense in the MVC world; it is less relevant in the reactive world. For example, if you want to be strict about this, you can say that ReactJS violates SoC because the same component often both controls the state and renders it. But these two frameworks are so different that this comparison seems a little unfair. Moreover, you can actually make that argument that the reactive world does SoC better than the MVC world in the sense that the latter frames SoC around three concerns: Model, View and Controller. In the reactive world, concerns can be defined in much broader terms -- and you can create a modular component to address whatever concerns you settle on.

You can still try to follow the traditional SoC in Shiny by doing all the Views strictly in the ui function and all the modeling strictly in the server function. You can be even more explicit with this if you use htmlTemplates. _However_, as hinted above, I think that a concept that is proving to be a lot more relevant in the modern web app world is not so much SoC, but modularity or componentization -- i.e. encapsulating each unit of functionality and reusing it as necessary. Shiny achieves this with modules.

Other resources

I recommend this video from Facebook Developers, which describes the shortcomings they found with MVC and how Flux and React were created to address these. While there are obviously differences with Shiny, it's a good overview of the two different approaches.

All 5 comments

Under the current design, I think the View is ui.R, the Model is server.R, and the Controller is embedded in ui.R and denoted by the input argument passed to server.R.

What you want is probably access to client data: http://rstudio.github.io/shiny/tutorial/#client-data Then it is up to you to define whatever controller you want. Not sure if that answers your question.

My view is that model, view and controller are all in server.

The view is output$ and input$

Controller is reactives and observers.

-Alex Brown

On Sep 30, 2013, at 2:30 PM, Yihui Xie [email protected] wrote:

Under the current design, I think the View is ui.R, the Model is server.R, and the Controller is embedded in ui.R and denoted by the input argument passed to server.R.

What you want is probably access to client data: http://rstudio.github.io/shiny/tutorial/#client-data Then it is up to you to define whatever controller you want. Not sure if that answers your question.


Reply to this email directly or view it on GitHub.

Well, I do not have a CS background, so I may well be speaking nonsense :)

Yeah you don't support MVC then. proper separation of concern would mean your business logic would be separate from the VIEW so 'ui.r' could not hold VIEW and CONTROLLER; that doesn't make sense.

So, this merits a much larger discussion which is how Shiny as a web app framework is situated in and fits into the rest of the web app world. @jcheng5, I feel like at some point, we should write something along these lines... I'm just going to try my not-completely-well-formed-yet two cents here.

The two frameworks

MVC (and the subsequent idea of Separation of Concerns, SoC) has been the most popular web app framework until fairly recently. Nowadays, I think it is very much a tie between the traditional, imperative MCV frameworks and the more declarative, functional alternatives made possible with things like ReactJS (complemented by Flux/Redux) and Shiny's reactivity.

With Shiny, we've mostly decided to commit ourselves to reactivity and a declarative render style, because we feel like that is better suited for the kind of apps that our users want to create. There's some exceptions to this, like the brand new insertUI and removeUI functions, which completely break away from the normal reactive workflow and use a 100% imperative style. Still, Shiny is 99% reactive.

In the reactivity world, the controller becomes a blackbox

In traditional MVC, the controller is essential and explicit: you have to specify what to do when you receive user requests and what resources you are going to mobilize to carry out the necessary tasks outlined in the model. In the reactivity world, the controller becomes a blackbox: you no longer have to worry about the sequence of imperative mutations to the DOM. The framework takes care of that for you. You only need to specify how to render the view given a particular state. When that state changes, the framework will do the work of calculating the most efficient set of mutations that take you from where you are to where you want to be. So the controller is effectively hidden from the app author, who can rest assured that -- given the right "recipe" for rendering -- the frameworks knows how to process any user request.

This results in code that is more predictable. You just have to know the state of your application at any given time -- you don't have to worry about a series of imperative statements and how all those DOM mutations translate into a new state.

One-way data binding

Shiny has one-way data biding in a similar way to the single directional data flow that Flux has. On the other hand, MVC typically uses two-way data binding (between the model and the views) which does not necessarily scale very well. You can end up with hard-to-spot cascading and nested actions, which again makes code less predictable and harder to debug. Because of this difference, the very concept of a "model" is not as relevant or important in the reactive world as it does in the imperative or MVC world. I feel like "rendering instructions" or "recipe" make a lot more sense for reactivity.

But if you want to stretch this terminology, you could kind of say that the model is whatever is placed inside the render functions in the server code. When that code changes, that is reflected in the UI (for example, when an input$z - that is used in a render function - changes value, the app will re-render the corresponding UI element for that render function). But you cannot update the content of the render functions based on new UI.

For Shiny, what this means is roughly the following. In your server code, you can (and should) have:

output$y <- renderXXX({ 
  ## code that uses input$z 
})

But you can never have:

input$z <- renderXXX({ 
  ## code that uses output$y 
})

So inputs inform outputs and never the other way around. If you need dynamic inputs, you have to use either uiOutput and renderUI which keeps you safely in the reactive framework, or (if you know what you're doing) the imperative-style insertUI and removeUI.

SoC revisited

SoC is a concept that makes a lot of sense in the MVC world; it is less relevant in the reactive world. For example, if you want to be strict about this, you can say that ReactJS violates SoC because the same component often both controls the state and renders it. But these two frameworks are so different that this comparison seems a little unfair. Moreover, you can actually make that argument that the reactive world does SoC better than the MVC world in the sense that the latter frames SoC around three concerns: Model, View and Controller. In the reactive world, concerns can be defined in much broader terms -- and you can create a modular component to address whatever concerns you settle on.

You can still try to follow the traditional SoC in Shiny by doing all the Views strictly in the ui function and all the modeling strictly in the server function. You can be even more explicit with this if you use htmlTemplates. _However_, as hinted above, I think that a concept that is proving to be a lot more relevant in the modern web app world is not so much SoC, but modularity or componentization -- i.e. encapsulating each unit of functionality and reusing it as necessary. Shiny achieves this with modules.

Other resources

I recommend this video from Facebook Developers, which describes the shortcomings they found with MVC and how Flux and React were created to address these. While there are obviously differences with Shiny, it's a good overview of the two different approaches.

Was this page helpful?
0 / 5 - 0 ratings