Seam has been out for a week now, and I thought I'd give my first impressions of it. I guess it's not entirely fair to say first impressions as I've been working with seam and contributing to the project for the last month or two. Regardless, I'm going to try and summarize my experience with porting the DVD Store to Seam.
This was an interesting experience because just doing an EJB3 application is a huge savings. I completed the DVD Store over the course of one weekend. It's not a huge application, but I'm quite sure it would have taken me at least a full week with J2EE + XDoclet. That type of productivity increase might not make a rails fanboy giddy, but I was rather impressed because I didn't think EJB3 would be that different from XDoclet when I started.
Although EJB3 was clearly a huge win, I wasn't as sure about JSF. I used JSF in the DVD Store and although I really liked the model, I have to admit that there were some rough edges. JSF + EJB3 forced the DVD Store into a fairly typical layered architecture. The backing beans talk to a session facade of sorts that provides the business logic and access the entities. I chose to layer the app because it is much easier to work with the entity manager and do transactional things in a session bean than it is in a JSF backing bean. The architecture made sense, but I knew the app could be better if I didn't have to have an artificial barrier between the web code and the application code.
At the same time, I came to love working with annotations. In JSF, backing beans and their dependencies are declared in XML. I found myself wishing for the annotation-driven injection of EJB3 components. If only my backing beans could be more like EJB3 components.
Ok, I'm sure that is going to sound like a contrived lead-in, but it was exactly what I was thinking at the time. And, that is why I was initially excited by seam. I was looking for something to bridge the gap between the UI components and the EJB3 components. It wasn't until I got into it that I realized seam is much bigger than a simple integration layer. It's an entirely new model for contextual components that changed a lot more in my app than I expected.
My first pass through the app was making everything a Seam component, from the backing beans to the EJBs. This got rid of the XML and allowed me to do dependency injection throughout the application. With that, I slowly dismantled the facade. I moved entity access to the components that used them. For example, the DVD search component manages the search completely instead of going through a very awkward mirroring of methods in the facade. Then, I moved the business logic into the components. I moved the purchase functionality into the shopping cart component. It was a natural fit. Before long the facade was gone and I had a much cleaner app.
I was worried about mixing UI code with pure application code, but that doesn't really show up much in JSF. The backing beans never talk to an HTTP session or output HTML. They take objects in and spit objects out. JSF takes care of all the ugly UI code. Although you can do JSF stuff in a component, the only time it really has to even show it's head is through the action methods which return a string result that is used to determine what view is displayed.
Conversations started playing a big role at this point. The search and cart component were session-scoped beans, and I had to take care to reset their state appropriately. And, of course, hitting them from multiple windows would be quite confusing. Moving to conversations let me eliminate the state management. When a user hits the search page, Seam starts a conversation and binds the stateful component to that context. If the user continues clicking, he stays in the same conversation. (unless the component ends the conversation) If at some point the user goes to the page in another window or simply hits the URL manually (a new GET operation, not a click on the page that continues the conversation) a new conversation is started. The state of one conversation doesn't get mixed up with the other.
The only problem I ran into was my desire to use redirects. After an action, I often like to redirect to a new view so that the URL in the browser is always meaningful. This is problematic if the action was intended to start a new conversation. With an additional hard redirect, a second conversation would be started. I decided to remove the use of redirects and just accept this as the way thing work for now. I need to play with Seam a bit more to see what the best way is to work with redirects.
Conversation scope is really nice, and I'm not really doing justice to it here. Suffice to say that it let's components just be components and not worry about managing anything other than their core functionality. Bijection is very similar. The component doesn't have to pull things from the view or push things to the view. A component just maintains its state, and annotations declare what part of the state gets linked in and out. @Out to the view is really nice. The feel is similar to the feel in Rails where controller values are directly accessible in the view by name.
The last step I took was to dump JSPs and use facelets. Facelets is so simple and natural that the idea of doing Seam (or any JSF for that matter) without it seems unthinkable. If you've turned your nose up at JSF because of the quirks of running with JSP, give facelets a try. You won't regret it.
That was the first pass at seam-ifying the DVD Store. I didn't think the app could get much simpler than the original EJB3 version, but it did. But there is still a lot more to Seam, and I've been hard at work bring some of those aspects in to the second version. The two main keys are model validation and business process.
Seam supports the Hibernate validation model. An EJB3 entity can declare validation requirements using the hibernate 3 validation annotations. It's all declarative. When a model object is injected into a bean for use by an action, you can request that the model be @Valid. If the model isn't valid, you can specify an alternate view to go to. JSF has the concept of validating input, but this takes it a step forward to actually validating the resulting object. Both types of validation have their place, and seam makes both types equally easy to use.
I'm also adding workflow (business process) to the DVD Store using jbpm. I think this is also a topic that requires a bit more explanation than I can give here. If you are deciding to integrate process into your application (and I'm increasingly of the opinion that this is generally a good thing to do) then you can can use seam for almost free process integration. Not only can you easily mark the beginning, end and transitions of the process using declarative annotations, but you can inject from/outject to the process context trivially. In the case of the DVD Store, the order fulfillment process needs to be associated with a specific order. With seam, I can link those process variables directly into a seam component for use. The component doesn't really even need to consider that it is part of a process. It takes input and produces output. The fact that some of the state is being managed by a long-running process doesn't have to show it's head.
Overall, I like seam a lot. I was just looking for a way to get away from sticky glue code and micro-managing beans in XML. What I found was a fundamentally different way to write web applications. I think seam not only ups the bar for Java web frameworks, but I think it provides a clear counter to those who would argue that Java just can't compete with a more dynamic language like ruby. I'm a ruby fan, but when you take a look at AOP+annotations you'll see that you can indeed get a very high level of dynamic metaprogramming-like behavior out of Java. In fact, I'd say that Java approach is in some cases even more interesting since the same metadata can be more easily re-interpretted (multiple-interpretted?) at runtime by different aspects. This is far too contentious an argument to make here, and I don't want to take away from my real point that seam is very cool. I highly recommend taking a look at it.