programing

루트를 집계하기 위해 리포지토리 축소

i4 2023. 7. 15. 09:41
반응형

루트를 집계하기 위해 리포지토리 축소

저는 현재 데이터베이스의 거의 모든 테이블에 대한 리포지토리를 가지고 있으며, 이를 Aggregate 루트로만 줄임으로써 DDD와 더욱 연계하고 싶습니다.

가 다음 표를 있다고 User그리고.Phone각 사용자는 하나 이상의 전화기를 가질 수 있습니다.Aggregate 루트 개념이 없으면 다음과 같은 작업을 수행할 수 있습니다.

//assuming I have the userId in session for example and I want to update a phone number
List<Phone> phones = PhoneRepository.GetPhoneNumberByUserId(userId);
phones[0].Number = “911”;
PhoneRepository.Update(phones[0]);

골재 뿌리의 개념은 실제보다 종이 위에서 이해하기 쉽습니다.사용자에게 속하지 않는 전화 번호는 절대 없을 것입니다. 전화 저장소를 없애고 전화 관련 방법을 사용자 저장소에 통합하는 것이 타당할까요?예라고 가정하면, 저는 이전 코드 샘플을 다시 작성하려고 합니다.

사용자 저장소에서 전화 번호를 반환하는 메서드를 사용할 수 있습니까?또는 항상 사용자에게 참조를 반환한 다음 사용자를 통해 관계를 이동하여 전화 번호로 이동해야 합니다.

List<Phone> phones = UserRepository.GetPhoneNumbers(userId);
// Or
User user = UserRepository.GetUserWithPhoneNumbers(userId); //this method will join to Phone

어떤 방식으로 전화기를 구입하든 그 중 하나를 수정했다고 가정하면 어떻게 업데이트해야 합니까?저의 제한된 이해는 루트 아래의 객체가 루트를 통해 업데이트되어야 한다는 것이며, 이는 아래의 선택 #1로 저를 이끌 것입니다.Entity Framework에서 완벽하게 작동하지만, Entity Framework가 그래프 내의 변경된 개체를 탭하고 있음에도 불구하고 코드를 읽어보면 실제로 무엇을 업데이트하고 있는지 알 수 없기 때문에 설명할 수 없습니다.

UserRepository.Update(user);
// Or
UserRepository.UpdatePhone(phone);

어떤 개 가정해 . 를 들어, 마막으로같아연않결지룩가다여테정니합있개다고러이이블업은되지다무음도것제이실로과▁lastly▁that▁as▁assuming▁tables▁such다▁i▁lookup가니,▁several정합있,다여마▁have개고▁are,▁tied.CountryCodes,ColorsCodes,SomethingElseCodes드롭다운을 채우거나 다른 이유로 사용할 수 있습니다.이러한 저장소는 독립 실행형입니까?다음과 같은 일종의 논리적 그룹/리포지토리로 결합할 수 있습니까?CodesRepository아니면 모범 사례에 반하는 것입니까?

당신은 당신의 저장소에 당신이 원하는 모든 방법을 가질 수 있습니다 :) 당신이 언급한 두 경우 모두, 전화 목록이 채워진 상태로 사용자를 돌려보내는 것이 타당합니다.일반적으로 사용자 개체는 모든 하위 정보(예: 모든 주소, 전화 번호)로 완전히 채워지지 않으며 사용자 개체를 다른 종류의 정보로 채워지는 방법이 있을 수 있습니다.이를 게으른 로딩이라고 합니다.

User GetUserDetailsWithPhones()
{
    // Populate User along with Phones
}

이 경우 업데이트를 위해 전화 번호 자체가 아닌 사용자가 업데이트됩니다.스토리지 모델은 전화기를 다른 테이블에 저장할 수 있으며, 그렇게 하면 전화기만 업데이트된다고 생각할 수 있지만 DDD 관점에서는 그렇지 않습니다.가독성에 관한 한, 선은

UserRepository.Update(user)

단독으로는 업데이트되는 내용을 전달하지 않으며, 그 위에 있는 코드는 업데이트되는 내용을 명확하게 합니다.또한 업데이트되는 내용을 나타내는 프런트 엔드 메소드 호출의 일부일 가능성이 높습니다.

룩업 테이블의 경우 또는 실제로는 그렇지 않은 경우에도 Generic Repository를 사용하여 이를 사용하는 것이 유용합니다.사용자 지정 리포지토리는 일반 리포지토리에서 상속할 수 있습니다.

public class UserRepository : GenericRepository<User>
{
    IEnumerable<User> GetUserByCustomCriteria()
    {
    }

    User GetUserDetailsWithPhones()
    {
        // Populate User along with Phones
    }

    User GetUserDetailsWithAllSubInfo()
    {
        // Populate User along with all sub information e.g. phones, addresses etc.
    }
}

Generic Repository Entity Framework를 검색하면 많은 좋은 구현이 가능합니다.그것들 중 하나를 사용하거나 당신 자신의 것을 쓰세요.

Aggregate Root 저장소의 예는 문제가 없습니다. 즉, 다른 개체에 의존하지 않고 합리적으로 존재할 수 없는 개체는 자체 저장소를 가질 수 없습니다(전화의 경우).이러한 고려 없이도 DB 테이블에 대한 1-1 매핑에서 리포지토리가 폭발적으로 증가하는 상황을 신속하게 발견할 수 있습니다.

데이터 변경에 대해 저장소 자체보다는 작업 단위 패턴을 사용하는 것을 고려해야 합니다. DB에 대한 변경을 유지하는 것과 관련하여 의도와 관련하여 혼란을 야기하고 있다고 생각하기 때문입니다.EF 솔루션에서 작업 단위는 기본적으로 EF 컨텍스트 주변의 인터페이스 래퍼입니다.

조회 데이터 저장소와 관련하여 도메인 엔티티(국가, 색상 등)에 속하지 않는 데이터를 담당하는 참조 데이터 저장소를 생성하기만 하면 됩니다.

사용자가 없는 전화기가 의미가 없는 경우 해당 전화기는 엔티티(아이덴티티가 중요한 경우) 또는 가치 개체이므로 항상 사용자를 통해 수정하고 함께 검색/업데이트해야 합니다.

집계 루트를 컨텍스트 정의자로 생각합니다. 집계 루트는 로컬 컨텍스트를 그리지만 글로벌 컨텍스트(사용자의 응용프로그램) 자체에 있습니다.

도메인 기반 설계를 따르는 경우 리포지토리는 Aggregate 루트당 1:1이 됩니다.
변명의 여지가 없습니다.

여러분이 직면하고 있는 문제는 다음과 같습니다.

  • 기술적 문제 - 객체 관계 임피던스 불일치.전체 개체 그래프를 쉽게 유지하는 데 어려움을 겪고 있으며 엔티티 프레임워크가 도움이 되지 않습니다.
  • 도메인 모델은 (동작 중심이 아닌) 데이터 중심입니다.그 때문에 - 개체 계층 구조(앞에서 언급한 컨텍스트)에 대한 지식을 잃게 되고 마법처럼 모든 것이 집계 루트가 됩니다.

첫 번째 문제를 어떻게 해결해야 할지 잘 모르겠습니다만, 두 번째 문제를 해결하는 것이 첫 번째 문제를 해결하는 데 충분하다는 것을 알게 되었습니다.제가 말하는 행동 중심적인 의미를 이해하려면 이 논문을 한 번 써보세요.

추신: 저장소를 루트를 집계하도록 축소하는 것은 의미가 없습니다.
추신: 피하세요."CodeRepositories"이는 데이터 중심 -> 절차 코드로 이어집니다.
P.P.P.P.S 작업 패턴의 단위를 피합니다.집계 루트는 트랜잭션 경계를 정의해야 합니다.

이것은 오래된 질문이지만 간단한 해결책을 게시할 가치가 있습니다.

  1. EF Context는 이미 작업 단위(변경 트랙)와 저장소(DB의 항목에 대한 메모리 내 참조)를 모두 제공합니다.추가 추상화는 필수 사항이 아닙니다.
  2. 전화는 집계 루트가 아니므로 컨텍스트 클래스에서 DBSet를 제거합니다.
  3. 대신 사용자의 'Phones' 탐색 속성을 사용합니다.

static void updateNumber(int userId, string oldNumber, string newNumber)

static void updateNumber(int userId, string oldNumber, string newNumber)
    {
        using (MyContext uow = new MyContext()) // Unit of Work
        {
            DbSet<User> repo = uow.Users; // Repository
            User user = repo.Find(userId); 
            Phone oldPhone = user.Phones.Where(x => x.Number.Trim() == oldNumber).SingleOrDefault();
            oldPhone.Number = newNumber;
            uow.SaveChanges();
        }

    }

전화 엔티티가 root 사용자 집계와 함께만 의미가 있는 경우, 새 전화 레코드를 추가하는 작업은 특정 방법(DDD 동작)을 통해 사용자 도메인 개체의 책임이며, 몇 가지 이유로 완벽하게 의미가 있을 수 있습니다.바로 그 이유는 사용자 개체가 존재하는지 확인해야 하기 때문입니다. 전화 엔티티는 존재 여부에 따라 달라지기 때문입니다. 그리고 다른 프로세스가 작업을 완료하기 전에 루트 집계를 삭제하지 않았는지 확인하기 위해 더 많은 유효성 검사를 수행하는 동안 트랜잭션 잠금을 유지해야 합니다.다른 종류의 루트 Aggregate를 사용하는 경우에는 나중에 다른 작업에서 보다 효율적으로 처리할 수 있도록 루트 Aggregate의 열 속성에 일부 값을 집계하거나 계산하여 유지할 수 있습니다.참고로 사용자 도메인 개체에 Phone을 추가하는 방법이 있을 것을 제안하지만, 데이터베이스나 EF의 존재를 알아야 한다는 의미는 아닙니다.EM 및 Hibernate의 큰 기능 중 하나는 엔티티 클래스의 변경 사항을 투명하게 추적할 수 있다는 것이며, 탐색 컬렉션 속성에 따라 새 관련 엔티티를 추가하는 것을 의미합니다.

또한 소유한 사용자와 관계없이 모든 전화를 검색하는 방법을 사용하려면 사용자 저장소를 통해 한 가지 방법만 사용하면 모든 사용자를 IQueryable로 반환할 수 있지만 모든 사용자 전화를 가져오도록 매핑하고 이를 사용하여 세분화된 쿼리를 수행할 수 있습니다.따라서 이 경우에는 전화 저장소도 필요하지 않습니다.또한 메서드 뒤에 있는 쿼리를 추상화하려면 리포지토리 클래스뿐만 아니라 어디서나 사용할 수 있는 IQueryable에 대한 확장 메서드가 있는 클래스를 사용하는 것이 좋습니다.

전화 저장소가 아닌 도메인 개체만 사용하여 전화 엔티티를 삭제할 수 있는 경우 주의할 점은 UserId가 전화 기본 키의 일부인지, 즉 전화 레코드의 기본 키가 전화기의 UserId와 일부 다른 속성(자동 생성된 ID)으로 구성된 복합 키인지 확인해야 합니다.전화 레코드가 사용자 레코드에 의해 "소유"되고 사용자 탐색 컬렉션에서 제거되는 것은 데이터베이스에서 완전히 제거되는 것과 마찬가지이기 때문에 직관적으로 의미가 있습니다.

언급URL : https://stackoverflow.com/questions/5158064/reducing-repositories-to-aggregate-roots

반응형