Saturday, February 11, 2006

WebApps Development the Rails way

When people think of Web application developers, presumably the first image they have is a graphic designer with some PHP or other scripting knowledge. While this was true 5-7 years ago, the web application development landscape has changed dramatically in recent years. I can only speak for Java based web development, but I am sure that there is a similar proliferation of choices in other languages too. Today's Java web developer needs to know object relational mapping (ORM) technologies such as EJB, Hibernate or JDO, web application frameworks such as Struts, Webwork or Spring, front end tools such as HTML, JSP, Velocity, Tapestry, JSTL, Java Server Faces or even Javascript with AJAX, with miscellaneous middleware components thrown in such as Lucene for search, JMS for asynchronous messaging, SOAP for remoting, and so on. It a wonder that a web developer doesn't get more respect.

However, I find that the biggest challenge in web development is not what technologies to use (although that choice does have impact on scaling, responsiveness and other usability concerns), but what the site's navigation should look like. Most sites attempt to layer logic over HTTP's stateless protocol to provide an illusion of continuity to the end-user. Figuring out the appropriate navigation strategy plays a big part in creating that illusion. Of course, it helps if the web developer can think like his end customer, such as a developer for an online games site who is himself a gamer. Some companies are not so lucky, such as a bank, whose developers are typically more financially savvy than the end users they serve.

It is easy to get bogged down by details of the navigation, and in the process, create web site flow logic that is difficult to maintain. Worse, the flow logic leaks into the application logic, making core application code that much more brittle.

I have been reading about Ruby on Rails in the Agile Web Development with Rails book. In short, what the Rails framework does is allow you to start with a database table and automatically generate your model, controller and view with rails scripts. This allows you to set up simple web based applications relatively painlessly. To make any significant customizations to the generated application, however, you would need to know Ruby, which I do not as yet. There are other efforts to replicate this functionality in the Java world, such as the Trails project and Appfuse (with AppGen). However, what excited me about Rails is not the code generation itself, but that it represents a simplified way to do web development.

Most web applications have as their basis one or more databases, with tables representing the business entities for the application. The Rails approach to web development allows you to build the controllers and views to store, retrieve or delete these entities first. Once this is done, you can then concentrate on building up the relationships between the entities by providing the appropriate navigation hooks.

For example, in a purchasing system, you would first build up the operations for the Order and Part entities, including the views for each. The Part entity would be a one-to-many relationship with Order, so in your database you would represent it with a foreign key in Part pointing back to Order.

1
2
3
4
5
6
create table Order (...);
create table Part (
   id int not null primary key,
   ...
   order_id int not null foreign key(Order)
);

In your model, you would represent this with a collection member variable, like so:

1
2
3
4
5
6
class Order {
   private Set<Part> parts;
   ...
   public Set<Part> getParts() { get parts; }
   public void setParts(Set<Part> parts) { this.parts = parts; }
}

So even before you attempted to build the view for the Purchase order which contains a set of Part line items for the Order entity, you would have the model, view and controller for the Part and Order entities ready.

Navigation would then be grafted on to the system at the entity level. For example, the Payment entity would be linked to the purchase order view by a navigation link such as "Pay for this order". At the database level it would be represented as a one-to-one mapping between Order and Payment.

1
2
3
4
5
6
7
8
create table Order (
   ...
   payment_id int not null foreign key (Part)
);
create table Payment (
   ...
   order_id int not null foreign key (Order)
);

The model for Payment and Order will look like this, with properties that point back to each other.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Payment {
   private Order order;
   ...
   public Order getOrder() { return order; }
   public void setOrder(Order order) { this.order = order; }
}
class Order {
   private Payment payment;
   ...
   public Payment getPayment() { return payment; }
   public void setPayment(Payment payment) { this.payment = payment; }
}

Effectively, you are letting the database model for the application drive the navigation, which is probably not a bad thing for a number of reasons.

First, by designing the database layer first, its very likely that the database model has a strong correlation with the business domain model. The domain model represents the aggregate of the way people have been doing this particular thing for a number of years, so inefficiencies have presumably been weeded out of the process. In some industries, domain models are standardized, so new developers joining your team are likely to be familiar with the domain model even before they sign up. Even if the domain model is not standard, it is easier to explain a domain model to customers and new hires than arcane data models. As for explaining navigation flows, I have found that it is most often never explained, much less justified with logic; new developers are left to navigate through the site themselves and find out on their own.

Second, the user is more likely to be familiar with the domain model for your business than navigation that you as a developer or designer can dream up, since you are more likely than not to be out of sync with your customer's needs, unless you are in the happy position of being able to think exactly like your customer. Thus the user is going to be more comfortable with a navigation strategy that is based on the domain model. Users who migrate to your application from other applications are also likely to have less of a re-learning curve, since the other application would have implemented at least part of its navigation to be domain-driven.

Third, and finally, it allows you to defer development of navigation details until you are done developing the core functionality for the business entities. From an agile viewpoint, this allows you to demonstrate working code to your client sooner than if you try to develop code with the navigation logic in place, and possibly mixed in with, the core logic for the entities. During the phase where you are grafting your navigation into your partially completed web application, you are really working with larger working components (the entities) and treating them like black boxes as you wire them together with your navigation strategy, so it also results in robust, easy to understand, and easy to maintain code.

I have recently been experimenting with this strategy on a open-source development project where the mandate was to develop a web application to front a model layer developed with Hibernate by members of the core team. I decided to use Tapestry to do the development (I did not know Tapestry and this was a chance to learn). The core entities are now ready and I have submitted it back to the core team for their feedback. The great thing about this strategy so far is that I did not have to have a complete understanding of the domain which the model is designed for. I can defer this work till the next stage, when I apply the navigation and refine the view layer based on the core team's feedback. The feedback itself will provide me with most of the understanding of the domain model that is needed.

No comments:

Post a Comment

Comments are moderated to prevent spam.