The SAM Pattern

SAM (State-Action-Model) is a software engineering pattern that helps manage and reason about the application state. SAM's founding principle is that State Mutation must be a first class citizen of the programming model

Let's consider the simplest application possible, an application that counts events

var model = {counter: 0}

Most software engineers would implement the application state mutation as an assignment:
model.counter = model.counter + 1 ;

SAM challenges that model and suggests isolating application state mutations on the foundation of the Paxos protocol and TLA+:

// actions compute proposals
const proposal = {incrementBy: 1}

// the model accepts or rejects the proposals
model.accept(proposal)
    // the state function computes the new state representation
    .then(state)

SAM is unapologetically driven by simplicity and does not require any library or framework.


This is what Daniel Neveux, Entrepreneur and Full Stack Developer, said:


SAM is simple, I have never seen a concept such minimalist and so powerful [in particular] in its decoupling potential

Here is a 10 min introduction:Here is an in depth presentation of the pattern in the context of Angular2


You want to see some code? Take a look at the Fishing Game (index.html) here which you can run here.

This new pattern was first introduced in an InfoQ article, by Jean-Jacques Dubray. A translation of the article is available in Chinese, French, Japanese and Russian


You have some questions?   

If you like it, please consider sharing the word:   
  1. How does SAM work?
    1. The View as a function of the Model
    2. Business Logic
  2. How can I get started?
  3. Where can I can find more information?
  4. Who is using SAM?

How does SAM work?

SAM is built on one of the most robust foundation of computer science (TLA+).
SAM recommends factoring application state management along three building blocks: actions, model and state

  • Actions are triggered by events, their role is to translate events into proposals to mutate the model
  • The Model is solely responsible for the decision of accepting (or rejecting, or partially rejecting) these values
  • Once accepted, the State function computes the State Representation from the Model property values and makes sure that everyone who needs to "learn" about the new application state is notified, such as the view which will display the “state representation”
  • Finally, the State function computes the next-action-predicate which will invoke any automatic action, given the current application state

Every event is processed as “step” which involves this propose/accept/learn flow.
That's it! That's all there is to SAM.

The pattern is best implemented with the model as a single state tree and a unidirectional data flow. In the case of Front-End applications, SAM is best implemented when the view is a pure function of the State Representation. Two-way data binding is strictly prohibited as it would entail uncontrolled model mutations.

The pattern forms a reactive loop: events trigger actions which present data to the model, which computes the state representation. Logically, after every step a new "state representation" is created (as a pure function of the model).

As we show in many samples below, the view can be implemented as a series of stateless components which have no knowledge of the model or even the actions.

When learning SAM, we recommend that you make a clear distinction between the programming model (State, Actions, Model), the wiring (how the elements of the pattern communicate) and the Architecture (where the elements of the pattern are physically running). SAM supports different wiring approaches and allows you to write code that can easily be migrated from the client to the server or vice versa (isomorphic javascript).

the SAM pattern
The State-Action-Model (SAM) Pattern

The View as a Function of the Model

When learning SAM in the context of Front-End applications, the best starting point is to understand the functional relationship between the view and the model. SAM suggests decomposing the view as a series of pure functions that are fed with the properties of the State Representation.

This concept is extremely simple to implement. I generally implement the front-end components as part of "theme" singleton (because themes are interchangeable):


let theme = {} ;

// React
theme.ready = (counter,intents) => <Counter count={counter} action={intents['start']}/>
                            
let Counter = ({ counter,action }) =>  (
    <p>Counter: {counter} </p>
    <button onClick={action}>
      Start! 
    </button>
  )

// Angular2 (with Directive)
view.ready = function(model) {
    return (
            `<p>Counter:${model.counter}</p>
            <form onSubmit="return actions.start({});">
                [Directive]<br>
                <input type="text" placeHolder="AutoGrow Directive" autoGrow/><br>
                [/Directive]<br>
                <br>    
                <input type="submit" value="Start">
            </form>`
        ) ;

}

// Snabbdom
theme.ready = (counter, intents) => h('div', [
                'Counter: ',
                counter,
                h('br'),
                h('button', {on:{click:function() { intents['start'](); }}}, 'Start')
            ]);  

// Vanilla.js - ES6 
theme.ready = (counter, intents) => `
            <p>Counter: ${counter} </p> 
            <form onSubmit="return ${intents['start']}({});"> 
                <input type="submit" value="Start">
            </form>`


With SAM, front-end developers can focus on building the view directly from the State Representation, unencumbered from the underpinning APIs. How the we got to the current State Representation is irrelevant. This is the paradigm shift that React introduced in Front-End architectures. SAM does not always eliminate the need for interactions (request/responses), but it tends to keep a good balance between reactive and interactive modes.

We get a fair amount of questions asking "why not use templates"? The reason is simple: templates are interpreters while functions are mini-code generators. Interpreters are limited in what they enable you to express (not to mention you often have to learn a specific syntax that does not translate at all to another template interpreter). On the other hand, code generators are infinitely flexible with hardly any drawback. I would personally choose functions over templates any day.

SAM allows you to choose the framework of your choice including none, but when using a Vanilla.js implementation, you need to make sure you mitigate the risks of cross-site-scripting.

A couple of promising libraries have appeared recently (hyperHTML, lit-html and yalla.js) that offer support a "Functional HTML" approach seamlessly. They, of course, are particularly good fit for the SAM Pattern.


Business Logic

Not only SAM supports a functional structure for the View, but SAM enables a complete decoupling of the application logic from the View components. If you are not convinced you can look at this vanilla.js TODO sample and the "theme" section.

SAM's factoring of the business logic is inspired from TLA+:

TLA+ is based on the idea that the best way to describe things formally is with simple mathematics, and that a specification language should contain as little as possible beyond what is needed to write simple mathematics precisely. TLA+ is especially well suited for writing high-level specifications of concurrent and distributed systems. - Dr. Leslie Lamport
If you are interested in learning about TLA+, I found Stephan Merz's course the most approachable introduction to TLA+.

TLA+ is a formal specification which can be used describe, analyze and reason about systems, such as:

  • sequential algorithms
  • interactive systems
  • reactive & distributed systems
  • real-time & hybrid systems
  • security-sensitive systems

TLA+ offers a uniform language where Mathematics form the basis for description and analysis of reactive and distributed systems. The key paradigm shift is that no programming language today support any "temporal" constructs. Making programming much more difficult and error prone than it ought to be.

TLA defines two levels of syntax: action formulas and temporal formulas.

  • action formulas describe states and state transitions
  • temporal formulas describe state sequences

TLA+ also includes concepts such as safety (something bad should never happen) and liveness (something good eventually happens).

In SAM, the business logic is decomposed in three elements: Actions, the Model and the State.


Warning I have been told by readers that it is important to emphasize that SAM does not use the word "State" in the traditional sense of computer science (where a state is an assignment of values to all possible variables). SAM suggests that the Model structure (and property values) are different from the State Representation, which there could be many of (Web, Mobile, Voice...). The State Representation also includes the Control State of the system. The State-Action elements, in SAM, specify the behavior of the system (not the Model):

A State-Action behavior is a sequence:
           α1    α2
        s1 −→ s2 −→ s3 −→  ...

The step <si, αi, si+1> represents a transition from state si to state si+1 that is performed by action αi


Please refer to Section 2 of this paper from Dr. Lamport for a discussion on State-Action behavior.


If you don't like the State-Action-Model terminology, I could have also used the Paxos protocol terminology (PAL, Proposer, Acceptor and Learner):

Client   Proposer      Acceptor     Learner
|         |          |  |  |       |  |
X-------->|          |  |  |       |  |  Request
|         X--------->|->|->|       |  |  Prepare(1)
|         |<---------X--X--X       |  |  Promise(1,{Va,Vb,Vc})
|         X--------->|->|->|       |  |  Accept!(1,Vn)
|         |<---------X--X--X------>|->|  Accepted(1,Vn)
|<---------------------------------X--X  Response
|         |          |  |  |       |  |  
View     Action       Model         State  (SAM)

In essence the Paxos protocol roles are exactly the roles of the SAM components: Actions propose values to the Model, which accepts them, which the State accesses (the Learner) to create the State Representation (which is displayed by the View).

I personally like SAM because it surfaces the concept of "State-Action" behavior, which is at the core of SAM, and leads naturally to creating the "State Representation" (i.e. the View).

So, for all intent and purposes, please be warned that SAM's Model is the assignment of values to all possible variables of the application state, while SAM's State, which is sometimes associated to the "control state" (such as "started" and "stopped" are two control states of a car), refers to a function that computes the current (control) state of the system. In general, the "State" of a system controls which actions are allowed at any given point in time.



Actions

Actions are functions which translate an event into a proposal. The purpose of a function is to compute the values we want the model to accept. There is nothing else to it. In Redux for instance, "actions" are a data structure, which are a lot closer to an intent or event than an action. This is a fundamental difference between Redux and SAM because in Redux the reducer creates an unnecessary and unwanted coupling between the model mutations and the logic that translates intents into model property values.


    
    // Reactive loop wiring
    function action(event, present) {
        // compute the values we want the model to mutate to
        const proposal = the_actual_pure_function_implementing_the_action(event) ;
        
        // present these values to the model
        present(proposal) ;
        
        // since we are in a reactive loop, the action returns nothing
    }
    
Actions typically implement context specific logic. If we take the example of a "change of address" action, we might implement some context specific rules, such as when there is no country specified in the input dataset, the default country is Australia, while the model is responsible for the integrity of a customer address which requires a country value. Actions play another important role with respect to invoking 3rd party APIs. For instance, we can define an action which will invoke a 3rd party validation service, which given an address, returns the postal address (or an error). It is then the postal address which is presented to the model.

    
    async function changeOfAddress(address = {}, present) {
        address.country = address.country || 'Australia' ; 
        const postalAddress = await getPostalAddress(address); 
        // assuming the dataset returned by the 3rd party
        // service can be directly presented to the model
        present(postalAddress) ;
    }) ;
        
    }
    

Actions are typically highly reusable across models, and we could even imagine some companies starting offering SAM actions following a SaaS model. A "change of address" action, which returns a postal address given user data is highly reusable, across many businesses.


Model

The model is a singleton that contains all the application state and (logically) exposes a single method: present(proposal). The model is responsible for accepting (or rejecting) the effects of an action. The model is where integrity rules get enforced. In other words, actions are generally triggered with some context specific data, they do not have access to the application state, for instance, when you implement a "change password" action, the action will check if the password is valid in the context of the request, but the model might enforce additional integrity rules, such that you cannot use a password that matches any of your last three passwords.

The model is also solely responsible for the persistence of the application state. As such, the Model's architecture may follow the Event/Command Sourcing Pattern with respect to the data the it accepts:


    
    model.present = function(proposal, state) {
        
        if (proposal.address !== undefined) {
            model.shippingAddress = proposal.address ;
            model.billingAddress = model.billingAddress || proposal.address ;
        }
        
        // trigger the state representation computation
        state(model) ;
        
        // since we are in a reactive loop, the present method returns nothing
    }
    

State

This concept is unique to SAM. Its purpose is to futher decouple the Model from the View. The State function has two roles:

  • Translate the model property values into a State Representation
  • Compute the control state and trigger the next-action predicate

Unintuitively, the State is a pure function that does not hold any (application) “state”. The State function also computes the current "control state" of the system, but SAM does not require the State implementation to be based the semantics of finite state machines. It offers a structure that is compatible with FSMs but for the most part avoids the unecessaries intricaties of FSMs. In the Rocket Launcher example, to demonstrate the compatibility, we've have implemented the full semantics of a finite state machine where each control state is computed with pure functions such as:

// Assuming the following model structure
var model = {
        counter: COUNTER_MAX, 
        started: false,      
        launched: false, 
        aborted: false}
    
    
// Derive the current control states of the system
const ready = model => (model.counter === COUNTER_MAX) && !model.started && !model.launched && !model.aborted

const counting =model => (model.counter <= COUNTER_MAX) && (model.counter >= 0) && model.started && !model.launched && !model.aborted)

const state = (model, render) => {
    const stateRepresentation = _state(model)
    if (!nextActionPredicate(stateRepresentation)) {
      render(stateRepresentation, model.present) 
    }
}

const _state = model => {
    // Compute the control state
    const stateRepresentation = model
    let controlState = 'oops... something went wrong, the system is in an invalid state' ;
    
    if (ready(model)) {
      controlState = 'ready' ;
    } 

    if (counting(model)) {
      controlState = 'counting' ;
    }

    if (launched(model)) {
      controlState = launched(model) ;
    }

    if (aborted(model)) {
      controlState = aborted(model) ;
    }
    
    // Other computations that are part of the state representation go here

    return { controlState, ...stateRepresentation }
}
    

Depending on the number of control states of your application and the size of your model, these functions can become rather tedious to write. Again, there is no requirement to adopt such an approach, it may help in some cases and be a burden in another where a series of if-then-else on some key properties of the model is good enough.

Once the state representation is rendered, the State function is responsible for invoking the next-action predicate (nap), which is a function of the model. The purpose of the nap() function is to determine if, given the current control state of the model, there are some automatic actions that need to be invoked. For instance in the Rocket Launcher example, the "decrement()" action is invoked by the next-action predicate while the system is in the counting state. When the counter reaches zero, it invokes the "launch()" action.


const nextAction = (stateRepresentation, present) => {
    if (stateRepresentation.controlState === 'counting') {
        if (stateRepresentation.counter>0) {
            actions.decrement({ counter: stateRepresentation.counter }, present) ;
        }

        if (stateRepresentation.counter === 0) {
            actions.launch({}, present) ;
        }
    }
}

When you feel that a full state machine is required for your app, you may use a library such as the STAR library.


Rendering (or Consumming) the State Representation

In Front-End applications, the State Representation is typically rendered as part of the reactive loop, however, in the most general sense, the state representation is published to consumers (i.e. Learners in Paxos). Pure functions of the state representation work well.

One of the key problems that front-end frameworks try to tackle is the wiring between the HTML events and event handlers. SAM provides an elegant solution to decouple the view components from your application via the State function. You can design your view components to accept a list of "intents" which map the application's action to the component's handlers. We have implemented that approch in the TODOSAM sample. All the theme's components accept an intent map:


theme.list = (todos, displayActive, displayCompleted, intents) => {

    ... 

    const label = `<label ondblclick="return actions.${intents['edit']}({'id':'${todo.id}'});">${todo.name}</label>` ;

    ... 

}

// mapping view intents -> actions
actions.intents = {
  edit: 'edit',
  save: 'save',
  done: 'done',
  displayAll: 'displayAll',
  displayActive: 'displayActive',
  displayCompleted: 'displayCompleted',
  toggleAll: 'toggleAll',
  delete: 'delete'

}

The concept could also be extended to map the event format to be directly consumable by the action, but this coupling is less important than the coupling of the actions with the view.


View-Model

When the View is expressed as a pure function of the Model (as opposed to a template or a query), the Model does not have to conform artificially to the interface of the view. With SAM, the View is generally decomposed into components, each of them expressed as pure functions exposing a component specific/centric interface.

For instance, when a view displays a value v and a graphical indicator as to whether this value is great, good or bad, there is no reason to have the indicator’s value in your model: the component's function should simply compute the value of the indicator from the value v provided by the model.

Now, it’s not a great idea to directly embed these computations in the the view, but it is not difficult to use a view-model function, such as:

V = f( vm(M) )

The view-model function can be associated to the structure of the model of the view in the MVVM pattern, but in SAM there is no "view state" or "view specific actions". There is a global state and actions which are visible across the entire application.


Wiring

The SAM pattern can be described as a Mathematical expression (formula):

V = State( vm(Model.present(Action(data))), nap(Model) )
Where State(), vm(), Action() and nap() are pure functions with respect to the model.
However, that expression is only a logical expression that describes the sequence in which the elements of the pattern are invoked. The expression itself is not representative of the Reactive Flow or the different wiring options.

SAM can be implemented in the browser (like React and Redux), but one of SAM's advantages is that the elements of the pattern can be distributed and composed in virtually any topology. So there is not a single way to wire the pattern.

You should also keep in mind that SAM is a reactive pattern so there is never a response that is expected: The view triggers an action, which presents is dataset to the model, which asks the state to create a state representation (i.e. View). The new state representation is just that, a new view of the current state of the model. It is **not** a response to the original view.

Let's start with a sample where running SAM exclusively in a single process (e.g. the browser, perhaps assuming the model will communicate with a persistent store).

The model can be defined as a singleton, the actions as pure functions of a dataset, the state (stateRepresentation() and nap()) as a pure function of the model. Here is an example of wiring achieved with functional wrappers.



// Model is a singleton /////////////////////////////////////////////
var model = {} ;

model.present = function(data) {
    // Logic that accepts or rejects the proposed values
    // ...
    
    // -> Reactive Loop
    state(model) ;
    
    // persist the new state
    // this is generally guarded and optimized
    model.persist() ;
} ;

model.persist = function() {

} ;

// Actions are pure functions /////////////////////////////////////////////  
function action1(data) {
    // Logic that prepares the data to be presented to the model
    // ...
    
    // -> Reactive Loop
    present(data) ;
    
    // to avoid a page reload
    return false ;
} 

function action2(data) {
    // Logic that prepares the data to be presented to the model
    // ...
    
    // -> Reactive Loop
    present(data) ;
    
    // to avoid a page reload
    return false ;
} 

// State is a pure function /////////////////////////////////////////////
function state(model) {

    // the state behavior is hard wired
    stateRepresentation(model) ;
    nap(model) ;
} 

function nap(model) {

    if (condition(model)) {
        var data = f(model) ;
        action2(data,model.present) ;
    }
}


// View is a pure function /////////////////////////////////////////////
function someView(model) {

    // render the view
    var output = '' ;
    // ...
    
    // wire the possible actions in the view
    output = output + 'onSubmit="JavaScript:return action1({data:data});'
    
    // ...
    // -> Reactive Loop
    display(output) ;
}



// Wiring /////////////////////////////////////////////
// 
// Actions are known to the stateRepresentation() and nap()
//
// Actions -> Model
function present(data) {

    model.present(data) ;

} 

// State -> View
function stateRepresentation(model) {

    someView(model) ;

} 


// View -> Display
function display(view) {

    var stateRepresentation = document.getElementById("view");
    stateRepresentation.innerHTML = view;

}




Of course, JQuery event handlers could also be used:



...

<input id="username" type="text" class="login-username" value="username">
<input id="password" type="password" class="login-password" value="password">
<button class="button button-green center-button" id="login">Login</button>

...

<script type="text/javascript">
$('#login').click(function() {
    var session = $.post( "http://authserver/v1/login", { username: $( "#username" ).val(), password:$( "#password" ).val() } ) ;
        session.done(function( data ) {
            var loginPanel = document.getElementById("login_panel") ;
            loginPanel.innerHTML = state.render(model.present(data)) ;
             ;
        })
}) ;
</script>

... 

// another option is to implement the state, model and actions on the server:

<script type="text/javascript">
$('#login').click(function() {
    var session = $.post( "http://authserver/v1/login", { username: $( "#username" ).val(), password:$( "#password" ).val() } ) ;
        session.done(function( stateRepresentation ) {
            var loginPanel = document.getElementById("login_panel") ;
            loginPanel.innerHTML = stateRepresentation ;
             ;
        })
}) ;
</script>



One can also use RxJS to wire events to actions.



var result = document.getElementById('submit');

var source = Rx.Observable.fromEvent(document, 'click');

var subscription = source.subscribe(function (e) {
  var data = { name: document.getElementById('name').value, ... } 
  result.innerHTML = state.render(model.present(data));
});

Wiring is an important concern when implementing the pattern. For instance, the mobx framework wires individual property value changes to the view rendering function, which is not representative of the behavior of the system and the processing of an action as a unit of work. As you can see in the example tab below, the view renders for state data changes:



// The state of our app
var state = mobx.observable({
    nrOfSeats : 500,
    reservations : [],
    seatsLeft : function() { return this.nrOfSeats - this.reservations.length; }
});

// The UI; a function that is applied to the state
var ui = mobx.computed(function() {
    return "\n
Seats left: " + state.seatsLeft + "
Attendees: " + state.reservations.join(", ") + "
"; }); // Make sure the UI is 'rendered' whenever it changes ui.observe(function(newView) { console.log(newView) }, true); // Put in some test data state.reservations[0] = "Michel"; state.reservations.push("You?"); state.reservations[0] = "@mweststrate"; state.nrOfSeats = 750;
<div>Seats left: 500<hr/>Attendees: </div>
<div>Seats left: 499<hr/>Attendees: Michel</div>
<div>Seats left: 498<hr/>Attendees: Michel, You?</div>
<div>Seats left: 498<hr/>Attendees: @mweststrate, You?</div>
<div>Seats left: 748<hr/>Attendees: @mweststrate, You?</div>


Time Travel

Gunar Gessner has implemented a client-side "Time Travel" dev tool that enables you to take snapshots of the model after every action is processed and then restore the state of the system with a given snapshot.

The SAFE container implements both a client-side and server-side "Time Travel" dev tool.



Micro Container

The SAFE project (State-Action-Fabric-Element) is a micro-container for SAM implementations which can run in the browser or on node.js. At a minimum SAFE enables you to wire the elements of the pattern. It also comes with Session Management, Logging and Error Handling. The current version enables global validation and enforcement of actions, including action "hang back". Last but not least, it implements the Time Travel dev tool.



Composition

The SAM pattern offers some generous composition mechanisms.

First Actions, as pure functions, compose naturally to present a single dataset to the model:

C(data) = A(B(data))

This type of composition is a functional composition and the resulting action is considered to be a single action applied to the system from SAM's point of view. Logically you cannot compose Actions, as it would violate, the Action->Model->State step, you can only create "composite" actions.

Similarly, the State Representation (View) can be decomposed in a hierarchy of components:

V = f( M )
f( M ) = f1( g1(M) + g2(M) ) + f2( h1(M) + h2(M) )

The most interesting composition mechanisms are at the pattern level itself. For instance, SAM supports an instance level composition where one instances runs in the browser and one instance runs in the server.


// The composition has a single view
V = Ss( Ms ) + Sc( Mc )

// Instance c (browser) invokes an action on instance s (server)
// this action is generally invoked in the nap() function of the client
V = Ss( Ms.present( As(Mc) )

Though it is theoretically possible, it is highly recommended to refrain from invoking client actions from the server. The role of the server is rather to deliver the SAM client instance as part of its state representation.

Here is a Parent/Child sample using React 15.0.2 with JSX/Babel. It shows how you can implement a complex form of wizard with a child instance and submit the resulting dataset to the parent, once the content of the form is valid.

NOTE: THIS IS JUST SOME INITIAL THOUGHTS, NEED MORE WORK:
SAM also offers an interesting alternative composition mechanism which enable to synchronize a client side and server side model, by which you present the same dataset to both the client side and server side model.


State Machines and Automatic Actions

Computation is a major topic of computer science,
and almost every object that computes is naturally viewed as a state machine.
Yet computer scientists are so focused on the languages used to
describe computation that they are largely unaware that those languages
are all describing state machines - Dr. Leslie Lamport

SAM is particularly well aligned with the semantics of State Machines except for one simple, yet fundamental difference. Classical State Machine semantics (based on Petri Nets) imply that the actions somehow connect two states, so State Machines are described as a series of tuples such as:

initialState = S0
(S0,A01,S1), (S1,A12,S2)...

This definition is somewhat of an approximation: it is not the action that decides the resulting state, it is the model, which, once the action has been applied (its value have been accepted, or not) that decides the resulting state (Aik are the allowed actions in a given state Si) :
initialState = S0 = S( M0 ) 
(S0, A00, A01), (S1, A10, A11, A12)...
S0 = S( M0 )
S1 = S( M1 )
...

The tuples (Si,Aik,Sk) are merely an observation of the behavior of the state machine, rather than a physical representation of its runtime.
This change of perspective (not semantics) is minute, yet fundamental. SAM would not work (as well) if the traditional semantics of State Machine would be structuring the code. The SAM semantics are inclusive of this traditional structure, and therefore strictly compatible, but SAM by no means require that one uses a graph of State and Actions.

I believe that is why some people have expressed that SAM "feels natural".

The Rocket Launcher example shows how to implement the Sx() functions:


// Derive the current state of the system
state.ready = function(model) {
    return ((model.counter === COUNTER_MAX) && !model.started && !model.launched && !model.aborted) ;
}

state.counting = function(model) {
    var status = ((model.counter <= COUNTER_MAX) && (model.counter >= 0) && model.started && !model.launched && !model.aborted) ;
    return status ;
}

state.launched = function(model) {
    return ((model.counter == 0) && model.started && model.launched && !model.aborted) ;
}

state.aborted = function(model) {
    return (
        (  model.counter <= COUNTER_MAX) && (model.counter >= 0) 
        && model.started && !model.launched && model.aborted ) ;
}

I cannot emphasize enough that it is not necessary to adopt a State Machine structure to use SAM and more often than not if-then-else will be adequate. However, sometimes it may become easier to use a strict State Machine structure. These functions can be used to validate that an action is enabled or not. They can also more naturally break down the hierarchy in from which the view is rendered.


APIs

SAM was originally designed to solve the strong coupling that MVC creates between the Front-End structure and the Back-End APIs. APIs can be composed at a couple levels in SAM:

  • Actions: for APIs with no side-effect whatsoever on the model
  • Model: for CRUD APIs that persist/populate the model property values (commonly called the Data Access Layer)
Concurrency issues aside, an action can present its values when after processing an API call, for instance:


function getRssFeed(data) {
    
    var options = {
      url: 'http://www.ebpml.org/blog15/feed/',
      headers: {
        'accept': 'application/json'
      }
    };
    
    request(options, function (error, response, body) {
       if (!error && response.statusCode == 200) {
            
            // parse the RSS feed into a json object
            parseString(body, function (err, result) {
            
                // prepare proposed dataset
                data.blog = result.rss.channel[0].item ;
               
                // present data to the model
                present(data) ;
           });
       }
     });
}

Similarly, the model can perform all the persistence operations (which could result in rejecting some proposed values) before passing control of the Reactive Loop to the State object. That is very different from React because you cannot touch the State without triggering a rendering of the view, which makes very little sense, when you think of it. It only makes sense when you spread the model state into the various components, and even then it warps the articulation of APIs with the Front-End reactive Flow.



Headless SAM

SAM can also be used without a View, whereby it listens on incoming Action requests and returns a response which can be the model of a fraction of the model. As such SAM offers a new API implementation pattern which is particularly well suited for implementating Stateful APIs.




A node.js implementation of Headless SAM would look like this:



var express = require('express');
var app = express();

//// Model ////////////////////////////

var S0 = { ... } ;

var present = (

    function (initialState) {
        var model = initialState ;
        return (function( data, res) {
        
            // mutate the model
            ...
        
            state(model) ;
        }) ;
    }
    
)(S0);


//// Actions ////////////////////////////

actions.addAPI('submit') ;
app.post(actions.apis.submit.path, function(req,res) {
    
    // extract intent from request body
    var intent = new Intent('submit',req.body) ;
    
    // Compute model property values
    data_ = actions.impl.submit(intent) ;
    
    // present data to the model
    presentToModel(data_, res) ; 
    
}) ;

//// State ////////////////////////////

function state(model) {
    
    // prepare and return API response
    var response = model.response() ;
    res.send(response) ;
    
    // execute next action predicate
    nap(model) ;

}


The pattern can easily be adapted to mount a user session in the model or rehydrate/dehydrate the context of the request before/after the model mutates.

Isomorphic JavaScript

Typical SAM's implementations make it easy to move JavaScript code between the client or the server (a.k.a Isomorphic JavaScript).

# Client Server Implementation
1 View Actions, Model, State Event handlers call Actions
Server returns HTML
2 View,Actions Model, State Event handlers implement actions which call present()
Server returns HTML
3 View,Model,State Actions Event handlers invoke Actions, response is presented to the Model on the client
Server returns JSON
4 View,State Actions,Model Event handlers invoke Actions, response is presented to the State on the client
Server returns JSON
5 View,Actions,State Model Event handlers implement Actions which call present(), response is presented to the State on the client
Server returns JSON

For instance, in the Blog sample we have implemented the Actions on the client and the Model and the State on the server. In that case, we have to implement two "APIs" on the server:

  • init()
  • present(data)


Option #1 should be preferred when authorization (RBAC) is a concern. Option #5 is the one that pushes as much processing on the client as possible.


    
function present(data) {
    // client side
    //model.present(data) ;

    // server side
    $.post( "http://localhost:5425/app/v1/present", data) 
    .done(function( representation ) {
        $( "#representation" ).html( representation );
    }       
    );
}

function init() {
    // client side
    //view.display(view.init(model)) ;

    // server side
    $.get( "http://locahost:5425/app/v1/init", function( data ) {
        $( "#representation" ).html( data );
    }       
    );
}            



Concurrency

JavaScript is a concurrent programming language. In SAM, the model is a critical section that must be synchronized. In simple applications, this is usually achieved without any specific considerations. SAM's structure where actions can invoke APIs prior to presenting their proposal to model helps significantly by isolating the way the proposal is constructed from the application state mutation. In particular, another action executed concurrently would be able to present it's proposal as if the first (long running) action would have never been triggered.

When concurrency is an issue, it's possible to use the "Bakery Algorithm", again using the work of Dr. Lamport.

How can I get started?

We suggest that you download an HTML5 template of your choice and start building an application with it. To make this tutorial more realistic, we are going to build a simple Web Site with Blog and a Contact form.

The SB Admin template seems like a great starting point. The list of components are easy to identify, from the index.html file:

  • ----- Structure ------
  • Navigation Bar
  • DropDown
  • Search
  • Menu
  • Footer
  • ----- Structure ------
  • Stats Summary
  • Graphs
  • Tables
  • ...


Many developers dislike the code that will follow (let's call it vanilla.js). Since this is a matter of preferences, there is not much point in arguing about it. I just happen to prefer a raw JavaScript/HTML5/CSS3 style because:

  • it enables the greatest number of developers to become full stack developers (with Node.js)
  • it enables the greatest decoupling between the View and the Model
  • Vanilla.js makes it really easy to develop Isomorphic JavaScript
  • And perhaps, most practically, it allows anyone to take the work of a Web designer and turn it into a beautiful Web app in a matter of minutes

The experienced developers will easily translate this code to meet their neesds. So let's go ahead and start creating the component interfaces without any further apologies:

var theme = {} ;

theme.head = function(title, includes, addons ) { } ;

theme.navBar = function(dropDowns,sideBar) { } ;

theme.adminDropDown = function(urls) { } ;

theme.search = function(search) { } ;

theme.menuItem = function(label,symbol, url, submenu, level) { } ;

theme.sideBar =  function(menu,color) { } ;

theme.well = function(size,h,title,body,br) { } ;

theme.footer = function( includes ) { } ;

theme.statSummary = function(fa,label,number,link,linkLabel,color) { } ;

theme.advancedTable = function(size,id,color,heading,headers,columns,data,href,domain) { } ;

theme.ediTable = function(id,headers,data,editable, style, maxSize) { } ;

... 

The implementation of each function is just a parameterization of the HTML template (the whole theme implementation can be found here):


theme.statSummary = function(fa,label,number,link,linkLabel,color) {
        return (
          ```<div class="col-lg-3 col-md-6">
              <div class="panel panel-${color}">
                  <div class="panel-heading">
                      <div class="row">
                          <div class="col-xs-3">
                              <i class="fa fa-${fa} fa-5x"></i>
                          </div>
                          <div class="col-xs-9 text-right">
                              <div class="huge">${number}</div>
                              <div>${label}</div>
                          </div>
                      </div>
                  </div>
                  <a href="${link}">
                  <div class="panel-footer">
                      <span class="pull-left">${linkLabel}</span>>
                      <span class="pull-right"><i class="fa fa-arrow-circle-right"></i></span>
                      <div class="clearfix"></div>
                  </div>
                  </a>
              </div>
          </div>```) ;
} ;

Code Samples

Rocket Launcher

Vanilla JavaScript and HTML - You can run that sample here (courtesy of Jose Pedro Dias)
MobX + React (Fred Daoud)
Cycle.js (Fred Daoud)
Snabbdom ( Jose Pedro Dias). You can run the sample here.
Knockout.js (Troy Ingram)
React+Redux (following the SAM pattern) (Gunar Gessner)
Angular (following the SAM pattern) (Bruno Darrigues)
Angular+TypeScript (following the SAM pattern) (Bruno Darrigues)
Inferno
Angular2 with Dynamic templates and ComponentFactory
Vanilla.js+Redux (Fred Daoud)

Fishing Game

Vanilla.js

ToDo

Vanilla JavaScript and HTML
Vue.js (Michael Solovyov)
TODOMVC Challange (HTML+Vanilla JS, both ES5 and ES6) Angular2 Admin Template with ToDo component with RxJS This is a TodoMVC project that shows how SAM Pattern can be implemented in a typical Angular application with ngrx (@Nivani)

Blog

Node.js / Isomorphic JavaScript - BootStrap

Contact

Simple Web site with Contact App (Node.js+HTML)

Themes

A simple Web app built on the SB Admin template (Node.js+HTML)
A simple Web Site (with actions) built on the Ex-Machina template (HTML+Snabbdom) (@imnutz / Son Ngoc)
A Vanilla ES6 Builerplate Project
A full stack, vanilla.js/Node.js of the Bootstrap Clean Blog project (ES6)

Others

A dual-client (session based) Tic-Tac-Toe game with a SAM-based engine (React/Redux+Firebase) via David Fall A simple Counter App using Flyd (@imnutz / Son Ngoc)
How to use Mocha and Chai to write unit tests (BDD) for SAM implementations (via Robert Blixt)
A contact manager web application built with SAM and CSP (Communicating Sequential Processes) (@imnutz / Son Ngoc)
SAM-Swift The SAM (State-Action-Model) pattern in Swift A simple CRUD application running on AWS Lambda and DynamoDB for session management
A simple counter using React 15.0.2 with JSX/Babel
A Parent/Child sample using React 15.0.2 with JSX/Babel
Another Parent/Child sample in vanilla.js
A CSS Spinner using React/JSX without "looping" through the model
SVG Clock using the very promising lit-html library (Polymer Labs)
Counter in C# (Dmitry Shalkhakov)

This is what Jonathan Siu, Startup Consultant, had to say about SAM:


Frameworks


André Statlz has written a great paper comparing the different Unidirectional User Interface Architectures. The pattern closest to SAM is Famous, but unfortunately, they mutate the model directly from the actions (events in Famous' terms) and they do not support a nap() function.

This talk from Andrew Clark at the React 2016 Conference offers some great perspective on the different approaches (React,Cycle,Elm) starting with a simple formula...:

V = f( M )


React.js


The SAM pattern spans several libraries of the React.js nebula: React.js itself, but also Flux/Redux, including separate libraries such as Redux-Thunk and Redux-Saga. This framework (of libraries) has been built rather opportunistically, solving one problem at a time, without a coherent architecture. Further its architecture focuses heavily on the client, without much consideration for the server.

React + Redux + Thunks + Sagas SAM
Single State Tree Single Model Tree
State is read-only Model is presented with new proposed values
Reducers are pure functions
Reducers have access to full scope of state when processing actions
Actions are pure functions with respect to the model
Actions have no access to the model to compute the values presented to the model
Store is updated with the output of the reducer function
Listeners are notified of the update
State Representation is created after the model has mutated
Next-Action Predicate evaluates whether an automatic action needs to be triggered
View is a function of the state State representation is a function of the model
Next action predicate (nap) is implemented as a Saga which couples many operations and generally relies on local state as shown in this implementation of the Rocket Launcher. The Next Action Predicate is a pure function of the model which computes the current (control) state of the system and derives the next-action.

The main issues I see with this framework (as a set of libraries) are:

  • Redux combines the logic that computes the values with which you would update the model with the logic that decides whether the model accepts these values or not.
  • As a corollary it couples the interface of the reducers to the model. One of the key roles of actions is to adapt the View-Model interface in ways that makes the model insensitive to the view semantics. The Reducers' interfaces are directly shapped by the view, which is an anti-pattern.
  • Anemic actions are the default in Redux, you need to add the "thunk" library to be able to use functions as actions
  • Sagas are stateful by definition and their imperative style makes it difficult to reason about what is the next action that will occur.

That being said, the deal breaker, for me, is the fact that Reducers cannot run on the server because of React's architecture. The store would need to be updated with the output of the root reducer, i.e. the entire state, which would create a high bandwidth, chatty interaction with the client.


Angular2

Angular2 SAM
Two-way Data Binding Functional HTML - V = f(M)
Components as Classes (OOP) Functional building blocks (View, Action, State, NAP), in SAM only the Model is an Object
Uncontrolled View updates with Change Detection SAM programming Step and Rendering are fully decoupled

With all its imperfections, boilerplate and template language, Angular makes it easy to implement the SAM pattern, which in turn, I believe, adds a lot of value to Angular with a Single State Tree and the ability to control mutations outside the Angular components. SAM allows you to code your business logic coupletely decoupled from Angular itself.

For complex Angular components, I found it easier to create a local SAM implementation where all the state mutations are groupled in a single method and map event handlers to actions (which compute the proposal to mutate the application state)


Vue.js

Vue.js is an increasingly popular framework for building modern Web applications. Just like React, the basic framework focuses (mostly) on the View and you need to select from another nebula of libraries when you need to build more robust applications. In this brief analysis, I will focus on vue.js only, as well as the vuex.js library which is a Redux-like state management library. Vue is template based and relies on two-way databinding which is a major step back in Front-End architectures.

Vue + Vuex SAM
Two-way Data Binding Functional HTML - V = f(M)
Single State Tree Single Model Tree
Two-Way Data Binding Model is presented with new proposed values
Computed properties, Watches and Methods are free to assign new values to the model
In Vuex,
Actions are pure functions with respect to the model
Actions have no access to the model to compute the values presented to the model
Store is updated with the output of the reducer function
Listeners are notified of the update
State Representation is created after the model has mutated
Next-Action Predicate evaluates whether an automatic action needs to be triggered
View is a function of the state State representation is a function of the model
Next action predicate (nap) is implemented as a Saga which couples many operations and generally relies on local state as shown in this implementation of the Rocket Launcher. The Next Action Predicate is a pure function of the model which computes the current (control) state of the system and derives the next-action.

Meiosis

Meiosis is a library build by Fred Daoud that is well aligned with the SAM pattern. The library's core focus is to help organize the data flow of web applications. The general idea behind Meiosis is to start with a model and write a function that creates the view. When you want a looser coupling between the view and the model, you can optionally configure an "actions object" which translates view events into model proposals. Once the proposal is accepted by the model, Meiosis calls the view function with the updated model. You can also define a function to determine if another action should be automatically triggered, given the new application state. Meiosis is designed to work with plain JavaScript functions which, it wires together, without tying your code to the library. Last, but not least, you can switch from one Web framework to another and by changing the view code. The remainder of the code (the model, receiving proposals, actions, and so on) remains the same.

Here is a code sample implementing the SAM pattern and using lit-html

Where can I find more information?

Jean-Jacques Dubray, InfoQ, Feb. 2016 "Why I no longer use MVC Frameworks?"
Gunar Gessner, Jean-Jacques Dubray, InfoQ, Oct. 2016 "Lessons learned in implementing the SAM pattern"
Jean-Jacques Dubray, Marcus Feitoza, Sladan Ristic "Three Approximations You Should Never Use When Coding"
Jean-Jacques Dubray "HEX: A no framework approach to building modern Web Applications"
Alexander Schepanovski, March 2015 "Boiling React Down to a Few Lines in jQuery"
Simon Friis Vindum, "Functional Front-End Architectures"
Tim Branyen, "DiffHTML"
Steven Lambert, "A proposal to improve the DOM creation API: HTML Tagged Templates"
Mike Samuel, "Using type inference to make web templates robust against XSS"
owasp.org "XSS (Cross Site Scripting) Prevention Cheat Sheet"
Google Application Security
Greg Young, "CQRS and Event Sourcing"
SmartDraw "Why we ditched AngularJS"

Who is using SAM?

ebpml.org The about page is built from an off-the-shelf template, the model populates itself with the ebpml.org RSS feed, the contact form invokes an action.
Gliiph Gliiph is a new kind of Web platform that brings together content, data and APIs to deliver the next generation Web apps
MichelDuran.com Michel Duran is a French-Canadian Actor, Director and Producer who recently engaged xgen.io to rebuild its Web site.