엔터티 개체를 데이터 전송 개체로 사용하는 것이 좋습니까?


29

그렇다면 Entity Framework가 레이어간에 데이터를 전송하기 위해 동일한 속성을 가진 새 객체를 생성하는 로직을 제공하지 않는 이유가 궁금합니다.

엔티티 프레임 워크로 생성 한 엔티티 오브젝트를 사용합니다.


3
나는 이것이 좋은 질문이라고 생각하지만 읽기가 너무 어렵 기 때문에 실제로 말할 수는 없습니다. 나는 그것을 고치는 방법을 잘 모르겠습니다. 질문을 수정하십시오.
candied_orange

1
@CandiedOrange +1, 그리고 이것이 너무 많은 공짜를 얻었을 때 더 무서워집니다.
guillaume31

답변:


23

너하기에 달렸다.

대부분의 사람들은 그것이 좋은 습관은 아니지만 어떤 경우에는 그것을 벗어날 수 있다고 말할 것입니다.

EF는 여러 가지 이유로 DDD와 잘 어울리지 않았지만 두 가지가 있습니다. 엔터티에 매개 변수화 된 생성자를 가질 수 없으며 컬렉션을 캡슐화 할 수 없습니다. 도메인 모델에는 데이터와 동작이 모두 포함되어야하므로 DDD는이를 사용합니다.

어떤 식 으로든 EF는 빈혈 도메인 모델을 갖도록 강요하며이 경우 엔터티를 DTO로 사용할 수 있습니다. 탐색 속성을 사용하는 경우 일부 문제가 발생할 수 있지만 해당 엔티티를 직렬화하여 유선으로 전송할 수 있습니다. 실용적이지 않을 수 있습니다. 전송할 필요가없는 속성이있는 각 엔티티의 직렬화를 제어해야합니다. 더 쉬운 방법은 데이터 전송에 맞게 개별 클래스를 간단하게 디자인하는 것입니다. 이를 위해 AutoMapper 와 같은 라이브러리 가 생성됩니다.

예를 들어 Person, 다음과 같이 정의 된 클래스가 있다고 가정하십시오 .

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; get; }

    // plus a bunch of other properties relevant about a person
}

어딘가에 직원 목록을 표시하려는 경우 Id, FirstName및 을 전송하는 것이 실용적 일 수 있습니다 LastName. 그러나 다른 모든 관련성이없는 속성을 보내야합니다. 응답의 크기에 신경 쓰지 않는다면 큰 문제는 아니지만 일반적인 아이디어는 관련 데이터 만 보내는 것입니다. 반면에 개인 목록을 반환하는 API를 디자인하면이 경우 모든 속성을 보내야 할 수 있으므로 엔터티를 serialize하고 보내는 것이 좋습니다. 이 경우 DTO 클래스 작성은 논쟁의 여지가 있습니다. 엔터티와 DTO를 혼합하는 것을 좋아하는 사람들도 있고 그렇지 않은 사람들도 있습니다.

업데이트 된 질문에 대답하기 위해 EF는 ORM입니다. 데이터베이스 레코드를 객체에 매핑하거나 그 반대로 매핑하는 것이 그 역할입니다. EF를 통과하기 전후에 이러한 객체로 수행하는 작업은 그 문제의 일부가 아닙니다. 해서는 안됩니다.


1
간단히 요약합니다. DTO와 POCO의 차이점은 무엇입니까? 그들은 단지 데이터를 보유하지 않습니까?
Laiv

1
@ guillaume31 지속성을 고려하지 않고 진정한 도메인 모델을 가질 수는 없습니다. 집계를 유효한 상태로로드하기 위해 리플렉션 (다른 종류의 핵은 말할 것도없고 내 공용 속성이 다르게 개인 필드에 의해 지원되는 경우)을 사용해야하는 경우 퍼블릭 인터페이스는 무엇입니까? EF가 공용 인터페이스를 사용하여 집계를 숨길 수있는 인프라를 제공하지 않는 경우 비즈니스 로직을 다른 곳에 유지하고 데이터베이스에서로드하기 위해 추가 보일러 플레이트를 작성하는 대신 도메인 빈혈을 유지하는 것이 좋습니다.
devnull

1
그렇다면 공개 컬렉션 게터를 개인 필드와 같은 방식으로 지정하는 것보다 전체 도메인 모델을 빈틈없이 만들고 싶습니까? (자체 객체의 모든 필드를 취하는 생성자를 노출시키는 것이 가장 먼저 도메인에 반대하기 때문에 생성자 문제를 제쳐
두자

1
@Konrad here : As of EF Core 2.1, only services known by EF Core can be injected. Support for injecting application services is being considered for a future release.나는 엔터티에 응용 프로그램 서비스를 주입하는 것을 매우 좋아합니다.
devnull

1
@ Konrad 내 도메인 엔터티를 DTO로 사용하지 않을 것입니다 (이 방법으로 사용하는 경우). DTO는 EF의 서비스 주입 지원에 관계없이 유용합니다.
devnull

8

전혀 그렇지 않다.

이상적으로 DTO는 지속성 저장소 (일명 데이터베이스 테이블)와 일치합니다.

그러나 비즈니스 클래스가 반드시 일치하는 것은 아닙니다. 추가 클래스가 필요하거나 데이터베이스에있는 클래스와 분리되거나 결합 된 클래스가 필요할 수 있습니다. 응용 프로그램이 작 으면 실제로 이런 종류의 문제는 보이지 않지만 중대형 응용 프로그램에서는 자주 발생합니다.

또 다른 것은 DTO가 지속성을 다루는 모든 영역의 일부이며 비즈니스 계층은 그에 대해 아무것도 알지 못한다는 것입니다.


명령 개체는 비즈니스 계층에 대해 뭔가를 알고 있어야하는 DTO이다.
devnull 2016 년

비즈니스 계층이 인터페이스를 통해 명령 객체를 간접적으로 호출 할 것이라고 생각합니다. 그리고 기능이 전혀없는 데이터를 운반하는 간단한 개체에서와 같이 DTO를 의미합니다. 명령 객체가 DTO인지 확실하지 않습니다 ( martinfowler.com/eaaCatalog/dataTransferObject.html ).
Juan Carlos Eduardo Romaina Ac

1
일반적으로 명령은 실제 ​​명령과 처리기로 구분됩니다. 명령에는 데이터가 포함되고 핸들러에는 동작이 포함됩니다. 이와 같이 사용하면 명령 부분에는 클라이언트에서 수신 한 데이터 만 포함되며 클라이언트와 비즈니스 계층 간의 DTO 역할을합니다.
devnull 2016 년

DTO는 지속성에서 도메인으로 흐르지 않으며, 오는 데이터에서와 같이 외부에서 나올 수도 있습니다 (REST API 고려). devnull이 지적한 것처럼 명령 및 이벤트는 다소 DTO이며 비즈니스 계층으로 전송됩니다. 핸들러가 비즈니스 계층인지 여부는 디자인에 따라 다릅니다.
Laiv

4

실제로는 매우 나쁜 생각입니다. Martin Fowler는 로컬 DTO에 관한 기사를 가지고 있습니다.

간단히 말해, DTO패턴은 프로세스 외부로 데이터를 전송하는 데 사용되었습니다 (예 : 동일한 프로세스 내의 레이어 간이 아니라 와이어를 통해).


모든 프로그래밍 접근법과 마찬가지로, 우리가 구축하지 않은 모든 애플리케이션에서 서로 다른 패턴과 접근법에 대해 토론하고 논의하지 않는 한 가지 크기가 모두 적합하지는 않습니다. 항상 같은 것을 사용합니다. DTO는 본질적으로 단지 POPO 일 뿐이며 의도를 보여주기 위해 이름이 붙여졌습니다. DTO를 사용하여 서비스, 컨트롤러 및보기간에 "데이터를 전송"하는 것은 아무 것도 없습니다. 클래스의 이름이나 구조에는 데이터가 전송되는 위치와 데이터를 표시하거나 제한하는 내용이 없습니다.
James

4

아니요, 나쁜 습관입니다.

몇 가지 이유 :

  1. 새 엔티티 필드는 기본적으로 Dto에 있습니다. 엔터티 사용은 기본적으로 모든 정보를 사용할 수 있음을 의미합니다. 이로 인해 합리적인 정보가 노출되거나 API를 소비하는 사람에게는 사용되지 않는 많은 정보가 포함 된 API 계약이 팽창 될 수 있습니다. 물론 @JsonIgnoreJava 세계 와 같은 일부 주석을 사용하여 필드를 무시할 수는 있지만 다음 문제가 발생합니다 ...
  2. 엔터티에 대한 주석 / 조건 / 컨트롤이 많이 있습니다. 일부 속성 이름이 변경되면 (계약을 위반할 때) Dto에 전송하려는 항목을 제어해야하고 엔티티에없는 일부 속성, 속성 순서 등을 추가해야합니다. 곧 간단한 것이 표시됩니다. 주석이 많은 엔티티, 추가 필드 및 매번 무슨 일이 일어나고 있는지 이해하기가 더 어려울 것입니다.
  3. 유연성이 떨어 집니다. Dto를 사용하면 더 많은 클래스에서 정보를 자유롭게 나누고, 속성 이름을 변경하고, 새로운 속성을 추가하는 등의 작업을 자유롭게 수행 할 수 있습니다. Dto와 같은 엔터티에서는이 작업을 쉽게 수행 할 수 없습니다.
  4. 최적화되지 않았습니다 . 귀하의 실체는 항상 간단한 Dto보다 큽니다. 따라서 항상 더 많은 정보를 무시 / 직렬화하고 더 많은 불필요한 정보를 전송해야합니다.
  5. 게으른 문제 . 일부 프레임 워크 (예 : 최대 절전 모드)의 엔티티를 사용하여 이전에 데이터베이스 트랜잭션 내에 지연로드되지 않은 정보를 검색하려고하면 ORM 프록시가 트랜잭션에 첨부되지 않고 일종의 "게으른 예외"가 수신됩니다. get엔터티 의 메서드 를 호출하기 만하면됩니다 .

따라서 엔터티 필드를 Dto에 매핑하여이 작업을 도와주는 일종의 매퍼 도구를 더 쉽고 안전하게 사용할 수 있습니다.


1

@Dherik이 말한 것을 완성하기 위해 엔티티 객체를 데이터 전송 객체로 사용하는 주요 문제는 다음과 같습니다.

  1. 트랜잭션에서 DTO로 사용하기 때문에 엔터티에 대한 변경 사항을 커밋 할 위험이 있습니다 (트랜잭션에서 세션 엔터티를 분리 할 수있는 경우에도 대부분의 경우 이전에이 상태를 확인해야 함) 엔터티 -DTO에 대한 수정 사항 및 거래에 있지 않거나 수정 사항을 유지하지 않으려면 세션이 닫혔는지 확인하십시오).

  2. 클라이언트와 서버간에 공유하는 데이터의 크기 : 때로는 요청 응답의 크기를 최소화하기 위해 엔티티의 모든 내용을 클라이언트에 보내지 않으려는 경우가 있습니다. 엔터티에서 DTO를 분리하면 특정 사용 사례로 보내려는 데이터를 특수화 할 수 있습니다.

  3. 가시성 및 유지 관리 : 엔티티 필드에서 jpa / hibernate 주석을 관리하고 잭슨 주석을 유지하여 동일한 위치에서 json으로 직렬화해야합니다 (엔티티 구현과 분리하여 엔티티 인터페이스에서 분리 할 수 ​​있더라도) 실재). 그런 다음 새 필드를 추가 할 때 DTO 컨텐츠를 변경하는 경우 다른 사람이 해당 필드가 엔티티 필드이므로 데이터베이스와 관련된 테이블 필드라고 생각할 수 있습니다 ( @Transient모든 DTO 필드에 주석을 사용할 수있는 경우에도) 경우에 ..!).

내 의견으로는 엔터티를 읽을 때 소음이 발생하지만 내 의견은 분명히 주관적입니다.

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