12. The Session Façade pattern
비즈니스 처리를 위해서는 항상 서버측의 여러 객체들을 이용하게 된다.동일한 비즈니스 처리가 여러 클라이언트 프로그램에 존재한다고 전제하며,해당 처리를 위한 서버 객체들의 인터페이스를 변경하거나 처리 방법이 변경될 경우,코드관리가 어렵게 된다.물론,비즈니스 처리마다 여러 번의 네트워크 호출로 인해 성능상의 부담도 생기게 된다.문제는 데이터 접속과 워크플로우/비즈니스 로직이 클라이언트들 사이에 흩어져 있기 때문에,다수의 미분화된 세션/엔티티 빈들을 사용하는 경우에는 여러 번의 네트워크 호출로 인한 부담이 생길 뿐만 아니라 코드를 관리하기도 힘들어진다.
비즈니스 티어의EJB들을 여러 번 호출하면서 그 호출 사이에 컨텍스트를 수집하고,워크플로우나 세션 정보를 저장하는 클라이언트가 필요한 큰 단위의 인터페이스를 제공하는 것이Session Façade패턴의 목적이다.
구조
Session Façade pattern class diagram |
Session Facade pattern sequence diagram |
역할
· Session Façade :
특정 워크플로와 워크플로들의 집합이 제공하는 기능을 지원한다.실제 작업은 비즈니스 로직을 담당하는EJB에게 위임된다.
특정 워크플로와 워크플로들의 집합이 제공하는 기능을 지원한다.실제 작업은 비즈니스 로직을 담당하는EJB에게 위임된다.
· BusinessEJB :
기본적으로 필요한 비즈니스 로직을 포함하고 있다. Session Façade에 대한 레퍼런스를 갖고 있으면 안된다.
기본적으로 필요한 비즈니스 로직을 포함하고 있다. Session Façade에 대한 레퍼런스를 갖고 있으면 안된다.
의도
EJB클라이언트가 하나의 트랜잭션과 하나로 묶여진 네트워크 호출로 전체 비즈니스 로직을 수행하도록 한다.
결론
Session Façade패턴은 한번의 네트워크 호출만으로도 필요한 기능을 실행할 수 있도록 수행력을 강화하고,유스케이스를 중족 시키는데 사용되는 비즈니스 로직과 워크플로 로직들을 캡슐화 할 수 있는 레이어를 제공한다는 이점이 있다.상태 유지 세션빈에 적용되는 경우도 있지만,일반적으로 무상태 세션빈들을 레이어로서 활용한다.
Session Façade패턴은 오늘날 사용되고 있는 것들 중 가장 기본적인EJB패턴이다.퍼포먼스 이점을 제공할 뿐만 아니라 클라이언트와 서버 사이를 세션 빈의 레이러로 구분한다.또한,애플리케이션 내에서 모든 메소드들을 모든 유스케이스에 매핑한는 것과 같은 방법으로J2EE애플리케이션을 구분하는EJB시스템을 위한 표준적인 아키텍처를 제시해 준다.
Session Façade를 남용하거나 잘못 상요하는 경우 |
· 세션 빈 만능 클래스를 만든다:모든 유스케이스들을 하나의 세션 빈에 넣는다면 세션 빈은 거대해지고 개발 생산력은 떨어진다.관련된 유스케이스들만 모아 세션빈들을 구분해 놓아야 한다. · 도메인 로직을 세션 빈에 놓는다. :만일 상호 직접적인 관련이 없느 ㄴ다른 빈들을 연결하여 처리해야 하는 워크플로 로직과 관련된 유스 케이스가 아니라면,대부분의Session Façade에 정의된 메소드는 알맞은 엔티티 빈에 단순하게 전달되어야 한다. · 비즈니스 로직 중복:프로젝트가 진행될수록 세션 빈의 메소드들은 중복된 코드를 갖게 되는 경우가 발생한다.재사용 가능하고 유스 케이스에 독립적인 비즈니스 로직을 캡슐화 해서 세션 빈이나 단순한 자바 클래스로 구현된 서비스 레이어를 추가는게 좋다.프로젝트가 커질수록 이런 중복 로직을 찾거나 도출해낼 수 있는 리팩토링 세션을 갖는 것이 아주 중요하다. |
예제소스
Class Diagram
package webApp.news.dao; public class newsDAO { private newsDAO() { } public static StringBuffer NEWSMAXSEQ = new StringBuffer(); static { NEWSMAXSEQ.append("select nvl(max(SEQ),0)+1 from news \n"); } public static StringBuffer NEWSINSERT = new StringBuffer(); static { NEWSINSERT.append("INSERT INTO NEWS(SEQ, TITLE, INDATE, CONTENTS) VALUES(?, ?, sysdate,?) \n"); } public static StringBuffer NEWSSELECT = new StringBuffer(); static { NEWSSELECT.append("SELECT TITLE, INDATE, CONTENTS FROM NEWS WHERE SEQ = ?"); } public static StringBuffer NEWSLIST = new StringBuffer(); static { NEWSLIST.append("SELECT SEQ, TITLE, INDATE, CONTENTS FROM NEWS WHERE 1=1 \n"); } public static StringBuffer NEWSDELETE = new StringBuffer(); static { NEWSDELETE.append("delete from NEWS where SEQ=? \n"); } public static StringBuffer NEWSUPDATE = new StringBuffer(); static { NEWSUPDATE.append("update NEWS set \n"); NEWSUPDATE.append(" TITLE = ?, \n"); NEWSUPDATE.append(" CONTENTS = ? \n"); NEWSUPDATE.append("where SEQ = ? \n"); } } package webApp.news.value; import java.util.Date; import org.ezF.mvc.valueObject; public class newsVO implements valueObject { private int seq; private String titile; private String contents; private Date indate; public newsVO() { } public String getContents() { return contents; } public void setContents(String newContents) { contents = newContents; } public Date getIndate() { return indate; } public void setIndate(Date newIndate) { indate = newIndate; } public int getSeq() { return seq; } public void setSeq(int newSeq) { seq = newSeq; } public String getTitile() { return titile; } public void setTitile(String newTitile) { titile = newTitile; } } package webApp.news.ejb; import javax.ejb.CreateException; import javax.ejb.EJBException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import org.ezF.log.LogWriter; import org.ezF.mvc.valueObject; import org.ezF.sql.DBcon; import org.ezF.sql.DBconException; import webApp.news.value.newsVO; public class SENewsBean implements SessionBean { private ENNewsLocalHome localHome; private SessionContext ctx; public void ejbCreate() { } public void ejbActivate() { } public void ejbPassivate() { } public void ejbRemove() { } public void setSessionContext(SessionContext c) { ctx = c; try { Context ic = new InitialContext(); localHome = (ENNewsLocalHome) ic.lookup("ENNews"); } catch (NamingException ne) { throw new EJBException(ne); } } public void Insert(valueObject vo) throws DBconException { try { newsVO value = (newsVO)vo; value.setSeq(((Integer)localHome.create(vo).getPrimaryKey()).intValue()); } catch (CreateException ce) { LogWriter.log(0, "Insert: "+ce); throw new DBconException("News Helper Insert :"+ce); } } public void Update(valueObject vo) throws DBconException { try{ newsVO value = (newsVO)vo; ENNewsLocal newsLocal = localHome.findByPrimaryKey(new Integer(value.getSeq())); newsLocal.setNews(value); } catch (Exception e) { LogWriter.log(0, "Update: "+e); throw new DBconException("News Helper Update :"+e); } } public void Delete(valueObject vo) throws DBconException { DBcon connection = null; newsVO value = (newsVO)vo; try { ENNewsLocal newsLocal = localHome.findByPrimaryKey(new Integer(value.getSeq())); newsLocal.remove(); } catch (Exception e) { LogWriter.log(0, "Update: "+e); throw new DBconException("News Helper Delete :"+e.toString()); } } } package webApp.news.ejb; import java.io.BufferedReader; import java.io.StringReader; import java.sql.PreparedStatement; import java.sql.ResultSet; import javax.ejb.CreateException; import javax.ejb.EJBException; import javax.ejb.EntityBean; import javax.ejb.EntityContext; import org.ezF.log.LogWriter; import org.ezF.mvc.valueObject; import org.ezF.sql.DBcon; import org.ezF.sql.DBconException; import org.ezF.sql.DBconFactory; import webApp.news.dao.newsDAO; import webApp.news.value.newsVO; public class ENNewsBean implements EntityBean { private EntityContext context; public void ejbPostCreate(valueObject vo) { } public Integer ejbFindByPrimaryKey(Integer primaryKey) { return primaryKey; } public void ejbActivate() { } public void ejbLoad() { } public void ejbPassivate() { } public void ejbStore() { } public void setEntityContext(EntityContext ctx) { this.context = ctx; } public void unsetEntityContext() { this.context = null; } private int getMaxSeq() throws DBconException { DBcon connection = null; try { connection = DBconFactory.getDBcon(DBconFactory.PREPARECONNECTION); PreparedStatement stmt = connection.prepareStatement( newsDAO.NEWSMAXSEQ.toString(), ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY ); ResultSet rs = stmt.executeQuery(); rs.next(); return rs.getInt(1); } catch (Exception e) { LogWriter.log(0, "ENNewsBean getMasSeq = "+e.toString()); throw new DBconException(e); } finally { connection.close(); } } public Integer ejbCreate(valueObject vo) throws CreateException, DBconException { LogWriter.log(2, "ENNewsBean ejbCreate : "+newsDAO.NEWSINSERT); DBcon connection = null; newsVO value = (newsVO)vo; value.setSeq(getMaxSeq()); try { connection = DBconFactory.getDBcon(DBconFactory.PREPARECONNECTION); PreparedStatement stmt = connection.prepareStatement( newsDAO.NEWSINSERT.toString(), ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY ); stmt.setInt(1,value.getSeq()); stmt.setString(2,value.getTitile()); stmt.setCharacterStream(3, new BufferedReader(new StringReader(value.getContents())), value.getContents().length()); stmt.executeUpdate(); return new Integer(value.getSeq()); } catch (Exception e) { ejbFindByPrimaryKey(new Integer(value.getSeq())); LogWriter.log(0, "ENNewsBean ejbCreate : "+e.toString()); throw new DBconException(e); } finally { connection.close(); } } public void ejbRemove() { DBcon connection = null; Integer id = (Integer) context.getPrimaryKey(); try { connection = DBconFactory.getDBcon(DBconFactory.PREPARECONNECTION); PreparedStatement stmt = connection.prepareStatement( newsDAO.NEWSDELETE.toString(), ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY ); stmt.setInt(1,id.intValue()); stmt.executeUpdate(); } catch (Exception e) { LogWriter.log(0, "ENNewsBean ejbRemove : "+e.toString()); throw new EJBException (e); } finally { connection.close(); } } public void setNews(valueObject vo) throws DBconException { DBcon connection = null; newsVO value = (newsVO)vo; try { connection = DBconFactory.getDBcon(DBconFactory.PREPARECONNECTION); PreparedStatement stmt = connection.prepareStatement( newsDAO.NEWSUPDATE.toString(), ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY ); stmt.setString(1,value.getTitile()); stmt.setCharacterStream(2, new BufferedReader(new StringReader(value.getContents())), value.getContents().length()); stmt.setInt(3,value.getSeq()); stmt.executeUpdate(); } catch (Exception e) { LogWriter.log(e.toString()); throw new DBconException(e); } finally { connection.close(); } } } package webApp.news.helper; import javax.naming.InitialContext; import javax.rmi.PortableRemoteObject; import org.ezF.mvc.helper; import org.ezF.mvc.valueObject; import org.ezF.sql.DBconException; import webApp.news.ejb.SENews; import webApp.news.ejb.SENewsHome; public class newsHelper extends helper { public static SENews getRemoteObject() throws java.rmi.RemoteException, org.ezF.config.configException, javax.naming.NamingException, javax.ejb.CreateException { InitialContext ctx = new InitialContext(); Object protableObj = PortableRemoteObject.narrow(ctx.lookup("SENews"),SENewsHome.class); return ((SENewsHome)protableObj).create(); } public static void Insert(valueObject vo) throws DBconException { try { getRemoteObject().Insert(vo); } catch (Exception e) { throw new DBconException(e); } } public static void Update(valueObject vo) throws DBconException { try { getRemoteObject().Update(vo); } catch (Exception e) { throw new DBconException(e); } } public static void Delete(valueObject vo) throws DBconException { try { getRemoteObject().Delete(vo); } catch (Exception e) { throw new DBconException(e); } } } |
'Programming > Design Pattern' 카테고리의 다른 글
[펌] The Service Locator Pattern (0) | 2006.01.21 |
---|---|
[펌] The Business Delegate pattern (0) | 2006.01.21 |
[펌] The Message Façade Pattern (0) | 2006.01.21 |
[펌] The Session Façade pattern (0) | 2006.01.21 |
[펌] The Business Delegate pattern (0) | 2006.01.21 |