본문 바로가기

Homo Faber/Patterns

EJB3의 Entity Access Object 패턴

세션빈의 비즈니스 로직에서 EntityManager API를 직접적으로 사용하는 방법은 비즈니스 로직 내에 엔티티 접근 코드를 산재하게 한다는 문제가 발생되는데, 이는 유지보수 측면에서 고역이다. Entity Access Object (EAO) 패턴은 비즈니스 로직에서 엔티티 접근 로직을 결합력을 적게 하며 코드 유지보수를 향상시킨다. 이는 비즈니스 로직에 영향을 주지 않고 내부의 엔티티 접근 코드를 쉽게 변경 가능하게 해준다. 만일 어플리케이션에서 EAO 패턴을 도입하면, JDBC나 EJB2 CMP, 혹은 다른 저장 메커니즘에서 JPA로 저장 티어 메커니즘 변경을 훨씬 쉽게 해준다.

선의 blueprint 웹 사이트에서 데이터 접근 객체에 대해서 더 자세하게 배울 수 있다. DAO의 내용에서 DAO를 EAO로 transfer object를 entity로 바꾸기만 하면 된다.

EAO 패턴은 비즈니스 로직에서 JPA를 사용함으로 데이터 접근 코드를 추상화시킨다. 보통은 도메인 객체(엔티티)에 대한 CRUD 오퍼레이션을 수행하는 모든 도메인 객체(엔티티)에 대해서 하나의 EAO 객체를 둔다. 아래 그림은 Bid 엔티티에 대해 정의된 EAO의 클래스 다이어그램이다.

사용자 삽입 이미지

그림에서 보는 바와 같이 BidEAO는 Bid 엔티티를 다루는 엔티티 접근 객체를 나타낸다. PlaceBid EJB와 같은 비즈니스 객체는 BidEAO를 사용해서 Bid 엔티티의 인스턴스를 저장하고 조회한다. 이는 엔티티를 저장하는 실질적인 저장 메커니즘을 PlaceBid EJB로부터 감춘다.

Bid 엔티티에 대해서 엔티티 접근 객체를 어떻게 구현하는지 살펴보자.

엔티티 접근 객체 구현
BidEAO 인터페이스에 대한 다음의 코드는 Bid에 대해서 수행될 수 있는 오퍼레이션을 노출시킨다.
public interface BidEAO {
   public Bid addBid(Item item, String bidId, double bidPrice);
   public Bid cancelBid(Long bidId);
   ...
}
개발자 중에는 엔터티 접근 객체에 대해 인터페이스를 사용하는 것은 많은 문제가 있다고 믿는 사람들이 있다. 하지만, EJB3 JPA와 Spring을 같이 사용할 때 많은 장점들이 있다. 인터페이스를 통해 EAO 인터페이스를 사용하는 클래스들에 영향을 미치지 않고 저장 제공 로직을 변경할 수 있다.

다음은 CRUD 오퍼레이션을 수행하는 EAO에 대한 구현 클래스이다.
public class BidEAOImpl implements BidEAO {
   private static String EM_NAME = "java:comp/env/actionBazaar";
   public BidEAOImpl() {}
   private EntityManager getEntityManager() {
      try {
         Context ctx = new InitialContext();
         return (EntityManager) ctx.lookup(EM_NAME);
      } catch (Exception e) {
         e.printStackTrace();
         return null;
      }
   }

   public Bid addBid(Item item, String bidderId, double bidPrice) throws BidException {
      EntityManager em = getEntityManager();
      if (em != null) {
         Bid bid = new Bid();
         ...
         em.persist(bid);
         return bid;
      }
      ...
   }

   public Bid cancelBid(Long bidId) {
      ...
   }
}
구현 클래스에서 코드는 직관적이다. getEntityManager 메소드는 컨테이너 관리(container-managed) EntityManager의 인스턴스를 검색하는 JNDI lookup을 사용하고 있다. EAO은 일반 POJO이기 때문에 DI(dependency injection)을 사용할 수 없으며, 따라서 EntityManager의 인스턴스를 검색하기 위해서 JNDI를 사용했다. 어플리케이션 관리(application-managed) 엔티티 매니저를 사용하기 원한다면 적절한 형태로 변경해야 한다. 나머지 코드는 비즈니스 로직에 있던 엔티티 접근 코드를 EAO로 옮기는 것에 불과하다. addBid 메소드는 EntityManager의 인스턴스를 얻기 위해서 getEntityManager를 사용하며 Bid 엔티티의 인스턴스를 저장한다.

EAO 코드에 대한 클라이언트가 PlaceBid EJB 라고 가정하자. 다음은 PlaceBid EJB의 코드이다.
@PersistenceContext(unitName="actionBazaar", name="actionBazaar")
@Stateless
public class PlaceBidBean implements PlaceBid {
   public Long addBid(String userId, Long itemId, double bidPrice)
         throws BidException {
      ...
      BidEAO bidEAO = EAOFactory.jpa.getBidEAO();
      Bid bid = bidEAO.addBid(item, userId, bidPrice);
      return bid.getBidId();
   }
}
위의 코드에서 EntityManager의 인스턴스를 얻기 위해 EAO에서 JNDI lookup을 사용했기 때문에 @PersistenceContext annotation을 사용하여 저장 단위(persistence unit)을 참조하고 있다. 그 다음에 EAO의 인스턴스를 생성하기 위해서 EAOFactory를 사용했다. EAO의 인스턴스를 생성한 후, 엔티티 오퍼레이션을 수행하기 위해 이를 사용할 수 있다.
코드에서 EAO 인스턴스를 생성하는 EAOFactory를 사용했다. 다음은 JPA를 통해서 EAO 인스턴스들을 생성하는데 사용할 수 있는 간단한 EAO factory이다.
public abstract class EAOFactory {
   public static final EAOFactory jpa = new JPAEAOFactory();
   public abstract ItemEAO getItemEAO();
   public abstract BidEAO getBidEAO();
   ...
}

public class JPAEAOFactory extends EAOFactory {
   public JPAEAOFactory() {}
   public BidEAO getBidEAO() {
      return (new BidEAOImpl());
   }
}

비즈니스 로직과 저장 코드간에 느스한 결합도를 갖는 장점은 명확하다. 만일 다른 저장 티어로 변경하고자 한다면 EAO 구현 클래스를 변경하기만 하면 된다. FireStorm/DAO와 Hibernate의 hbm2java와 같은 많은 도구와 유틸리티들이 엔티티에 대한 EAO를 생성하는데 도움을 주며, 따라서 EAO를 도입하는 것은 비교적 큰 일은 아니다. 이는 비록 몇개의 추가 클래스를 관리하는데 부가적인 코딩이 필요하지만, 따라하는데 좋은 기법이다.

EAO로 세션빈 사용하기
EJB3 세션빈은 POJO이기 때문에 엔터프라이즈 어플리케이션을 Java EE 컨테이너에 배포한다면 이들이 좋은 EAO의 후보들이다. EAO는 주입(injection)을 통해서 작업을 단순화시킬 수 있으며 EAOFactory를 사용할 필요가 없다. 만일 세션빈을 사용해서 EAO를 구현하기로 결정했다면 코드는 다음과 같은 것이다.
@Stateless
public class BidEAOImpl implements BidEAO {
   @PersistenceContext(unitName = "actionBazaar")
   private EntityManager em;
   public BidEAOImpl() {}

   public Bid addBid(Item item, String bidderId, double bidPrice) {
      Bid bid = new Bid();
      ...
      em.persist(bid);
      return bid;
   }

   public Bid cancelBid(Long bidId) {
      ...
   }
}
위의 코드는 이전 것보다 더 단순해보이며 유지하기 더 쉽다. EAO를 사용하는 코드도 다음과 같이 단순해진다.
@Stateless
public class PlaceBidBean implements PlaceBid {
   @EJB private BidEAO bidEAO;
   public Long addBid(String userId, Long itemId, Double bidPrice)
         throws BidException {
      ...
      Bid bid = bidEAO.addBid(item, userId, bidPrice);
      return bid.getBidId();
   }
}
세션빈을 사용한 EAO 구현은 단순하고 일반적인 것 처럼 보인다. 어떤 사람들은 이와 같은 방법에 대해서 불평을 하지만, EAO에 대한 세션빈을 사용하는 부수 효과로 자동적으로 선언적인 트랜잭션이나 컨테이너 관리 EntityManager를 받아들이기 때문에 심각하게 고민할 필요가 있다. 가벼이 여길 것은 아니다.
반응형

'Homo Faber > Patterns' 카테고리의 다른 글

SOA Service Benefit Pattern  (0) 2008.03.13
비즈니스 패턴  (0) 2008.03.11