Tuesday, December 02, 2008

Dynamic Dependency Injection

Dynamic Dependency Injection, Context Aware Injection or "on the fly" configuration.

Something that should be real easy, though is not thought of in Spring nor in Guice.

What is DDI?

The challenges that DI meets are well defined. And are already met. This is the next thing, in removing more glue code from you application code.
The target of DDI is to to remove as much as possible of non value bearing code. Value bearing code is the pure logic. Like Seam removes the need to get the data from the JSF forms and places them in the @In annotated field. And you are left only with the need to do actual value bearing logic.
Sample from here:

public String register()
{
List existing = em.createQuery("select username from User where username=#{user.username}")
.getResultList();

if (existing.size()==0)
{
em.persist(user);
log.info("Registered new user #{user.username}");
return "/registered.xhtml";
}
else
{
FacesMessages.instance().add("User #{user.username} already exists");
return null;
}
}
In the sample above your job as a developer is to do checks and proceed with the right branch of operation.
That is 100% value bearing code, since it does exactly what it's intention is:
  • Check the data store for duplicate values
  • Save the data if not exists
  • Report error if exists
The @In annotated field is populated based on the context in witch the EJB was run. In Spring and Guice, it is awkward when doing the same. Mostly the beans are configured once and relations are more dynamic.

Now DDI is another step forward. I say: Why remove only the setting and basic configuration? Let us remove the data fetching code and "script it".

Spring DI code would be:

@Autowired
private WarehouseQuantityDAO dao;

@Autowired
private OrderItemTO oi;

public boolean checkAvailableQuantity(){
int availableQuantity = dao.getQuantity(oi.getProductCode());
return availableQuantity > oi.getOrderedQuantity();
}

While DDI code would be something similar:

@Autowired
@Bound(name="JPAEntity_named_query",bind="orderItem.productCode")
private int availableQuantity;

@Autowired
private OrderItemTO orderItem;

public boolean checkAvailableQuantity(){
return availableQuantity > oi.getOrderedQuantity();
}

Though the end code seems similar, we did get rid of the DAO class.
The @Bound annotation takes the value of orderItem.productCode property and sets the parameter in the JPA named query. The JPA query returns an Integer, so we just set the value just prior to the calling of the method.

In this way we can remove a lot of code that deals with data storage, when the essence of the logic is not data store.

Meaning that if we change the register() method code from Seam sample, it might be not as obvious that the whole idea of the code is to check the data store. Though it would make the code shorter:
@Autowired
@Bound(name="JPAEntity_named_query",bind="user.username")
private List existing;

public String register()
{
if (existing.size()==0)
{
em.persist(user);
log.info("Registered new user #{user.username}");
return "/registered.xhtml";
}
else
{
FacesMessages.instance().add("User #{user.username} already exists");
return null;
}
}
But the logic is the same.
This could be done via AOP in Spring and maybe in Guice.

Referenced material:
http://www.springframework.org/
http://code.google.com/p/google-guice/
http://seamframework.org/

0 comments: