Rationale

This page tries to explain some of the design decisions made while developing ESXX.

Filter chains

I used to say otherwise, but filter chains are now available in ESXX and it's a good thing to have. It's never too late to change your mind.

O/R mapping

First of all, let me just say that there are third party JavaScript ORM frameworks available (such as ActiveRecord.js or TrimJunction) that could, and probably should, be ported to ESXX.

An object-relational mapper tries to make a relational database into something that it isn't. ESXX uses a dynamically typed language and XML, so working directly with SQL databases is really easy. And by using a hand-designed database schema, your database is easily accessible from other programs and languages as well.

Much can be said in this matter, but my view is that relational databases should be used the way they were designed to be used, and that you should write your applications accordingly. If all you need is a way to store your objects and and able to retrieve them by key or simple queries, an index databases such as Lucene or Xapian could be another option.

Pitfalls

Pretending the RDMS is an object storage easily results in suboptimal perfromance, no matter if automatic object-relational mapping is used or not. Consider the following example, from the Spring MVC tutorial. (Admittedly, MVC-step-by-step is just a tutorial, but isn't the whole point of a tutorial to show how to best use your product?)

// From ProductManager.java in MVC-step-by-step
public void increasePrice(int pct) {
    ListIterator li = products.listIterator();
    while (li.hasNext()) {
        Product p = (Product) li.next();
        pmd.increasePrice(p, pct);
    }
}

// From ProductManagerDaoJdbc.java in MVC-step-by-step
public void increasePrice(Product prod, int pct) {
    logger.info("Increasing price by " + pct + "%");
    SqlUpdate su = 
        new SqlUpdate(ds, "update products set price = price * (100 + ?) / 100 where id = ?");
    su.declareParameter(new SqlParameter("increase", Types.INTEGER));
    su.declareParameter(new SqlParameter("ID", Types.INTEGER));
    su.compile();
    Object[] oa = new Object[2];
    oa[0] = new Integer(pct);
    oa[1] = new Integer(prod.getId());
    int count = su.update(oa);
    logger.info("Rows affected: " + count);
}

In the tutorial, every call to ProductManagerDaoJdbc.increasePrice() compiles and executes one SQL query.

In ESXX you would have written method as follows, which compiles the query only once, when it's first executed, and then updates all product prices at once each time ProductManager.increasePrice() is called.

// From app-form-sql-pm.js
function ProductManager.prototype.increasePrice(pct) {
  this.db.query("UPDATE products SET price = price * (100 + {percent}) / 100",
                { percent: pct });
}

If you're going to use a relational database, it helps knowing how they work.

Black and white?

Nothing is absolute. Automatic O/R mapping has many benefits, and database independence is one of them.

It's a bit like REST vs. SOAP-RPC: do you view the web as resources that can be modified, or objects with methods? I prefer REST, but won't prevent you from using SOAP; in the same way, I prefer the relational view, but I will obviously not prevent you from using the RDBMS as an object store.

Automatic session handling and continuations

HTTP is a stateless protocol, and that's really a good thing. Writing web applications the same way you would write a local-only application will sooner or later end in tears, either for you or for your users.

Use the protocol to your advantage, and let the database or the client worry about state. Stateless web applications are almost indefinitely scalable — isn't that great? And modern web browsers and client frameworks makes it painless to run almost all of the UI on the client.

Yet sometimes you just don't care about scalability. For these situations, you'll be happy to hear that keeping state on a single node is as easy as using a global hash object, since ESXX applications are long-lived on each node.