리포지토리로 또는 리포지토리로


10

Domain Driven Design에 대해 처음 알게되었을 때 데이터베이스에 대해 원시인과 같은 SQL 쿼리를 던진 멋진 아이들을위한 최고의 노하우였던 저장소 및 작업 단위 패턴에 대해서도 소개했습니다. 이 주제에 대해 더 깊이 알게되면 작업 단위와 저장소를 세션 또는 컨텍스트라는 하나의 API로 구현하는 EFNHibernate 와 같은 ORM 때문에 더 이상 필요하지 않다는 것을 알게 되었습니다.

이제 어떻게해야할지 모르겠습니다. 저장소에 또는 저장소에 없습니다. 데이터 유출을 단순화 할 수있는 아무것도 추가하지 않으면 서 유출 된 추상화가 지나치게 복잡해한다는 주장을 실제로 이해하지만 내 응용 프로그램의 모든 가능한 측면을 Entity Framework 와 결합하는 것은 옳지 않다고 생각 합니다. 일반적으로 몇 가지 간단한 지침을 따릅니다.

  1. 도메인 계층은 엔티티, 서비스, 리포지토리를 포함하는 시스템의 핵심입니다.
  2. 인프라 계층은 인프라 문제 (예 : 파일, 데이터베이스, 프로토콜)의 도메인 인터페이스 구현을 제공합니다.
  3. 애플리케이션 계층은 사물을 연결하고 모든 것을 오케스트레이션하는 컴포지션 루트를 호스팅합니다.

내 솔루션은 일반적으로 다음과 같습니다.

Domain.Module1
Domain.Module2
    IModule2Repo
    IModule2Service
    Module2
Infrastructure.Persistence
    Repositories
        EntityFrameworkRepositoryBase
MyApp
    Boostrapper
        -> inject EntityFrameworkRepositoryBase into IRepository etc.

나는 IRepository<'T>데이터 액세스 방법을 알려주는 다른 것에 의존하지 않는 도메인 문제 인 도메인 레이어를 사용하여 도메인 계층을 깨끗하게 유지 합니다. 이제 IModule2Service데이터 액세스가 필요한 구체적인 구현을 수행 할 때 DbContext이를 인프라 계층에 직접 연결 하여 주입해야합니다 . ( Visual Studio 프로젝트로 오면 순환 종속성으로 인해 까다로울 수 있습니다! )

또한 보관소작업장에 대한 대안이 될 수있는 것은 무엇입니까 ? CQRS? 순수한 인프라 프레임 워크를 어떻게 추상화합니까?


1
또한 DDD에 설명 된 것처럼 '리포지토리'에 대해 이야기하는 경우 EF와 nHibernate는 리포지토리가 아닙니다. 물론 이들은 데이터 액세스 메커니즘을 부분적으로 추상화하지만 리포지토리에는 그보다 훨씬 많은 것이 있습니다.
Eric King

답변:


4

엔지니어링은 타협에 관한 것입니다. 소프트웨어 개발도 마찬가지입니다. 지금 당장은 더 간단한 다른 옵션 만 ORM과 직접 작업하는 것입니다. 그러나 당신이 말했듯이, 그것은 특정 지속성 프레임 워크에 잠길 수 있습니다.

따라서 "지속성에서 코드를 분리 할 가치가있는 추가 복잡성"이라고 스스로에게 자문해야합니다. 사람들이 코드를 지속성에서 분리하고 싶다고 말할 때마다 "커리어에서 지속성 프레임 워크를 몇 번 변경 했습니까?"라고 묻습니다.

리포지토리에서 볼 수있는 문제는 일반적인 변경이 더 어렵다는 것입니다. 예 : 집계를 쿼리하는 새로운 방법 추가 그리고 드문 변경을 쉽게 만들 수 있습니다. 예 : 리포지토리의 구현 변경

그런 다음 단위 테스팅 논쟁도 있습니다. "퍼시스턴스 프레임 워크가 데이터베이스를 메모리 나 로컬에서 모의 ​​할 수 없다면 프레임 워크를 전혀 사용할 가치가 없습니다."라고 말합니다.


1
리포지토리의 핵심은 앱을 지속성에서 분리하는 것이므로 복잡성이 없습니다. 그리고 메모리 저장소에서 사용하는 시점까지 db 액세스가 마지막으로 코딩되기 때문에 지속성 세부 정보 를 한 번 이상 변경합니다 . 또한 지속성 세부 정보를 직접 사용하는 경우 매우 미묘한 트랩이 있습니다. 비즈니스 객체는 불가지론하고 비즈니스 객체와 호환되도록 설계되는 경향이 있습니다. 풍부하고 캡슐화 된 엔터티는 (어떤 추악한) 해결 방법없이 메모리 (메모리 제외)에서 직접 복원 할 수 없기 때문에
MikeSW

집계 루트 또는 엔티티 를 쿼리하는 새로운 방법을 추가하는 것이 CQRS가 등장한 이유입니다. 개인적으로 도메인 용도로 리포지토리를 유지하고 실제 쿼리에는 쿼리 처리기를 사용합니다. 그리고 이러한 처리기는 db와 밀접하게 연결되어 있으며 수행하는 데 매우 효율적입니다.
MikeSW

3

일반 리포지토리 패턴에서 멀어졌지만 프로 리포지토리입니다. 대신 리포지토리를 제공하는 비즈니스 기능에 맞 춥니 다. 저장소는 ORM을 추상화하는 것을 목표로하지 않습니다. 이것은 내가 바꿀 것으로 예상되지 않으며 동시에 저장소를 너무 세분화하지 않습니다. (즉 CRUD) 대신 내 리포지토리는 2-3 가지 주요 목적을 제공합니다.

  • 데이터 검색
  • 데이터 생성
  • 하드 삭제

데이터 검색의 경우 리포지토리는 항상을 반환합니다 IQueryable<TEntity>. 데이터 작성의 경우 TEntity를 리턴합니다. 저장소는 일시 삭제 패턴을 사용하는 시스템의 권한 부여 "활성"상태 및 히스토리 데이터를 사용하는 시스템의 "현재"상태와 같은 기본 레벨 필터링을 처리합니다. 데이터 생성은 필요한 참조가 해결되고 연관되어 있고 엔터티가 설정되어 준비가되었는지 확인하는 역할을합니다.

데이터 업데이트는 해당 엔터티와 작업하는 비즈니스 로직의 책임입니다. 여기에는 유효성 검사 규칙과 같은 것들이 포함될 수 있습니다. 나는 그것을 저장소 방법으로 캡슐화하려고하지 않습니다.

대부분의 시스템에서 삭제가 일시 삭제되므로 데이터가 업데이트됩니다. (IsActive = false) 하드 삭제의 경우 리포지토리에서 하나의 라이너가됩니다.

왜 리포지토리입니까? 테스트 가능성. 물론 DbContext를 조롱 할 수는 있지만 반환하는 클래스를 조롱하는 것이 더 간단합니다.IQueryable<TEntity>. 그들은 또한 UoW 패턴과 잘 어울립니다. 개인적으로 Mehdime의 DbContextScope 패턴을 사용하여 원하는 수준의 작업 단위 (MVC의 컨트롤러)를 범위 지정하고 컨트롤러와 도우미 서비스 클래스가 참조를 전달하지 않고도 리포지토리를 활용할 수 있습니다 UoW / dbContext 주변. IQueryable을 사용하면 리포지토리에 많은 래퍼 메서드가 필요하지 않으며 코드에서 데이터 소비 방식을 최적화 할 수 있습니다. 예를 들어 리포지토리는 "Exists"또는 "Count"와 같은 메서드를 노출하거나 하위 집합의 데이터를 원하는 경우 다른 POCO로 엔터티를 래핑 할 필요가 없습니다. 그들은 당신이 필요하거나 필요하지 않은 관련 데이터에 대한 열망하는 로딩 옵션을 처리 할 필요조차 없습니다. IQueryable을 전달하면 호출 코드는 다음을 수행 할 수 있습니다.

.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()

매우 유연하고 테스트 PoV를 통해 조롱 한 저장소는 채워진 엔터티 개체 목록을 반환하면됩니다.

일반 리포지토리의 경우 테이블 당 리포지토리로 끝날 때 컨트롤러 / 서비스가 하나의 비즈니스 작업을 수행하기 위해 여러 리포지토리에 대한 참조로 끝나기 때문에 대부분이 이들을 멀리했습니다. 대부분의 경우 이러한 리포지토리 중 하나 또는 두 개만 실제로 쓰기 작업을 수행하고 (탐색 속성을 올바르게 사용하는 경우) 나머지는 읽기 기능을 지원합니다. 차라리 5 ~ 6 개의 다른 리포지토리를 치는 것보다 주문을 읽고 생성 할 수 있고 주문을 만들 때 관련 조회 (가벼운 고객 객체, 제품 등)를 읽을 수있는 OrdersRepository와 같은 것이 있습니다. DNRY 순수 주의자를 위반할 수도 있지만, 이에 대한 나의 주장은 저장소의 목적은 관련 참조를 포함하는 주문 작성을 제공하는 것입니다.Repository<Product>주문의 기초를 위해 제품을 얻기 위해서는 소수의 필드가있는 엔티티 만 필요합니다. 내 OrderRepository 에는 응용 프로그램 요구의 다른 영역 및 / 또는 복잡한 통과 필터링 표현식을 시도하고 제공하기 위해 여러 가지 "Get"메소드 가있는 .GetProducts()메소드 IQueryable<ProductSummary>보다 더 좋은 메소드가 반환 될 수 있습니다 Repository<Product>.

따르고 테스트하고 조정하기 쉬운 더 간단한 코드를 선택합니다. 악의적으로 악용 될 수 있지만, 악용 될 수없는 방식으로 코드를 "잠그고"시도하는 것보다 남용을 발견하고 수정하기 쉬운 것이 있습니다. 실제로 클라이언트가 지불하는 것을 실제로 끝내기위한 악몽. :)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.