Several Architectural Styles with Java EE 7
Join the DZone community and get the full member experience.
Join For Freeif 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 abooksoap 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 :
@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; } } }
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 aresponse. this is the code of the bookendpoint:
@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(); } }
as you can see, the rest endpoint uses the new @transactional annotation from java ee 7 so it can handle theentitymanager 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
- this is a relatively flat architecture. you don’t have any extra layers, interfaces, oruselessabstractsomethingfactory pattern.
- very quick to develop, you develop following your needs
- perfect for easy applications (no complex business logic)
disadvantages
- no separation of concerns , one class does several things (e.g. the bookbean manages jsf navigation as well as handles entities)
- code duplication. the findbyid method is implemented the same way on both bookbean and bookendpoint
- heavy refactoring. if your application grows in complexity, you might end up refactoring and moving to an ejb centric architectural style
ejb centric style
this is the most common architectural style : it uses separation of concerns. the ejb layer deals with theentitymanager 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 :
@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)); } }
both the bookbean and bookendpoint get a bookejb injected and delegate all the entity management. the rest endpoint can then look like this :
@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(); } }
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
- seperation of concerns, each class does it’s own job
- if you add another type of interface (let’s say a soap web servies), you reuse the ejb layer
- perfect for complex applications
- scales better than the previous architectural style
disadvantages
- adds an extra layer to the application, but thanks to the no-interface view , in most cases you don’t even need to add an interface (so it’s just one class to add)
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 theentitymanager 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:
@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; } } }
as you can see, the client and webtarget are used to do get, post, put and delete operations on the rest endpoint.
advantages
- enforces a restful style
- perfect if you have several external rest clients
- eat your own dog food (your jsf backing beans are the first consumers of your rest endpoints)
disadvantages
- your jsf backing beans spend most of their time marshalling/unmarshalling the entities into/from xml (or json)
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…) :
- horizontal services : each service (jsf, rest, soap…) deals with the entitymanager, accesses the database and does the business logic
- ejb centric : each interface (jsf, rest, soap…) accesses an ejb layer that does most of the processing
- rest centric : the front views (jsf, javascript…) access a rest layer that does all the processing
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.
Published at DZone with permission of Antonio Goncalves, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Top 10 Pillars of Zero Trust Networks
-
Mastering Time Series Analysis: Techniques, Models, and Strategies
-
Merge GraphQL Schemas Using Apollo Server and Koa
-
How To Use Pandas and Matplotlib To Perform EDA In Python
Comments