Application에서 Persistent Data를 다루는 방법엔 여러가지가 있다. RDBMS를 사용하거나 단순히 txt로 저장할 수도 있고 네트워크를 통해 외부 시스템을 사용할 수도 있다. 그리고 각 저장 방식에는 메커니즘이 다르기 때문에 구현방법도 모두 다르다. 그렇기 때문에 Persistent Logic은 외부 환경과 Dependency가 매우 강하다. 따라서 Application Logic과 Persistent Logic은 섞이면 안되며 서로 독립된 레이어에 존재해야 한다. 만약 그렇지 않고 로직이 섞인 상태에서 Data Source가 바뀌게 된다면 Application Logic까지 영향도(Force)가 전달되어 변경 전파(Change Propagation)가 발생하게 된다. 

 

[그림 1] DAO Class Diagram

  [그림 1] Data Access Object Pattern은 특정 Data Source에 접근하는 로직을 추상화하고 캡슐화 한다. DAO는 DataSource의 메커니즘을 구현하고 단순한 API만을 Client에게 제공한다. (이때, DataSource에서 사용하는 ErrorCode나 Exception같은 것도 역시 상위 레이어로 전달하면 안되며, 모두 wrapping하여 상위로 전달 해야한다.) 이제 Client에서는 DAO를 생성하고 저장해야할 Value Object를 생성하여 DAO에 전달하기만 하면 된다. 이렇게 Layer를 분리하면 DataSource가 Oracle에서 외부 네트워크 시스템으로 변경되어도 Client의 수정없이 DAO만 변경하면 된다.

 

[그림 2] DAO Sequence Diagram

  [그림 2]는 Client가 DAO를 통해 데이터를 읽을때의 Sequence Diagram을 나타낸다. DAO는 Data Source를 통해 데이터를 읽어와서 Value Object로 변환하여 반환하고 Client를 Value Object를 통해 실제 데이터를 사용한다.

 

  수도 코드로 예를 들면, 회원 정보를 저장하는 DAO는 아래와 같이 구현될 수 있다. 

  만약 Data Source와 관련된 로직을 추상화 할 수 있다면, 아래와 같이 Generic으로 추상화하여 CommonDAO를 만들 수 있다. 그렇게 되면 앞으로 생성되는 DAO class들은 정의만 하면 되고 중복 구현은 추상화 되어 없앨 수 있다. 그리고 특정 서비스에서만 사용되는 로직들만 확장하여 사용하면 된다.

 

 

[그림 1. 우리가 기대하는 Singleton]

  Runtime에 유일한 상태를 공유하기 위해서 singleton을 많이 사용하는데, 우리가 singleton class를 만들때 기대하는 바는 위 [그림 1]과 같다.  Singleton은 다른 object들과의 dependency 관계가 쉽게 맺어지고 사용될 수 있도록 구현되어 있다. 이게 singleton의 장점이자 단점으로 다가오는데, 시간이 지나면서 개발자들은 singleton이 존재해야 할 레이어에 대한 인지가 점점 떨어진다. 특히 급하게 처리되어야 할 문제들이 쉽게 해결할 수 있는 singleton으로 모이게 되는데, 이런식으로 처음 의도와는 다르게 여러 service들이 하나의 singleton을 의존하게 된다. 그리고 여러 service들에 의해 많은 기능들이 추가 되면서 점점 large class가 되어 간다. 심지어 callback을 주기위해 역참조하는 경우도 발생한다.

 

[그림 2. Large class가 되어버린 Singleton과 Dependeny rot]

  [그림 2]처럼 service A를 위해 만들어졌던 func1이 다른 service들이 사용하기 시작한다. service A의 요구 사항이 변경되어 func1이 변경된다면 이를 사용하는 모든 service들에게 변경 전파(Shotgun Surgery)가 이루어진다. 그리고 이러한 coupling 문제를 개선하기 위해 singleton을 리팩토링 할 때의 영향도(Force)는 어플리케이션 전체가 된다.

 

  Singleton은 class dependency라서 mock으로 대체 될 수 없다. 이는 singleton을 사용하는 service들의 unit test를 작성하는데 어렵게 만든다. singleton의 변경에 따른 영향도는 굉장히 높지만 그 변경에 대한 검증이 매우 부족한 상태라는 의미다.

 

  Singleton에 대한 class dependency 문제를 해결하기 위해서는 의존 관계가 외부로부터 injection 되어야 한다. 그리고 client는 이렇게 주입받은 object가 singleton instance인지 일반 object intacne인지 모르는 상태에서 사용되어야 한다. 또한 이 object는 interface에 의존하도록 디자인 되어야 한다. 이는 통제된 인터페이스를 통해 정의된 서비스를 제공하도록 디자인됨을 의미한다. 그리고 해당 object의 유일성은 Service Locator같은 녀석이 책임지고 dependency wiring은 IoC Container로부터 injection 받도록 디자인 해볼 수 있다.

+ Recent posts