4

I'm wondering how to structure my application. Should I use custom views and one main view that will control them. Or is it better to hold my views in a lightbox. All of the examples I've found are somehow limited of functionality and present a single or a few screens. What is the right way to organize a bigger application?

user732456
  • 2,638
  • 2
  • 35
  • 49

2 Answers2

5

I'm wondering too.

Starting point:

  • Famo.us apps are structured in a hierarchical rendering tree.
  • Famo.us uses RequireJS modules.
  • Famo.us encourages the use of Views
    • Views are reusable components.
    • Views encapsulate functionality (and part of the rendering tree).
    • Views communicate using events.
    • View get initialized by an options object.

We need to add structure to manage:

  • Business logic
  • Non-UI functionality like REST api calls, audio, local storage, etc.
  • Data-flow:
    • Update data on user input
    • Update view on data change

Modules, events and loose coupling

Goals:

  • Scalable application structure.
  • Small, independent, testable modules.
  • Promote re-use of code.
  • Easy sharing of modules (just drop a file your project)
  • No dependence on other Javascript frameworks.

All to often, there is tight coupling between modules:

  • Module instantiates and keeps a reference to another module
  • Modules cross their semantic boundries
    • A view that knows about a specific REST api
    • A view that uses specific model code such as Backbone (see Taasky example)

Here is to how to avoid this:

  1. Modules emit events when something interesting happens (user input, data change, etc)
  2. A Mediator listens to the event
  3. The Mediator calls the public API of another module.

So, instead of having a View-module know about Backbone model stuff, it emits an user input event like this:

  1. Todo-Edit-View emits "User input" event: "todo-update",{id:1,title:'Code'}
  2. Todo-Mediator listens to the event.
  3. Todo-Mediator calls tasks.find({id:1}).update({title:'Code'}) of Todo module (e.g. using Backbone)

Rules

  • There are Modules and Mediators
  • Modules have a public API and emit events
  • The API and events stay in their semantic boundries; i.e. "updateTitle()" instead of "onBackboneModelChange()"
  • The Mediator couples the app, i.e. a backbone model change to a todo-item title update.
  • Mediators are singletons
  • An application can have multiple mediators.

For example, an e-mail application like GMail could have mediators for

  • chat function
  • todo list function
  • reading e-mail
  • writing & sending e-mail

File structure:

/src
   /lib
   /services
   /mediators
   /layout
   /content
   /config
   /main.js

Types of modules:

  • Service: An independent module to encapsulate non-UI functions such as: LocalStorage, Ajax, Web Audio, etc.
  • Layout: Layout, animation and positioning of content, e.g: ScrollView, HeaderFooterView, etc
  • Content: Actual content of the app: leaf nodes (surfaces) of the rendering tree.

Note the distinction between Layout and Content. By seperating UI-components & layout from actual content, it becomes easy to reuse UI patterns such as side-panels, popups, navigation-bars, sticky-headers, scroll-views, etc.

Also, I would recommend creating a *.css for every Layout containing only structure and minimal theming. All theming can be overriden/extended in config/theme.css, so it's easy to reskin an app.

Other code:

  • Mediator: Couples modules using events and public APIs.
  • Config: Contains all options used throughout the app on initialization.
  • main.js: Bootstraps the app.

Bootstrap app in Main.js

  1. Create mediators
  2. Create services
  3. Construct rendering tree

Module life-cycle

  • A module must be contained in a single file, i.e. don't depend on external libraries!

When a module is created, it announces its presence to all mediators. We use the Famo.us Engine to emit a global event. This will be the only required dependency!

 Engine.trigger('created',this)

When a module is destroyed, it announces its destruction to all mediators.

 Engine.trigger('destroyed',this)

The mediator listens to created and destroyed events and glues modules together:

 var someDataModule; // Backbone or whatever
 Engine.on('create',function(module){
    if(module instance of SomeDataModule) {
        var someDataModule = module;
    }
    if(module instanceof TodoView) {
      module.on('change-title',function({id:id,title:title}){
          someDataModule.find({id:id}).set('title',title);
      })
    }
 }) 

In simple mediators, you can initialiaze modules in order (i.e SomeDataModule before TodoView). However, in more complex scenarious, you might need to use a Promise to couple everything.

Note on dependencies

There are three exceptions on "self-contained" modules:

  1. Layout and Content modules are allowed to have hierachical dependencies. Parents can initialize children, and expect certain events from these children. A ListView might initialize ItemView and handle ItemRemoved events.

  2. Services can be a Facade/Adapter for another service. For example, a DataService might provide an simplification, abstraction and security layer for a RestApiService.

  3. Of course, mediators have hard-wired dependencies because they couple the app!

markmarijnissen
  • 5,569
  • 2
  • 28
  • 34
1

Famo.us is focused on UI, so you better look for a MVC pattern to have better structure, specially on large projects, actually as Famo.us is very young it only have an integration with Angular:

http://famo.us/integrations/angular/

But is well known that they will add other MVC integrations in the near future

Try Angular and look if it is what you need, it´s a pretty nice MVC Framework, my recommendation is to first learn Angular at least basics (http://angularjs.org/ and then after Famo.us University (http://famo.us/university) you can understand this integration

Sergio López
  • 973
  • 9
  • 16
  • There is also an unofficial Meteor integration: http://famous-components.meteor.com/ (Authored by me, supported by the community) – gadicc Jun 06 '14 at 14:06