본문 바로가기

Dev.../소프트웨어 아키텍처

[SA강좌] Part 4-14 Unit of Work 패턴

작업 단위(Unit of Work) 패턴

 

작업 단위 패턴의 정의

비즈니스 트랜잭션의 영향 받은 개체의 리스트를 관리하고, 변화의 기록과 동시 발생 문제의 해결를 조정하는 패턴이다.

그림 -17. 작업 단위 패턴의 구조

 

작업 단위 패턴의 설명

데이터베이스 내부와 외부로 데이터를 가지고 오는 경우 변화를 추적하는 것은 중요하다. 즉, 데이터가 데이터베이스에 쓰이지 않은 경우 이다. 비슷하게 새로운 개체를 삽입하면 개체를 생성하고, 임의의 개체를 삭제하면 개체를 삭제해야 한다.

작업 단위 패턴은 데이터베이스에 영향 받을 수 있는 비즈니스 트랜잭션 동안 모든 작업의 추적을 유지한다. 작업의 결과와 같은 데이터베이스 변경에 필요한 모든 작업을 설명한다.

작업 단위 패턴의 사용 이유

  • 수행성 향성
  • 비일관성을 제거
  • 참조의 무결성 관리
  • 데드락을 막음
  • 배치 업데이트를 도움

작업 단위 패턴은 어떻게 작업되는가?

  • 작업 단위의 개체를 추적하는 기술
  • 호출자 기록
  • 개체 기록
  • 작업 단위 컨트롤

작업 단위 패턴을 언제 사용하는가?

작업 단위 패턴을 다루는 근본적인 문제는 다양한 개체를 추적하는 것이다.

 

작업 단위 패턴의 예제 : 개체 기록을 이용한 작업 단위 패턴

작업 단위 패턴은 제공된 비즈니스 트랜잭션의 모든 변화를 추적할 수 있고, 명령이 전달되면 데이터베이스에 저장한다. 도메인 레이어는 레이어 수퍼타입 패턴에 해당하는 DomainObject를 가지고 있다. 이 패턴은 작업 단위 패턴 사이에 상호 작용을 하도록 한다. 설정 변화를 기록하기 위해 세 가지의 리스트 도메인 개체(new, dirty, removed)를 사용한다.

 

class UnitOfWork …

 

private List newObjects = new ArrayList();

private List dirtyObjects = new ArrayList();

private List removeObjects = new ArrayList();

기록 메서드는 위의 리스트의 상태를 관리한다. 리스트는 ID가 널이 아닌지를 확인하는 것 또는 dirty 개체가 새로 등록이 되지 않은지 같은 기본적인 명령을 수행한다.

 

public registerNew(DomainObject obj) {

Assert.notNull("id not null", obj.getId());

Assert.isTrue("object not dirty", !dirtyObjects.contains(obj));

Assert.isTrue("object not removed", !dirtyObjects.contains(obj));

Assert.isTrue("object not already registered new ", !newObjects.contains(obj));

newObjects.add(obj);

}

 

public void registerDirty(DomainObject obj) {

Assert.notNull("id not null, obj.getId());

Assert.IsTrue("object not removed", !removedObjects.contain(obj));

if(!ditryObjects(obj) && !newObjects.contains(obj)) {

dirtyObjects.add(obj);

}

}

 

public void registerRemoved(DomainObject obj) {

Assert.notNull("id not null, obj.getId());

if(newObjects.remov(obj)) return;

dirtyObjects.remove(obj);

if(!removedObjects.contains(obj)) {

removedObjects.add(obj);

}

}

public void registerClean(DomainObjects obj) {

Assert.notNull("id not null, obj.getId());

}

registerClean() 메서드는 여기서 아무일도 하지 않음을 주목하자. 공통 활동은 작업 단위 패턴에서 구분 맵 확인 패턴에 위치한다. 맵 확인은 도메인 개체를 메모리에 저장하기 위해 시간이 필요하다. 왜냐하면 같은 개체의 다중 복사는 정의되지 않은 행동에 있기 때문이다.

cmmit() 메서드는 개별 개체를 위한 데이터 매퍼 패턴을 위치시키고, 적절한 매핑 메서드를 포함한다. updateDirty()와 deleteRemoved() 메서드는 보이지 않지만, 기대되는 insertNew() 메서드와 같은 행동은 하지 않는다.

 

class UnitOfWork ...

 

public void commit() {

insertNew();

updatedirty();

deleteDirty();

}

 

private void insertName() {

for(Iterator objects = newObjects.iterator(); objects.hasNext();) {

DomainObject obj = (DomainObject) objects.next();

MapperRegistry.getMapper(obj.getClass()).insert(obj);

}

}

단위 작업 패턴에 포함되지 않은 것은 비일관성 오류 확인을 위한 읽기를 하는 임의의 개체의 추적이다. 최적 오프라인 록(Optimistic Offline Lock) 패턴에서 설명된다.

다음으로 필요한 것은 개체 등록이다. 첫째로 각 도메인 개체는 현재 비즈니스 트랜잭션을 제공을 위한 단위 작업이 필요하다.

 

class UnitOfWork ...

 

private static ThreadLocal current = new ThreadLocal();

public static void newCurrent() {

setCurrent(new UnitOfWork());

}

public static void setCurrent(UnitOfWork uow) {

current.set(uow);

}

public static UnitOfWork getCurrent() {

return (UnitOfWork) current.get();

}

추상 도메인 개체의 확인 메서드는 현재 작업 단위 패턴의 등록을 제공한다.

 

class DomainObject ...

 

protected void markNew() {

UnitOfWork.getCurrent().registerClean(this);

}

protected void markClean() {

UnitOfWork.getCurrent().registerClean(this);

}

protected void markDirty() {

UnitOfWork.getCurrent().registerClean(this);

}

protected void markRemoved() {

UnitOfWork.getCurrent().registerClean(this);

}

new와 dirty 개체를 마크를 기억하기 위한 도메인 개체를 지정한다.

 

class Album ...

 

public static Album create(String name) {

Album obj = new Album(IdGenerator.nextId(), name);

obj.markNew();

return obj;

}

 

public void setTitle(String title) {

this.title = title;

mark.Dirty();

}

제거된 개체의 등록은 추상 도메인 개체에 있는 remove() 메서드에 의해 다룰 수 있는 것은 보이지 않는다. 또한, Data Mapper 패턴에 registerClean() 메서드를 구현하는 경우 임의 새로운 로드된 개체를 등록은 필요하다.

마지막 작업은 작업 단위 패턴을 적절한 시점에 등록하고, 커미트하는 것이다. 이러한 작업은 명확하거나 불명확할 수 있다. 아래는 작업 단위 패턴의 명확한 코드를 보여준다.

 

clsss EditAlbumScript ...

 

public static void updateTitle(Long album, String title) {

UnitOfWork.newCurrent();

Mapper mapper = MapperRegistry.getMapper(Album.class);

Album album = (Album) mapper.find(albumId);

album.setTitle(title);

UnitOfWork.getCurrent().commit();

}

위의 코드는 어플리케이션의 가장 간단한 예이다. 불명확한 작업 단위 관리는 반복적 코딩을 피하는 것 만큼이나 적절할 수 있다.

 

class UnitOfWorkServlet ...

 

final protected void doGet(HttpServlet request, HttpServletRequest response)

throws ServletException, IOException {

try {

UnitOfWork.newCurrent();

handleGet(request, response);

UnitOfWork.getCurrent.commit();

} finally {

UnitOfWork.setCurrent(null);

}

abstract void handleGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException;

위의 서블릿 예제는 시스템 트랜잭션 제어에서 분명히 매우 간단하다. 만일 Font Controller 패턴을 사용하면, doGet() 보다 명령어들 주위의 작업 단위 관리를 지정하기 쉬울 수 있다.