회원 : 고유 ID와 도메인 개체 사용


10

메소드 / 함수 매개 변수로 도메인 객체 또는 고유 ID를 메소드 매개 변수로 사용 해야하는지 여부에 대한 몇 가지 유용한 답변을 얻은 후 식별자 대 도메인 객체를 메소드 매개 변수로 사용 하면 비슷한 질문이 있습니다. 이 커버). 고유 ID를 멤버로 사용하고 개체를 멤버로 사용하는 장단점은 무엇입니까? Scala / C # / Java와 같은 강력한 형식의 언어를 참조하고 싶습니다. 내가해야합니까 (1)

User( id: Int, CurrentlyReadingBooksId: List[Int])
Book( id: Int, LoanedToId: Int )

또는 (2), (1)보다 우선 : 모든 것에 대한 유형을 정의해야합니까?

User( id: UserId, CurrentlyReadingBooksId: List[ BookId] )
Book( id: BookId, LoanedToId: UserId )

또는 (3)

User( id: Int, CurrentlyReadingBooks: List[Book]) 
Book( id: Int, LoanedTo: User)

객체 (3)의 이점을 생각할 수는 없지만 ID (2) 및 (1)의 이점 중 하나는 DB에서 사용자 객체를 만들 때 Book 객체를 만들 필요가 없다는 것입니다. 결국 사용자 개체 자체에 의존하여 끝없는 체인을 만들 수 있습니다. RDBMS와 No-SQL 둘 다에 대해이 문제점에 대한 일반적인 솔루션이 있습니까 (다른 경우)?

지금까지 몇 가지 답변을 바탕으로 내 질문을 다시 설명합니다. 2) 항상 개체를 사용합니까? 3) 직렬화 및 역 직렬화에 재귀 위험이있을 때 ID를 사용하지만 그렇지 않으면 객체를 사용합니까? 4) 다른 것이 있습니까?

편집 : 당신은 항상 또는 일부 경우에 객체를 사용해야한다고 대답하면 다른 응답자가 게시 한 가장 큰 관심사에 답변해야합니다 => DB에서 데이터를 얻는 방법


1
좋은 질문에 감사드립니다. 관심을 가지고 이것을 따르기를 기대하십시오. 귀하의 사용자 이름이 "user18151"이라는 수치는 약간 부끄러운 일입니다. 이런 종류의 사용자 이름을 가진 사람들은 일부에 의해 무시됩니다.)
bjfletcher

@bjfletcher 감사합니다. 나는 그 잔소리 인식을 스스로 가지고 있었지만 왜 나에게 그런 일이 발생하지 않았습니다!
0fnt

답변:


7

ID로서의 도메인 객체는 몇 가지 복잡하고 미묘한 문제를 만듭니다

직렬화 / 직렬화

객체를 키로 저장하면 객체 그래프를 직렬화하는 것이 매우 복잡합니다. stackoverflow재귀로 인해 JSON 또는 XML로 순진 직렬화를 수행 하면 오류가 발생합니다. 그런 다음 실제 인스턴스를 객체 인스턴스를 직렬화하고 재귀를 생성하는 대신 ID를 사용하도록 변환하는 사용자 정의 직렬 변환기를 작성해야합니다.

유형 안전을 위해 객체를 전달하고 ID 만 저장하면 관련 엔터티가 호출 될 때 지연로드하는 접근 자 메서드를 가질 수 있습니다. 2 단계 캐싱은 후속 호출을 처리합니다.

미묘한 참조 누출 :

생성자에서 도메인 객체를 사용하는 경우 순환 참조를 작성하면 실제로 사용되지 않는 객체에 대해 메모리를 회수하기가 매우 어려워집니다.

이상적인 상황 :

불투명 ID vs int / long :

id이 식별 무엇인지에 대한 정보를 전달하지 않는 완전히 불투명 식별자해야한다. 그러나 시스템에서 유효한 식별자인지 확인해야합니다.

원시 유형은 이것을 깨뜨립니다.

int, longStringRDBMS 시스템의 식별자에 대한 가장 일반적으로 사용되는 원료 유형입니다. 수십 년 전으로 거슬러 올라간 실질적인 이유의 오랜 역사가 있으며, 모두 저축 space이나 저축 time또는 둘 다에 적합한 타협입니다 .

순차적 ID는 최악의 범죄자입니다.

순차 ID를 사용하면 기본적으로 시간적 의미 정보를 ID로 압축합니다. 그것이 사용될 때까지 나쁘지 않습니다 . 사람들이 ID의 의미 품질을 정렬하거나 필터링하는 비즈니스 로직을 작성하기 시작하면 미래의 관리자에게 고통의 세계를 설정합니다.

String 순진한 설계자들은 정보를 내용, 특히 시간적 의미론으로 포장 할 것이기 때문에 필드는 문제가된다.

이러한 있기 때문에,뿐만 아니라 분산 데이터 시스템을 구축하는 것은 불가능 할 12437379123것입니다 없는 독특한 세계가. 분산 시스템의 다른 노드가 시스템에서 충분한 데이터를 얻을 때 같은 수의 레코드를 작성할 가능성은 거의 보장됩니다.

그런 다음 해킹이 해결되기 시작하고 전체가 엉망진창으로 쌓입니다.

거대한 분산 시스템 ( 클러스터 )을 무시하면 다른 시스템과 데이터를 공유하려고 할 때 완전한 악몽이됩니다. 특히 다른 시스템을 제어 할 수없는 경우.

ID를 전 세계적으로 고유하게 만드는 방법과 똑같은 문제가 발생합니다.

UUID는 다음과 같은 이유로 작성 및 표준화되었습니다.

UUIDVersion사용 하는 것에 따라 위에 나열된 모든 문제가 발생할 수 있습니다.

Version 1MAC 주소와 시간을 사용하여 고유 한 ID를 만듭니다. 위치와 시간에 대한 의미 정보를 전달하기 때문에 나쁘다. 순진한 개발자가 비즈니스 논리를 위해 해당 정보에 의존하기 시작하는 것은 그 자체가 문제가 아닙니다. 또한 침입 시도에 악용 될 수있는 정보가 유출됩니다.

Version 2사용하는 사용자 UID또는 GIDdomian 및 UID또는 GUI에서 시간 대신에 Version 1이 나쁜대로입니다 Version 1데이터 유출에 대한이 정보를 위험이 비즈니스 로직에 사용되는.

Version 3비슷하지만 MAC 주소와 시간을 의미 적으로 의미가있는 어떤 것의 MD5일부 해시로 대체합니다 byte[]. 걱정할 데이터 누출이 없으므로 byte[]에서 복구 할 수 없습니다 UUID. 이를 통해 UUID인스턴스 형식과 외부 를 결정적으로 만드는 좋은 방법을 제공합니다 .

Version 4 좋은 솔루션 인 난수만을 기반으로하며 의미 정보를 전혀 가지고 있지 않지만 결정적으로 다시 만들 수는 없습니다.

Version 5그냥 Version 4이지만 sha1대신 사용 합니다 md5.

도메인 키 및 트랜잭션 데이터 키

도메인 개체 ID에 대한 선호는 기술적 인 이유로 사용 Version 5또는 사용 Version 3이 제한된 경우 Version 5입니다.

Version 3 많은 컴퓨터에 분산 될 수있는 트랜잭션 데이터에 적합합니다.

공간에 제약을받지 않는 한 UUID를 사용하십시오.

그것들은 고유 한 것으로 보장되어 한 데이터베이스에서 데이터를 덤프하고 다른 데이터베이스로 다시로드하므로 실제로 다른 도메인 데이터를 참조하는 중복 ID에 대해 걱정할 필요가 없었습니다.

Version 3,4,5 완전히 불투명하고 그것이 있어야하는 방식입니다.

a를 사용하여 단일 키를 기본 키로 UUID가질 수 있으며 자연 복합 기본 키에 대한 복합 고유 인덱스를 가질 수 있습니다.

스토리지 않습니다 되지 해야 CHAR(36)하나. UUID색인을 생성 할 수있는 한 주어진 데이터베이스의 고유 바이트 / 비트 / 번호 필드에을 저장할 수 있습니다 .

유산

원시 유형이 있고이를 변경할 수없는 경우에도 코드에서 추상화 할 수 있습니다.

사용 Version 3/5UUID당신은에 전달할 수 Class.getName()+ String.valueOf(int)A와 byte[]하고 다시 작성할과 결정적 불투명 참조 키가 있습니다.


내 질문에 명확하지 않은 경우 매우 죄송합니다.이 위대한 생각과 답변에 시간이 많이 걸리기 때문에 더 나쁘거나 실제로 좋습니다. 불행히도 그것은 내 질문에 맞지 않습니다. 아마도 자체 질문이 필요합니까? "도메인 객체의 id 필드를 만들 때 무엇을 명심해야합니까?"
0fnt

나는 명확한 설명을 추가했다.

알았어 답변에 시간을 보내 주셔서 감사합니다.
0fnt

1
Btw, AFAIK 세대 가비지 수집기 (요즘 지배적 GC 시스템이라고 생각합니다)는 GC의 순환 참조에 너무 많은 어려움을 겪지 않아야합니다.
0fnt

1
경우 C-> A -> B -> AB에 넣고 Collection다음 A과 모든 아이들이 아직 도달 할 수 있으며, 이러한 것들을 완전히 명확하지 않은 미묘한으로 이어질 수 누수 . GC그래프의 직렬화와 역 직렬화는 최소한의 문제이며, 복잡성의 악몽입니다.

2

예, 어느 쪽이든 이점이 있으며 타협도 있습니다.

List<int>:

  • 메모리 저장
  • 더 빠른 유형의 초기화 User
  • 데이터가 관계형 데이터베이스 (SQL)에서 오는 경우 액세스 사용자를 얻을 수있는 두 테이블, 바로이없는 Users테이블을

List<Book>:

  • 책에 액세스하는 것이 사용자에게 더 빠르며, 책은 메모리에 사전로드되어 있습니다. 더 빠른 후속 작업을 위해 시동 시간을 늘릴 수 있다면 좋습니다.
  • 데이터가 HBase 또는 Cassandra와 같은 문서 저장소 데이터베이스에서 가져온 경우 읽은 책의 값이 사용자 레코드에있을 가능성이 높으므로 "사용자가있는 동안"책을 쉽게 얻을 수있었습니다.

내가 갈 메모리 나 CPU 문제가 없다면 인스턴스 List<Book>를 사용하는 코드 User가 더 깨끗할 것이다.

타협:

Linq2SQL을 사용할 때 사용자 엔티티에 대해 생성 된 코드는 EntitySet<Book>액세스 할 때 지연로드됩니다. 이렇게하면 코드가 깨끗하고 사용자 인스턴스가 작게 유지됩니다 (메모리 공간이 현명합니다).


어떤 종류의 캐싱을 가정하면 사전로드 이점은 널이됩니다. 나는 Cassandra / HBase를 사용하지 않았으므로 그들에 대해 말할 수는 없지만 Linq2SQL은 매우 구체적인 경우입니다 (그러나이 특정한 경우와 일반적인 경우에도 게으른로드가 무한 체인 링 사례를 어떻게 막을 수 있는지는 알 수 없지만)
0fnt

Linq2SQL 예제에서는 성능상의 이점이없고 코드가 더 깨끗합니다. Cassandra / HBase와 같은 문서 저장소에서 일대 다 엔티티를 가져올 때 처리 시간의 대부분이 레코드를 찾는 데 소비되므로 많은 엔티티를 얻을 수 있습니다. 이 예).
ytoledano 2016 년

확실해? 책과 사용자를 별도로 정규화 한 경우에도 나에게 그것은 네트워크 대기 시간 추가 비용이되어야하는 것처럼 보입니다. 어쨌든 RDBMS 사례를 일반적으로 어떻게 처리합니까? (나는 그것을 명확하게 언급하기 위해 질문을 편집했습니다)
0fnt

1

짧고 간단한 경험 법칙 :

ID는 DTO 에서 사용됩니다 .
객체 참조는 일반적으로 도메인 로직 / 비즈니스 로직 및 UI 계층 객체에서 사용됩니다.

그것은 더 큰 규모의 엔터프라이즈 급 프로젝트에서 일반적인 아키텍처입니다. 이 두 종류의 객체로 변환하고 이리저리 매핑하는 매퍼가 있습니다.


들러서 답변 해 주셔서 감사합니다. 불행히도, 나는 위키 링크 덕분에 구별을 이해하지만 실제로는 본 적이 없습니다 (큰 장기 프로젝트를 한 적이 없었습니다). 동일한 객체가 두 가지 다른 목적으로 두 가지 방식으로 표현 된 예가 있습니까?
0fnt

다음은 매핑에 관한 실제 질문입니다. stackoverflow.com/questions/9770041/dto-to-entity-mapping-tool-다음 과 같은 중요한 기사가 있습니다. rogeralsing.com/2013/12/01/…
herzmeister

정말 도움이되었습니다. 감사합니다. 불행히도 순환 참조로 데이터를로드하는 방법을 이해하지 못합니까? 예를 들어, 사용자가 책을 참조하고 책이 동일한 사용자를 참조하는 경우이 객체를 어떻게 작성 하시겠습니까?
0fnt

리포지토리 패턴을 살펴보십시오 . 당신은해야 BookRepository하고를 UserRepository. 항상 전화를 걸 myRepository.GetById(...)거나 비슷하게하면 저장소는 객체를 만들어 데이터 저장소에서 값을로드하거나 캐시에서 가져옵니다. 또한 자식 개체는 대부분 게으르게로드되어 구성시 직접 순환 참조를 처리하지 않아도됩니다.
herzmeister 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.