The Web Beans Manifesto

Posted by    |       CDI

The theme of Web Beans is:

  • Loose coupling with strong typing!

/Loose coupling/ comes in a variety of guises:

  • deployment time polymorphism
  • runtime polymorphism
  • lifecyle independence
  • decoupling of technical concerns
  • decoupling of event producers from event consumers

Loose coupling makes a system more /dynamic/. The system can respond to change in a well-defined manner. In the past, frameworks that attempted to provide the facilities listed above invariably did it by sacrificing type safety. For example, many frameworks use XML deployment descriptors, or string-based identifiers to wire components together and define their lifecycle. Other frameworks use some kind of string-based expression language to bind interceptors to components (the most disgusting example of this is the use of regexes in AOP pointcut expressions).

Web Beans lets components interact in a loosley coupled way without /ever/ resorting to XML or String-based identifiers. The only time component /names/ are used is for binding Web Beans components to a non-typesafe language such as Unified EL.

  1. deployment time polymorphism is provided via typesafe /binding annotations/ and /component types/
  2. runtime polymorphism is provided via typesafe binding annotations on /producer methods/
  3. lifecycle independence is a feature of the /contextual component model/
  4. technical concerns are decoupled by interceptors bound via typesafe /interceptor binding annotations/
  5. event producers and event consumers are decoupled via a typesafe /event object/ and event binding annotations

You don't see string-based identifiers in Web Beans code, not because the framwork is hiding them from you using clever defaulting rules (configuration by convention, as the Ruby folks say, or configuration by exception, as we used to say in the EJB3 expert group), but because there are simply no strings there to begin with!

This provides obvious benefits in that /any/ IDE can provide autocompletion, validation and refactoring without the need for special tooling. But it also provides a less-immediately-obvious benefit. It turns out that when you start thinking of identifying components, events or interceptors via annotations instead of names, you have an opportunity to lift the semantic level of your code.

Web Beans encourages you develop reusable annotations that model concepts, for example,

  • @Asynchronous,
  • @Mock,
  • @Secure,
  • @Action or
  • @Updated,

instead of non-reusable compound names like

  • asyncPaymentProcessor,
  • mockPaymentProcessor,
  • SecurityInterceptor,
  • LoginAction or
  • DocumentUpdatedEvent.

When I look at Java code today, and I see these kind of compound names, it reminds me of old-fashioned naming conventions for variables in dynamic languages, which embed the type of the variable in the variable name, for example str_username. These naming conventions were a clear workaround for the fact that the language had no primitives for expressing typing information.

Compare the following observer methods:

void onDocumentUpdate(@Observes DocumentUpdatedEvent event) { ... }
void onDocumentUpdate(@Observes @Updated Document doc) { ... }

Both are correct, but the second approach is more Web-Beansy. The @Updated binding annotation models a reusable notion. We can use it for other events:

void onDocumentUpdate(@Observes @Updated Order order) { ... }

And notice how much more /literate/ this code is.

Or compare the following implementations of Login:

public 
@SessionScoped
@Component 
@Interceptors(SecurityInterceptor.class)
class Login { ... }
public
@SessionScoped
@Secure
@Component 
class Login { ... }

The second form is more Web-Beansy - the code is more decoupled and more literate.

The nirvana of this kind of literate programming is a new feature of Web Beans called /stereotypes/. A stereotype lets you encapsulate scope, interceptor bindings, component type, etc, into a single package that models a /role/ in the application architecture. For example, instead of defining:

public 
@RequestScoped
@Named
@Secure
@Transactional
@Component
class LoginAction extends BaseAction { ... }

We could create a stereotype:

@RequestScoped
@Named
@Secure
@Transactional
@Component
@Stereotype(requiredTypes=BaseAction.class)
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action {}

And reuse it for all our actions:

public @Action class LoginAction extends BaseAction { ... }
public @Action class UpdateDocumentAction extends BaseAction { ... }

Another example is a stereotype for singletons. Of course, we /could/ write:

public 
@ApplicationScoped 
@Component 
class ReferenceData { ... }
public 
@ApplicationScoped 
@Mock 
class DummyReferenceData extends ReferenceData { ... }

But I would prefer:

@ApplicationScoped
@Component
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface Singleton {}
public 
@Singleton 
class ReferenceData { ... }
public 
@Mock 
@Singleton 
class DummyReferenceData extends ReferenceData { ... }

Well, this stereotype did not save much typing. But on the other hand, it tells us more about the ReferenceData class. The code is more literate and understandable. Even better, it's /much/ easier for me to find all singletons in my code, or to add an interceptor to singletons. It also makes it easier to ensure that certain patterns are followed consistently in a large project.


Back to top