태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

'repository pattern'에 해당되는 글 2건

  1. 2010/01/20 Entity Framework 에 대한 변론 (4)
  2. 2009/02/10 Repository Pattern

Entity Framework 에 대한 변론

.Net/.Net Framework Design Guide Line 2010/01/20 13:41
C-Thinker 님의 많은 포스트를 읽고 매우 상쾌해지는 느낌과 동시에 많은 도움이 되었습니다.

하지만  Entity Framework 4.0 - 아직도 헤메는 MS 라는 포스트는 많은 부분에 동의를 하지 못하였고, 이렇게 EF에 대한 변론(?) 을 하는 포스트를 작성하게 되었습니다.

일단, 포스트 내용을 반박하게 되는 글이 될 것과 C-Thinker님의 글에서 EF에 대한 문제의식을 제대로 집어 내지 못한 것이 포스트를 작성하는 것에 대해 조심 스럽게 만듭니다. 하지만 논쟁을 통해서 서로가 발전할 수 있다는 변증법 사고에 기반을 두고 EF에 대한 저의 의견을 적어 봅니다.


Table Module Pattern 의 개발 모델 문제에 대해 말 해 보도록 하죠.

이상한 모습도 그렇지만 아직도 테이블 기반 (Table Module Pattern) 의 애플리케이션 개발 모델을 버리지 못하고 있는 느낌이다.


우선 EF가 테이블 기반인가? 테이블로 부터 EDM을 생성하므로 테이블 기반이라고 말 할 수 있겠네요. 하지만 마틴 파울러가 말한 Table Module Pattern이냐? 라는 것에는 이견이 있군요. 파울러의 Table Module Pattern은 Data Access Layer를 작성할 때 1:1 테이블로 사상된 일명 DataSet을 통해 작업을 하느냐. 다시 말하면 직접적인 RecordSet을 통하여 작업 하느냐 라고 봅니다.  개발자가 이용하는 관점에서는 EF는 Domain Model 이며, Data Mapper를 구현 할때 Table의 메타 데이터를 이용한다. 라고 볼수  있네요.

매퍼 자체가 매핑을 하기 위해서 Gateway를 이용해야 하는데 Gateway는 Table Data Gateway, Row Data Gateway, Active Record를 사용할 수 있으나,  이것은 매퍼를 구현하는 방법에서의 적절한 것을 선택하는 것의 문제이지 Table을 기반으로 생성된 EDM이 문제일 수는 없다고 봅니다.





ORM과  Persistence Ignorance를 아래의 글로 언급하셨는데

ORM 의 근본 목적이라면 OO (Object Orientation) 프로그램 상에서 생성되고 관리되는 객체들과 관계형 (Relation) 으로 구성된 데이타베이스와의 구조적 차이 (Impedance Mismatch) 를 극복하기 위한 반복되는 코드를 줄이고 어떻게 저장되는 지에 대해서는 상관하지 않겠다 (Persistence Ignorance) 는 관점에 있다고 볼 수 있다.


저는 "ORM 이라는 것은 Object Oriented 객체와 RDBMS와의 임피던스 미스매치(Impedance Mismatch)를 조절하며 데이터 영속화(Data Persistence)를 자동화(Automate) 함으로써 비지니스 로직을 작성하는 개발자에게 Domain Model로 작업하도록 지원해 주는 것이 목표 라는 것"이라고 정리하고 있는데,  artofsoftwaredev님의 의견과 거의 같으면서도 다름이 느껴집니다.


Persistence Ignorance는 Jimmy Nilsson 의 책 Applying Domain-Driven
Design and Patterns: With Examples in C# and .NET (Addison-Wesley Professional, 2006)에서 제시한 용어로써 "keep your domain model decoupled from your persistence layer " 로써 정확한 정의가 모호합니다.

Eric Evans의 DDD 방법론과 마틴 파울러의 생각을 정의해 보자면  "도메인 모델이 저장 장소에 무지하도록 한다." 라고 할 수 있습니다. 저장장소가 인-메모리 가 되었건 , 파일 시스템이 되었건, SQL Server가 되었건 몰라도 영속화를 할 수 있어야 함을 의미 하는 것으로 , "어떻게" 보다는 "어디"에 또는 "무엇"에 영속화 되는가에 관심이 있겠다 말할 수 있습니다.

영속화 매체가 파일 시스템, 또는 오라클이냐, MySql이냐, MS SQL 이냐를 도메인 모델이 인지를 하지 않고 특정 영속화 인터페이스를 사용한다면 영속화 할 수 있다는 개념으로 Repository 패턴을 생각해 볼 수 있습니다.





객체와 테이블이 1:1의 관계이냐?
데이타베이스로 부터 모델을 생성하고 코드만 자동으로 생성을 하지 않은 후, 그 모델에 맞는 객체를 만들어야 한다. 예를 들어 주문 테이블이 있다고 하면 이 테이블로 부터 주문이라는 모델을 만들고 이 주문이라는 모델이 갖고 있는 필드들을 갖고 있는 객체를 만들어야 한다는 얘기다. 여기서 다시 객체와 테이블의 1 : 1 관계를 벗어나지 못한다. 그래도 여기까지는 큰 문제가 없다고 봐 줄 수도 있다. 정작 문제는 이 객체들을 데이타베이스에 넣고 빼는 과정을 자동화 해 주는 ObjectContext 이다.

우선 EF는 상속받아 구현되는 컴플렉스 타입을 지원하며 1:1 관계라는 것 자체가 사실과 다릅니다.
OO의 객체를 영속화 하기 위해서 테이블을 모델링 할 때 상속받아 구현된 모든 객체를 테이블과 1:1로 구현 할 수도 있고, 타입으로 구분하여 상속받아 구현된 모든 객체를 한개의 테이블에 저장할 수도 있습니다. 이 두가지 모두 전략에 따라 적절한 패턴의 선택 문제입니다.




그럼 문제가 된다고 말씀하시는 ObjectContext의 POCO 지원시에 의존성 문제를 볼까요
객체들을 자동으로 생성하지 않았으므로 POCO 를 사용할 경우 ObjectContext 는 어떤 객체들이 어떤 모델들과 어떤 관계를 갖고 있는지 알 수가 없게 된다. 따라서 ObjectContext 를 상속받는 객체를 만들어 사용을 해야 하는데 문제는 각 POCO 객체를 Entity Framework 에서 Entity 로 인식을 하고 처리를 하기 위해 이 POCO 객체타입으로 EntitySet 을 만들어야 한다는 것이다. 다시 말해서 도메인 객체를 데이타 엑세스 레이어에서 알고 있어야 한다는 얘기다. 어라... 이건 뭐가 거꾸로 되도 한참 거꾸로 됐다. 프로젝트를 UI, 도메인, 데이타 레이어로 나누었을 경우 UI -> 도메인 -> 데이타 순서가 아니라 UI -> 데이타 -> 도메인 순서의 의존성 (Dependency) 가 생긴다.


객체를 영속화 하기 위해서는 상태 관리, CRUD의 인터페이스를 제공하는 그 어떤것이 필요하게 되는데 ObjectContext가 그 역할을 하는 것입니다. POCO의 영속성을 지원하기 위해서는 지원하는 '그 무엇'이 있어야 합니다. 그것이 Transaction Script가 되었건, Table Module이 되었건 말이죠. EF 4에서는 POCO를 지원하기 위해 POCO를 EDM에 추가하는 것으로 해결하는 것이죠.

도메인에서 만들어진 객체를 데이터 레이어에서 알아야 하는 것이 문제라고 말씀하셨는데, 언어의 Primative 타입만을 데이타 레이어에 전송하지 않고 Object로 전달하기로 결정하는 순가. DTO라고 하는 객체가 필요하게 됩니다. DTO는 도메인 모델과 데이터 모델에서 알지 않고서는 작업이 되지 않기 때문에 EDM의 엔터티 자체가 DTO 역할을 하면서 엔터티의 상태 관리와 CRUD 및 데이터 매핑 작업을 ObjectContext가 해 주는 구조 입니다.

다시 말해서 DTO가 필요해 지는 순간 도메인 레이어와 데이터 레이어가 동시에 알아야 할 필요성이 생기고, 이것을 의존성이라고 말한다면 Primative Type 만으로 작업하는 것 이 외에는 방법이 없다고 생각합니다.

의존성의 순서는 둘째 치고, 의존성이 있다는 얘기는 변경발생시 두군데의 변경이 불가피 하나는 얘기이고 굳이 두개로 나눌 이유가 없다는 말일 수도 있다. 이는 다시말해 도메인 로직과 데이타 엑세스가 분리 될 수 없다는 것을 말하고 결국 데이타베이스에 대한 의존성은 줄지 않았다는 얘기가 된다.


의존성 문제는 개발자의 코드 자체가 레이어 간의 의존성을 가지냐의 관점으로 봐야 옳으며, 중간자 (Mediator)가 의존성을 알고 있느냐의 관점으로 보는 것이 아니라고 생각합니다. 데이터베이스의 변경 마다, 데이터 레이어와 도메인 레이어의 수직 변경을 메터데이터(EDM)에 위임하고 , EDM의 업데이트로 변경을 반영함으로써 , 수직 변경을 최소화하는 것은 옳은 관점이라고 생각합니다.


artofsoftwaredev 님의 말씀대로 모든 레이어가 Ignore 하고 서로의 변경에 전혀 관심 갖지 않아도 된다면 얼마나 좋겠습니까 만은 , 그 개념은 아직 이상적이며 완벽한 솔루션이 없습니다.


현재로써는 "관심의 분리 , Seperate of Concern"로 "한 곳에서만 관리하도록 하자 Single Point Management "의 관점으로 디자인 하는 것이 옳다고 볼수 있습니다.


EF가 완벽하냐? 라고 묻는다면 대답할 수 없습니다만, 디자인 관점이 옳으냐 라고 묻는다면 "예" 라고 하겠습니다.
저작자 표시
이올린에 북마크하기(0) 이올린에 추천하기(0)

현재글 : Entity Framework 에 대한 변론 posted By - 반더빌트 2010/01/20 13:41
Trackback 0 : Comments 4

Trackback Address :: http://smack.kr/trackback/357 관련글 쓰기

  1. C-Thinker 2010/01/20 16:58 Modify/Delete Reply

    안녕하세요. 변론을 하신다기에 들어와 봤습니다. ^^
    우선 지적하신 부분들에 대해서 변명을 하겠습니다.

    Table Module 에 대해서는 그 패턴을 사용했다기 보다는 개념적인 기반이 되어 있다고 표현을 한 것입니다.
    ORM 과 Persistence Ignorance 에서는 솔직히 무슨 말씀을 하려 하시는지 잘 이해가 가지 않는군요.
    객체와 테이블이 1:1 관계라고 표현한 부분에서는 테이블이라고 썼는데 사실 그 전에 언급한 모델을 그렇게 써 버렸군요. 도메인 모델을 지원을 하려면 기존의 도메인 모델과 EF가 만들어낸 모델과의 자유로운 매핑이 가능해야 한다고 생각합니다.
    POCO 와 ObjectContext 에 대해서 말씀드리자면, 개발자가 어떤 이유에서든 자유의지로써 의존성을 만들게 될 수는 있지만 프레임워크에서 강제해서 발생되는 의존성하고는 많은 차이가 있다는 생각입니다. 개발자가 만든 의존성도 나중에 문제가 되는데 프레임웍에서 의존성을 갖도록 강제를 당한다면 추후 확장성에 많은 문제가 발생할 소지가 많습니다.

    어쨋든 다른 관점에서 볼 수 있는 기회를 주신것에 감사드립니다.

    • 스맥 반더빌트 2010/01/20 18:04 Modify/Delete

      네 Thinker님 또 뵙게 되었습니다.

      Table Module Pattern에 대해서는 L2S이 가깝다고 할 수 있습니다. EF는 L2S에 논리 모델이 더해진 것으로 생각할 수 있네요, 또한 EF Table Module Pattern 을 기반으로 한다 해도 그것이 문제인가? 라는 의견입니다.

      "PI는 PI 의 정의 및 룰이 완벽하지 않은 데다가, 전적으로 ORM의 책임은 아니다 라는 의미이며, 도메인 모델은 Repository 패턴 등을 이용함으로써 PI를 일정부분 충족 시킬수 있다" 라는 의미입니다.


      POCO와 ObjectContext의 문제에 대해서는 Thinker님의 문제 의식에는 동의 하나, POCO를 영속화하기 위한 또다른 방법의 필요가 요구됩니다. 이것을 ObjectContext가 담당하도록 하는 일관적인 패턴에 대해서 동의 한다는 말입니다.

      DTO 예를 든 것은 불가피한 의존성의 발생 현실을 말하고자 함이며 , 의존성이 없이도 작동하는 해결책이 있다면 저도 매우 기뻐하고 싶습니다.

  2. 권효중 2010/01/21 16:41 Modify/Delete Reply

    EF 베타버전을 아직 저도 써보지는 않았지만 지난 PDC2009 의 EF 세션에서 보여준 데모에는 객체에서 데이타베이스를 생성하던데요..

    • 스맥 반더빌트 2010/01/21 21:01 Modify/Delete

      네 효종님

      EF는 .NET Framework 3.5 SP1에 정식 포함 되어 V1 이었으나, 다음 버전은 .NET Framework 4.0에 포함되어 EF4 로 불리웁니다.

      EF V1에서는 Entity를 DB에서 부터 생성 + 모델 디자이너에서 생성 할 수 있으나, Custom Entity에 대해서 DB에 반영 할 수는 없었습니다.

      EF 4.0(V2) 에서는 EDM 디자이너에서 생성한 Custom Entity를 DB에 테이블로 생성 할 수 있도록 기능이 업그레이드 되었답니다.

      EF 4.0의 새로운 기능은 EDM으로 부터 DB로의 테이블 생성, POCO 지원 등이 있습니다.


[생각을 적어 주세요~ 댓글은 작은 인연의 씨앗입니다.]


Repository Pattern

아키텍처 2009/02/10 10:59
Repository Pattern은 디자인 패턴이 아닌 아키텍처 패턴 영역에서 다루어 집니다..

디자인 패턴 책만 본다면 Repository pattern에 친숙하지 않을 것이며, DDD가 아직 개발자 사이에 보편화 되지 않아, 개념을 파악 할 수 있는 좋은 예가 없이는 학습이 어렵습니다.

Repository pattern의 개념을 예제를 가지고 쉽게 설명한 블로그 포스트를 번역해서 여기에 올립니다.
http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/10/08/the-repository-pattern.aspx


The Repository Pattern

Introduction

 datasource로 부터 data에 접근할 때 , 우리는 잘 정의된 몇가지 방법을 가지고 있다. e.g 마틴 파울러는 그의 저서 PoEE 에 몇가지를 기술 하였다.

1. Table data gateway
2. Row data gateway
3. Active record
4. Data mapper

DDD를 적용 하여 domain model의 data에 접근할 때, 우리는 Repository pattern이라고 불리우는 것을 이용한다.

What is a Repository

마틴 파울러 writes:

"Repository는 domain과 data mapping layer 사이의 중간자 이다. in-memory-domain object collection 처럼 작동한다. Client Object는 선언적인 쿼리 명세를 생성하고, Repository에 submit한다. Objects는 Repository에 추가 되거나, 제거 될 수 있다. 단순한 Object의 콜렉션으로서 Repository에 의해 캡슐화 된 매핑코드는 뒷단에서 지정된 동작을 하게 된다. 개념적으로, Repository는 data store에 영속화된 object의 집합을 캡슐화 하고, 그에 대한 operation을 수행한다. persistance layer에 좀 더 object-oriented view를 제공한다. 또한 Repository는 저장에 있어서 깔끔하게 분리되고, domain고 domain mapping layer 사이에 단방향 의존의 목적을 지원한다."


Implementation

DDD에서 우리는 연관(Aggregates)의 관념을 가진다.  aggregate란 "데이터의 변경을 위해 하나의 단위로 취급되는 관련된 objects의 집합 이다. 외부 참조는 Aggregte의 멤버중 root로 디자인된 하나의 object 만으로 제한된다.  일관적인 룰의 집합은 aggregate의 경계에로 적용된다. "

일반적으로 domain model의 한개의 aggregate당 하나의 Repository가 정의된다. 이 말은 : Entity당 하나의 Repository를 가지는 것이 아님을 의미한다. 우리가 간단한 주문관리시스템을 보고 있다면 , Order Entity는 Order Aggregate의  root object가 될 것이고 , 우리는 Order Repository를 가지게 될 것이다.

Repository의 인터페이스(또는 계약)은 domain model의 한 부분으로 고려되며, Repository의 구현은 Infrastructure에 specific 하며, Domain model에 포함되지 않는다.

Aggregate로 일을 할때 우리는 대부분의 시간을 정확히 3개의 영속성에 관계된 작업을 필요로 한다.
1. 그 id를 이용하여 aggregate 얻기 (primary key)
2. repository에 새로운 aggregate 추가하기
3. repository로 부터 aggregate 제거하기

문맥에 따라서 또다른 영속성과 관려된 작업을 필요로 할  수 있겠지만, 최고로 일반적이고, 빈번하게 사용되는 것은 위에 언급된 3개의 작업이다. 이것은 우리가 Repository에 단순한 계약(또는 인터페이스)를 이끌어 낼 수 있게 한다.


public interface IOrderRepository{    
Order GetById(int id);
void Add(Order order);
void Remove(Order order);
}


위의 샘플에서 우리는 Order의 Primary key는 대리키이고, 타입은 int 임을 가정한다.

이제 우리의 domain model에서 구별된 Product aggregate라는 두번째 aggregate가 있다고 가정하자. 그러면 우리는 즉시 우리가 가지고 있는 Order Repository를 참고하여, 기대되는 Repository 인터페이스를 추출해 낼 수 있다.
 

public interface IProductRepository{    
Product GetById(int id);   
void Add(Order order);    
void Remove(Order order);
}


이제는 매우 유사한 위의 예를 보듯, 제너릭 파라미터를 이용하여 인터페이스를 일반화 시킬 수 있다.


public interface IRepository<T>{    
T GetById(int id);    
void Add(T entity);   
void Remove(T Entity);
}


이제, Order와 Product의 인터페이스는 이렇게 된다.

public interface IOrderRepository : IRepository<Order> { }
public interface IProductRepository : IRepository<Product> { }




A Fake Repository

테스팅을 목적으로 우리는 product aggregate를 위한 가짜 Repository를 구현 할 수 있다. 이 경우에 미리 정의된 상품에 대한 dictionary가 될 것이다. 각각 product의 key는 product의 primary key가 되고, 우리의 간단한 샘플은 product entity로만 구성된 aggregate가 된다.


public class Product{    public virtual int Id { get; set; }    public virtual string Name { get; set; }    public virtual int ReorderLevel { get; set; }    public virtual bool Discontinued { get; set; }}


이것은 매우 간단한 entity이지만 우리의 목적에 꼭 들어 맞는다. 이제 가짜로 구현된 Repository를 보자

public class ProductRepositoryFake : IProductRepository{    private readonly Dictionary<int, Product> dictionary;     public ProductRepositoryFake()    {        dictionary = new Dictionary<int, Product>();        dictionary.Add(1, new Product {Id = 1, Name = "Product 1", ReorderLevel = 10, Discontinued = false});        dictionary.Add(2, new Product {Id = 2, Name = "Product 2", ReorderLevel = 15, Discontinued = false});        dictionary.Add(3, new Product {Id = 3, Name = "Product 3", ReorderLevel = 10, Discontinued = false});        dictionary.Add(4, new Product {Id = 4, Name = "Product 4", ReorderLevel = 12, Discontinued = false});        dictionary.Add(5, new Product {Id = 5, Name = "Product 5", ReorderLevel = 20, Discontinued = true});    }     public Product GetById(int id)    {        return dictionary[id];    }     public void Add(Product product)    {        dictionary.Add(product.Id, product);    }     public void Remove(Product product)    {        dictionary.Remove(product.Id);    }}</pre><br />
생성자에서 int를 key(우리의 경우 primary key가 되는)로 가지고 Product 인스턴스를 값(value)으로 가지는 dictionary를 인스턴스화 시켰다. 그리고 dictionary를 채우는 몇개의 샘플 product 인스턴스를 정의 하였다. GetById, Add와 Remove 메소드는 직관적이어서 더이상 설명하진 않는다.<br />
이제 위의 Repository 구현을 이용하는  몇개의 UNIT 테스트를 만들어 보자 , 몇개의 특별한 메소드가 보이는데 (eg. ShoudEqual, ShoudNotBeNull, ShoudBeThrownBy etc) 이는 Assert 테스트를 쉽게하기 위해 만든 메소드로써 <br />
The NHibernate FAQ 블로그 포스트에서 정보를 얻을 수 있다. 
public class FakeRepositoryTester{    
private IProductRepository repository;     
[SetUp]    
public void SetupContext()    {        repository = new ProductRepositoryFake();    }   
[Test] 
public void can_load_a_product_by_its_id_from_the_repository()    
{ var product = repository.GetById(2);
product.ShouldNotBeNull();
product.Id.ShouldEqual(2); }
[Test]
public void can_add_a_new_product_to_the_repository()
{
repository.Add(new Product {Id = 99, Name = "Product 99", ReorderLevel = 13, Discontinued = false});
// let's try to load this new product var product = repository.GetById(99); product.Id.ShouldEqual(99); product.Name.ShouldEqual("Product 99"); }
[Test]
public void can_remove_an_existing_product_from_the_repository()
{ var product = repository.GetById(1);
repository.Remove(product);
typeof (KeyNotFoundException).ShouldBeThrownBy(() => repository.GetById(1)); }}
원본 글에는 session을 이용한 NHibernate UNIT OF WORK가 이어지나, 이 번역의 목적은 Repository 패턴의 이해에 목적이 있으므로, 여기에서 줄입니다.  남은 내용은 원본 포스트를 더 읽어 보시길 추천합니니다.<br /> Repository를 설명하는 또 다른 블로그 포스트 : <A title="[http://weblogs.asp.net/fredriknormen/archive/2008/04/24/what-purpose-does-the-repository-pattern-have.aspx]로 이동합니다." href="http://weblogs.asp.net/fredriknormen/archive/2008/04/24/what-purpose-does-the-repository-pattern-have.aspx" target=_blank><EM>What purpose does the repository pattern have?</EM></A><br /> A title="[http://morshedanwar.wordpress.com/2009/06/10/implementing-repository-pattern-with-entity-framework/]로 이동합니다." href="http://morshedanwar.wordpress.com/2009/06/10/implementing-repository-pattern-with-entity-framework/" target=_blank>Implementing Repository Pattern With Entity Framework</A> : EF를 이용한 Repository 구현하기의 예제 소스를 포스팅 하고 있습니다.<br /> UNIT OF WORK 에 대해서는 차후의 포스트에서 다루겠습니다
이올린에 북마크하기(0) 이올린에 추천하기(0)

현재글 : Repository Pattern posted By - 반더빌트 2009/02/10 10:59
Trackback 0 : Comment 0

Trackback Address :: http://smack.kr/trackback/300 관련글 쓰기


[생각을 적어 주세요~ 댓글은 작은 인연의 씨앗입니다.]