delegate code. For example, many CRUD-based use cases can be implemented very
efficiently with a single boundary acting as a
facade for accessing multiple entities.
However, a direct one-to-one relationship
between the concepts in the ECB pattern and
packages inside a component can still be
beneficial. When packages are kept separate,
static analysis tools can be used more easily
to measure dependencies between packages. Furthermore, frameworks such as OSGi
and Jigsaw rely on the existence of separate
packages to expose public APIs.
In Java EE 6, the boundary is always realized with EJBs. The control layer can contain
either CDIs or EJBs, and the entity layer can
contain either JPA 2 entities or transient,
unmanaged entities. The final decision of
whether to use a CDI or an EJB in the control
layer does not have to be made up front. You
can start with a CDI and convert it into an
EJB down the road by using the @Stateless
annotation. You may need to use an EJB in
some cases, such as when you need to start a
subsequent transaction with @RequiresNew,
when you need to execute a method
asynchronously, or when you need to roll
back the current transaction by invoking
SessionContext.setRollbackOnly().
CDI, on the other hand, is more suitable
for integrating legacy code or implementing
Strategy, Factory, or Observer software
design patterns. All of these capabilities are
already built in and result in far less code
than with the Java SE counterpart.
When you are developing applications
with the ECB pattern, the ECB layering
should evolve iteratively and not be forced
in a top-down way. You should start with
the persistence (Entity) layer, perform
unit testing, and then implement the
boundary layer. For building the unit test,
the EntityManager and the associated transactions need to be created and managed
manually (as shown in Listing 7).
The persistence.xml file must also be
adjusted to handle standalone execution.
Specifically, the transaction type should be
changed to RESOURCE_LOCAL and a JDBC
connection (instead of a datasource) must be
configured explicitly, as shown in Listing 8.
When building the control layer, note that
its content will be the product of entity and
boundary layer refactoring. The reusable and
noncohesive parts of the boundary layer,
such as queries, algorithms, or validations,
along with cross-cutting concerns from
the entity layer, will be extracted into CDI
managed beans in the control layer.
USING THE CEC PAT TERN
The main purpose of the boundary in the
ECB pattern is to provide a clear separation
between business and presentation logic. By
definition, the boundary needs to be inde-
pendent of presentation logic. Even with the
many compromises you may make in your
architecture, a clear separation between
the business and UI technology is a must.
In practice, UI logic tends to vary more fre-
quently than business logic. It is common to
produce business logic that can be accessed
by a Web client (such as JSF 2), a rich client
(such as Swing or Eclipse RCP), and REST at
the same time.
Code Listing 5: A boundary implemented as an EJB session bean
package com.abien. messageme.business.messaging.boundary;
import com.abien. messageme.business.messaging.control.MessageStore;
import com.abien.messageme.business.messaging.entity.Message;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import javax.inject.Inject;
@Stateless
public class Messaging {
@Inject
MessageStore messageStore;
@Asynchronous
public void store(Message message){
}
}
Code Listing 6: A CDI bean from the control layer
package com.abien. messageme.business.messaging.control;
import com.abien.messageme.business.messaging.entity.Message;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
public class MessageStore {
@PersistenceContext
EntityManager em;
public void store(Message message){
}
}
Code Listing 7: Standalone JPA unit tests
package com.abien.messageme.business.messaging.entity;
import javax.persistence.*;
import org.junit.Test;
@Test
public void mappingSmokeTest() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
EntityManager em = emf.createEntityManager();
Entity Transaction tx = em.getTransaction();
tx.begin();
em.persist(new Message("duke"));
tx.commit();