Site icon Antonio's Blog

Several Architectural Styles with Java EE 7

If you are like me, in your career you came across Architects who want to homogenize every single application in the company : from the smallest web app to the biggest application. All projects have to conform to the 542 pages In-house Architectural Guide and develop a 6 tier application (it could be 5, 6, 7 or 8 tiers, look like architects are paid by the tier). This is wrong. In fact, you should follow the good old saying : the right tool for the right job, or, the right architecture for the right project’s needs. And you could go even further : the right language for the right application and so on. Due to my Java EE background, I’m sticking to Java EE 7 in this post (JSF front end, EJBs services, JAX-RS endoints and JPA entities). So, how many architectural styles you can create with Java EE 7 ? An infinite ;o)

I’m writing this blog because I was showing the code generated by JBoss Forge to a colleague and he went “Forge doesn’t create a 5-tier web application“… and so what ? In this post I’ll explain the default code generated by JBoss Forge (which I call Horizontal Service Style) and the other variants you can create from it.

Horizontal Services Style

When JBoss Forge generates a JSF web application with a REST interface, both JSF backing beans and JAX-RS endpoints use the EntityManager to deal with JPA Entities. The architectural style is as follow :

I call this Horizontal Services style because if you need to add a SOAP Web Service interface, you will write a BookSOAP which will also use an EntityManager and directly invoke the entities. To have an idea of the dynamics in the code, I’ll show you an extract of some CRUD operation on both the JSF backing bean and the REST endpoint :

[sourcecode title=”JSF BookBean” language=”java” highlight=”1,2,3,12,18,26,29,44,45″]@Named
@Stateful
@ConversationScoped
public class BookBean implements Serializable {

// …

@Inject
private Conversation conversation;

@PersistenceContext(unitName = "sampleJavaEEHorizontalPU", type = PersistenceContextType.EXTENDED)
private EntityManager em;

// …

public Book findById(Long id) {

return em.find(Book.class, id);
}

public String update() {
conversation.end();

try {
if (id == null) {
em.persist(book);
return "search?faces-redirect=true";
} else {
em.merge(book);
return "view?faces-redirect=true&id=" + book.getId();
}
} catch (Exception e) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(e.getMessage()));
return null;
}
}

public String delete() {
conversation.end();

try {
Book deletableEntity = findById(getId());

em.remove(deletableEntity);
em.flush();
return "search?faces-redirect=true";
} catch (Exception e) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(e.getMessage()));
return null;
}
}
}[/sourcecode]

This backing bean deals with navigation (each method returns a String, which is the name of the page to navigate to) and creates, deletes, updates the Book entity. Now if you look at the REST endpoint, you will see that the responsibilities are quite similar : the BookEndpoint creates, deletes, updates the Book entity and returns a Response. This is the code of the BookEndpoint:

[code title=”JAX-RS BookEndpoint” language=”java” highlight=”1,2,8,16,29,40″]@Transactional
@Path("/books")
public class BookEndpoint {

// …

@PersistenceContext(unitName = "sampleJavaEEHorizontalPU")
private EntityManager em;

// …

@GET
@Path("/{id:[0-9][0-9]*}")
@Produces("application/xml")
public Response findById(@PathParam("id") Long id) {
TypedQuery findByIdQuery = em.createQuery("SELECT DISTINCT b FROM Book b LEFT JOIN FETCH b.authors WHERE b.id = :entityId ORDER BY b.id", Book.class);
findByIdQuery.setParameter("entityId", id);
Book entity = findByIdQuery.getSingleResult();
if (entity == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(entity).build();
}

@PUT
@Path("/{id:[0-9][0-9]*}")
@Consumes("application/xml")
public Response update(Book entity) {
em.merge(entity);
return Response.noContent().build();
}

@DELETE
@Path("/{id:[0-9][0-9]*}")
public Response deleteById(@PathParam("id") Long id) {
Book deletableEntity = em.find(Book.class, id);
if (deletableEntity == null) {
return Response.status(Status.NOT_FOUND).build();
}
em.remove(deletableEntity);
return Response.noContent().build();
}
}[/code]

As you can see, the REST endpoint uses the new @Transactional annotation from Java EE 7 so it can handle the EntityManager in a transactional manner. So if you are like my colleague and don’t like this kind of architectural style, here is what I think of the pros/cons :

Advantages

Disadvantages

EJB Centric Style

This is the most common architectural style : it uses separation of concerns. The EJB layer deals with the EntityManager and other complex business logic, while the BookBean and BookEndpoint only deal with respectively JSF and REST concerns. This looks like this :


The BookEJB is a stateless session bean that does all the database access and orchestrates other external services. The code looks like this :

[code title=”BookEJB” language=”java” highlight=”1,5,10,14,18″]@Stateless
public class BookEJB {

@PersistenceContext(unitName = "sampleJavaEEEJBCentricPU")
private EntityManager em;

// …

public Book findById(Long id) {
return em.find(Book.class, id);
}

public void update(Book entity) {
em.merge(entity);
}

public void delete(Book deletableEntity) {
em.remove(em.merge(deletableEntity));
}
}[/code]

Both the BookBean and BookEndpoint get a BookEJB injected and delegate all the entity management. The REST endpoint can then look like this :

[code title=”BookEndpoint” language=”java” highlight=”1,5,13,24,31,35″]@Path("/books")
public class BookEndpoint {

@Inject
private BookEJB bookService;

// …

@GET
@Path("/{id:[0-9][0-9]*}")
@Produces("application/xml")
public Response findById(@PathParam("id") Long id) {
Book entity = bookService.findByIdWithRelations(id);
if (entity == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(entity).build();
}

@PUT
@Path("/{id:[0-9][0-9]*}")
@Consumes("application/xml")
public Response update(Book entity) {
bookService.update(entity);
return Response.noContent().build();
}

@DELETE
@Path("/{id:[0-9][0-9]*}")
public Response deleteById(@PathParam("id") Long id) {
Book deletableEntity = bookService.findById(id);
if (deletableEntity == null) {
return Response.status(Status.NOT_FOUND).build();
}
bookService.delete(deletableEntity);
return Response.noContent().build();
}
}[/code]

As you can see, the BookEndpoint deals with all the REST interactions (building a Response, error status…) and delegates the other concerns to the EJB. I’m not showing the BookBean code but it would look very similar.

Advantages

Disadvantages

Rest Centric Style

Thanks to JAX-RS 2.0, we have now a Client API, meaning that we can finally invoke a REST web service in a standard way. We can then put the REST endpoint in the center of our application and the BookBean will use the JAX-RS Client API to invoke it :

In this configuration the REST endpoint becomes the central point of your external invocations, deals with the EntityManager and do all the complex business logic. The BookEndpoint code looks similar to what I’ve showed you so far. The interesting part is in the BookBean that uses the Client API extensively:

[code title=”BookBean” language=”java” highlight=”1,2,7,8,21,29,32,46″]@Named
@ConversationScoped
public class BookBean implements Serializable {

// …

private Client client = ClientBuilder.newClient();
private WebTarget target;

@PostConstruct
private void setWebTarget() {
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String restEndointURL = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/rest/books/";
target = client.target(restEndointURL);
}

// …

public Book findById(Long id) {

return target.path("{id}").resolveTemplate("id", id).request(MediaType.APPLICATION_XML).get(Book.class);
}

public String update() {
conversation.end();

try {
if (id == null) {
target.request().post(Entity.entity(book, MediaType.APPLICATION_XML));
return "search?faces-redirect=true";
} else {
target.request().put(Entity.entity(book, MediaType.APPLICATION_XML));
return "view?faces-redirect=true&id=" + book.getId();
}
} catch (Exception e) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(e.getMessage()));
return null;
}
}

public String delete() {
conversation.end();

try {

target.path("{id}").resolveTemplate("id", getId()).request(MediaType.APPLICATION_XML).delete();

return "search?faces-redirect=true";
} catch (Exception e) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(e.getMessage()));
return null;
}
}
}[/code]

As you can see, the Client and WebTarget are used to do GET, POST, PUT and DELETE operations on the REST endpoint.

Advantages

Disadvantages

Conclusion

There is no good or bad architecture, there are several use cases and you can create the appropriate style for your needs. Here I just explain 3 different ways of writing the same thing with Java EE 7 but there are many more (another one would be to have several concerns into a single class, such as the Monster Component, another one is to create an extra (useless?) DAO layer…) :

I hope JBoss Forge 2 will be able to easily generate such diversity, I’ll contribute to make it happen… feel free to join the discussion.

Any other style that you use ? What about the ones I’ve presented here, any other ideas ? Download the code and do not hesitate to contribute to it.

Exit mobile version