DTO를 도메인 개체에 매핑하는 모범 사례


81

DTO 를 도메인 개체 에 매핑하는 것과 관련된 많은 질문을 보았지만 그들이 내 질문에 답하지 못했다고 생각했습니다. 이전에 많은 방법을 사용해 왔고 내 의견이 있지만 좀 더 구체적인 것을 찾고 있습니다.

그 상황:

우리는 많은 도메인 객체를 가지고 있습니다. CSLA 모델을 사용하므로 도메인 개체가 매우 복잡 할 수 있고 자체 데이터 액세스가 포함됩니다. 당신은 이것들을 와이어로 전달하고 싶지 않습니다. 우리는 여러 형식 (.Net, JSON 등)으로 데이터를 반환하는 몇 가지 새로운 서비스를 작성할 것입니다. 이를 위해 (그리고 다른 이유들) 우리는 또한 유선상에서 전달할 린 데이터 전송 객체를 생성하고 있습니다.

내 질문은 : DTO와 도메인 개체를 어떻게 연결해야합니까?

첫 번째 반응은 Fowler, DTO 패턴 유형 솔루션 을 사용하는 입니다. 나는 이것을 여러 번 보았고 나에게 옳다고 느낀다. 도메인 개체에 DTO에 대한 참조가 없습니다. 도메인 개체에서 DTO를 만들기 위해 외부 엔터티 ( "매퍼"또는 "어셈블러")가 호출됩니다. 일반적으로 도메인 개체 측에는 ORM 이 있습니다. 이것의 단점은 "매퍼"가 실제 상황에 대해 매우 복잡 해지는 경향이 있고 매우 취약 할 수 있다는 것입니다.

또 다른 아이디어는 도메인 개체가 단순한 데이터 개체이기 때문에 DTO를 "포함"하는 것입니다. 도메인 개체 속성은 내부적으로 DTO 속성을 참조하고 요청이있을 경우 DTO를 반환 할 수 있습니다. 나는 이것에 아무런 문제가 없다는 것을 알 수는 있지만 잘못된 것 같습니다. NHibernate 를 사용 하는 사람들 이이 방법을 사용 하는 것으로 보이는 기사를 보았습니다 .

다른 방법이 있습니까? 위의 방법 중 하나를 사용할 가치가 있습니까? 그렇다면 그 이유는 무엇입니까?


4
automapper가 흥미로워 보입니다. 나는 그것이 대체되기 전에 많은 코드를 보았다. 내 주요 문제는 어떤 이유로 든 많은 양의 매핑 코드에 갇히게된다면 내가 직접 제어하는 ​​것을 선호한다는 것입니다.
Brian Ellis

2
DTO 에서 도메인 개체 로 이동할 때 해당 매핑은 100 % 수동입니다. 단순히 데이터 컨테이너가 아닌 도메인 개체를 운영 기반으로 유지하려고하므로 해결하기가 훨씬 더 어려운 문제입니다. 가는 DTO, 그 해결하기 쉬운 문제입니다.
Jimmy Bogard

또 다른 옵션은 마지막 프로젝트에서 시작한 ServiceToolkit.NET의 베타 버전입니다. 아마 당신을 도울 수 있습니다 http://servicetoolkit.codeplex.com/

도메인 개체가 dto 개체에 대한 지식이 없어야한다는 점에서 잘못된 점에 동의합니다. 이 경우 관련 될 수 있지만 그 목적은 완전히 분리되어 있으며 (dto는 일반적으로 목적을 위해 만들어 짐) 불필요한 종속성을 생성하게됩니다.
Sinaesthetic 2013

답변:


40

도메인과 DTO 사이에있는 매퍼를 사용하는 이점은 단일 매핑 만 지원할 때는 나타나지 않지만 매핑 수가 증가함에 따라 해당 코드를 도메인에서 격리하면 도메인을 더 단순하고 간결하게 유지하는 데 도움이됩니다. 많은 추가 가중치로 도메인을 복잡하게 만들지 않을 것입니다.

개인적으로 저는 제 도메인 엔터티에서 매핑을 유지하고 "관리자 / 서비스 계층"이라고 부르는 책임을 맡기려고합니다. 이것은 애플리케이션과 리포지토리 (들) 사이에 위치하며 워크 플로우 조정과 같은 비즈니스 로직을 제공하는 계층입니다 (A를 수정하면 서비스 A가 서비스 B와 작동하도록 B도 수정해야 할 수 있음).

가능한 엔딩 형식이 많으면 방문자 패턴을 사용하여 엔터티를 변환 할 수있는 플러그 형 포맷터를 만드는 방법을 살펴볼 수 있지만 아직이 복잡한 항목에 대한 필요성을 찾지 못했습니다.


"(A를 수정하면 서비스 A가 서비스 B와 작동하도록 B도 수정해야 할 수 있습니다.)"-이것은 비즈니스 로직에 해당되지 않습니까? 이 부분은 서비스가 아닌 컨트롤러로 이동해야한다고 생각합니다.
Ayyappa

24

Jimmy Bogard작성한 것과 같은 자동 매퍼를 사용할 수 있습니다.이 자동 매퍼 는 객체간에 연결이없고 준수되는 명명 규칙에 의존합니다.


9
Automapper는 우발적으로 노출 된 속성-> 보안 허점으로 이어질 수 있습니다. DTO로 노출되어야하는 것을 명시 적으로 말하는 것이 좋습니다.
deamon

4
@deamon : 유효한 우려 사항이지만 모든 끈적 끈적한 매핑 코드를 작성하여 생성 할 수있는 버그 (및 인간의 감독으로 인한 잠재적 보안 허점)도 마찬가지입니다. 나는 automagic road로 가서 내장 된 사용자 지정 매핑 기능을 사용하여 5 %를 처리 할 것입니다.
Merritt

@deamon-노출하면 안되는 속성에 대해 조건부 매핑을 수행 할 수 없습니까? AutoMapper가 이러한 시나리오를 처리한다고 생각하십니까?
Richard B

AutoMapper를 사용하는 경우 매핑이 올바르게 수행되었는지 테스트하기 위해 모든 단위 테스트를 갖추는 것이 매우 중요하다고 생각합니다.
L-Four

7

T4 템플릿을 사용하여 매핑 클래스를 만듭니다.

프로-런타임 매퍼보다 빠르게 컴파일 타임에 사람이 읽을 수있는 코드를 사용할 수 있습니다. 코드에 대한 100 % 제어 (부분 메서드 / 템플릿 패턴을 사용하여 임시로 기능을 확장 할 수 있음)

단점-특정 속성, 도메인 개체 컬렉션 등을 제외하고 T4 구문을 학습합니다.


6

엔티티 내부에 매핑 논리를 유지한다는 것은 도메인 개체가 이제 알 필요가없는 "구현 세부 정보"를 인식한다는 것을 의미합니다. 일반적으로 DTO는 들어오는 요청에서 또는 외부 서비스 / 데이터베이스에서 읽기를 통해 외부 세계로 연결되는 게이트웨이입니다. 엔터티는 비즈니스 로직의 일부이므로 이러한 세부 정보를 엔터티 외부에 유지하는 것이 가장 좋습니다.

다른 곳에 매핑을 유지하는 것이 유일한 대안이 될 수 있지만 어디로 가야합니까? 나는 매핑 객체 / 서비스를 도입하려고 시도했지만, 결국 말하고 수행 한 것은 과잉 엔지니어링처럼 보였습니다. 저는 소규모 프로젝트에서 Automapper 등을 사용하여 약간의 성공을 거두었지만 Automapper와 같은 도구에는 자체 함정이 있습니다. Automapper의 매핑은 암시 적이며 나머지 코드와 완전히 분리되어 있기 때문에 매핑과 관련된 문제를 찾기가 꽤 어려웠습니다 ( "관심의 분리"가 아니라 "갓 포세이큰 매핑이 살고있는 곳"과 비슷 함). 때로는 추적하기 어려울 수 있습니다. Automapper가 그 용도가 없다고 말하는 것은 아닙니다. 저는 매핑이 문제를 피하기 위해 가능한 한 명확하고 투명해야한다고 생각합니다.

매핑 서비스 계층을 생성하는 대신 DTO 내부에 매핑을 유지하는 데 많은 성공을 거두었습니다. DTO는 항상 응용 프로그램의 경계에 있기 때문에 비즈니스 개체를 인식하고 여기에서 매핑하는 방법을 파악할 수 있습니다. 매핑 수가 합리적인 양으로 확장 되더라도 깨끗하게 작동합니다. 모든 매핑이 한곳에 있으며 데이터 계층, 부패 방지 계층 또는 프레젠테이션 계층 내에서 여러 매핑 서비스를 관리 할 필요가 없습니다. 대신 매핑은 요청 / 응답과 관련된 DTO에 위임 된 구현 세부 정보 일뿐입니다. serializer는 일반적으로 속성과 필드를 유선을 통해 전송할 때만 직렬화하므로 문제가 발생하지 않아야합니다. 개인적으로 저는 이것이 가장 깨끗한 옵션임을 발견했고 제 경험상


3

도메인 개체를 매개 변수로 사용하는 DTO 클래스 내부에서 생성자를 구현하는 방법은 무엇입니까?

말 ... 이런거

class DTO {

     // attributes 

     public DTO (DomainObject domainObject) {
          this.prop = domainObject.getProp();
     }

     // methods
}

9
제발 이러지마 DTO 계층이 도메인 계층을 인식하거나 이에 종속되는 것을 원하지 않습니다. 매핑의 장점은 매핑을 변경하여 하위 레이어를 쉽게 전환 할 수 있거나 매핑을 변경하여 하위 레이어의 수정을 제어 할 수 있다는 것입니다. dtoA가 오늘 domainObjectA에 매핑되지만 내일은 domainObjectB에 매핑되어야한다고 가정 해 보겠습니다. 귀하의 경우에는 DTO 개체를 수정해야합니다. 매퍼의 많은 이점을 잃었습니다.
Frederik Prijck 2014

2
우선 감사합니다! :디. 그래서 @FrederikPrijck DTO과 사이에 레이어를 삽입하여 DomainObject기본적으로 DTO의이 문제는 도메인 객체에 따라 달라 지므로 모든 "빌딩 작업"은 mapper두 DTO에 종속 된 중간 레이어 (클래스)에서 수행됩니다. 및 DomainObjects. 이 문제에 대한 접근 방식이 가장 좋거나 일반적으로 권장됩니까? 요점을 이해했는지 확인하기 만하면됩니다.
Victor

4
네, 레이어는 "어셈블러"라고합니다. 3 번째 레이어를 사용하여 매핑을 정의하면 어셈블러 레이어를 다른 구현 (예 : Automapper 제거 및 수동 매핑 사용)으로 쉽게 대체 할 수 있습니다. 이는 항상 더 나은 선택입니다. 그것을 이해하는 가장 좋은 방법은 내가 당신에게 Object A를주고 다른 누군가가 당신에게 Object B를 줄 수있는 곳을 생각하는 것입니다. 당신은 각각의 개체 (dll 만)에 접근 할 수 없기 때문에 매핑은 3 번째 층. 그러나 개체에 액세스 할 수 있더라도 매핑은 관련이 없으므로 항상 외부에서 수행해야합니다.
Frederik Prijck 2014

1
하지만이 답변은 사실 "유용한 것 이상"이라는 의견과 수정 사항이 포함되어있어 독자에게 문제에 대한 인정과 팁을 제공합니다. 배우는 데 정말 기여합니다. 왜 donwvote를해야하는지 모르겠습니다. ..하지만 그것에 대해 토론을 시작하고 싶지 않습니다 .. 당신에게 달렸습니다. 어쨌든 답변 주셔서 감사합니다.
빅터

3
사실 저는이 접근 방식을 좋아합니다. 현재 생성자를 사용하여 엔티티를 DTO에 매핑하고 매퍼 클래스를 사용하여 입력 dto를 엔티티에 매핑합니다.
dream83619 jul.

1

또 다른 가능한 솔루션 : http://glue.codeplex.com .

풍모:

  • 양방향 매핑
  • 자동 매핑
  • 다른 유형 간의 매핑
  • 중첩 된 매핑 및 병합
  • 목록과 배열
  • 관계 확인
  • 매핑 테스트
  • 속성, 필드 및 방법


0

내가 만든 도구를 제안 할 수 있으며 CodePlex : EntitiesToDTOs 에서 호스팅되는 오픈 소스 입니다.

DTO에서 엔티티로 또는 그 반대로 매핑은 확장 메서드에 의해 구현되며, 이들은 각 끝의 어셈블러 측을 구성합니다.

다음과 같은 코드로 끝납니다.

Foo entity = new Foo();
FooDTO dto = entity.ToDTO();
entity = dto.ToEntity();

List<Foo> entityList = new List<Foo>();
List<FooDTO> dtoList = entityList.ToDTOs();
entityList = dtoList.ToEntities();

DTO와 도메인 엔터티가 서로를 인식하게하므로 구조적으로 잘못되었습니다.
Raffaeu 2012

5
@Raffaeu ToDTO / ToDTOs / ToEntity / ToEntities 메서드는 어셈블러를 나타내는 확장 메서드로 정의되어 있기 때문에 그렇게 생각하지 않습니다. 엔티티를 DTO로 또는 그 반대로 변환하는 논리는 실제로 엔티티 / DTO가 아닌 확장 메서드 (어셈블러)에 있습니다.
kzfabi

2
"어셈블러"에 대해 이야기하는 경우 올바른 방법으로 구현하십시오. 모듈화하고 쉽게 교체 할 수 있도록 만들고 종속성 주입을 사용하십시오. 도메인 모델 자체가 DTO 로의 변환을 인식 할 필요는 없습니다. 하나의 도메인 개체가 있지만 동일한 도메인을 사용하는 50 개의 서로 다른 응용 프로그램이 있으며 각각 고유 한 DTO가 있다고 가정 해 보겠습니다. 50 개의 확장을 만들지 않을 것입니다. 대신 서비스에 대한 종속성으로 삽입되는 필수 어셈블러를 사용하여 각 애플리케이션에 대해 하나의 애플리케이션 서비스를 생성합니다.
Frederik Prijck 2014

0

왜 이렇게 할 수 없습니까?

class UserDTO {
}

class AdminDTO {
}

class DomainObject {

 // attributes
 public DomainObject(DTO dto) {
      this.dto = dto;
 }     

 // methods
 public function isActive() {
      return (this.dto.getStatus() == 'ACTIVE')
 }

 public function isModeratorAdmin() {
      return (this.dto.getAdminRole() == 'moderator')
 }

}


userdto = new UserDTO();
userdto.setStatus('ACTIVE');

obj = new DomainObject(userdto)
if(obj.isActive()) {
   //print active
}

admindto = new AdminDTO();
admindto.setAdminRole('moderator');

obj = new DomainObject(admindto)
if(obj.isModeratorAdmin()) {
   //print some thing
}

@FrederikPrijck (또는) 누군가 : 제안 해주세요. 위의 예에서 DomainObject는 DTO에 의존합니다. 이렇게하면 dto <-> 도메인 개체를 매핑하는 코드를 피할 수 있습니다.

또는 DomainObject 클래스가 DTO 클래스를 확장 할 수 있습니까?


0

또 다른 옵션은 ModelProjector 를 사용하는 것 입니다. 가능한 모든 시나리오를 지원하며 최소한의 풋 프린트로 사용하기 매우 쉽습니다.


0

이를 위해 Factory, Memento 및 Builder 패턴을 사용할 수 있습니다. Factory는 DTO에서 도메인 모델의 인스턴스를 만드는 방법에 대한 세부 정보를 숨 깁니다. Memento는 DTO로 /에서 도메인 모델의 직렬화 / 역 직렬화를 처리하고 개인 구성원에 액세스 할 수도 있습니다. Builder는 유창한 인터페이스로 DTO에서 도메인으로의 매핑을 허용합니다.

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