2017-11-10 7 views
2

제 질문은 DDD 집계에 도메인 또는 InfrastructureServices를 주입하기 위해 실제로 수행하는 작업에 관한 것입니다. 난 일반 DomainObjects에서 DependencyInjection을 허용하는 것은 나쁜 생각인데, 내 경험으로 볼 때 뾰루지 개발자는 그걸로 불쾌한 일을하기를 권합니다. 그러나 Domainobjects의 DependencyInjection이 이해할 수있는 예외적 인 경우가 특히 있습니다. 특히 가독성과 단순성에 도움이되는 경우에는 특히 그렇습니다. 제 경우에는 aggregateroot 중 하나 인 새로운 Ids을 만드는 방법에 대한 문제를 해결하려고합니다.DDD 집계 및 엔티티의 InversionOfControl (Dependency Injection)

목록이 Address 인 집계를 합계 루트로 갖는 UserAddresses 집계가 있다고 가정합니다. 다음과 같이 단순화

public AddressId changeAddress(AddressId addressId, AddressChangeDto dto) { 
    Address address = nullableAddress(addressId); 

    if (address == null) { 
     // Doesn't matter for this question 
    } else if (address.hasChanged(dto)) { 
     address = changeAddress(address, dto);    
    } 
} 

및 개인 changeAddress(Address address, AddressChangeDto dto) 방법 :

private Address changeAddress(Address address, AddressChangeDto dto) { 
    if (!addressCopyNeeded(address)) { 
     address.change(dto); 
     return address; 
    } 

    // Address is both Shipping- & BillingAddress, hence it must be copied to 2 separate 
    // entities. 
    Address copiedAddress = address.copy(new AddressId()); // <-- New AddressId created 
    ... 
} 

지금 나는 AddressIdFactoryDomainService에서 생성되는 AddressId 생성을 리팩토링 할의 더 우리가 집합 적 방법 changeAddress(AddressId addressId, AddressChangeDto dto)을 가정 해 봅시다 기반 도메인에있는 MongoDbRepository를 기반으로하는 내 DomainModel에 머물면서 MongoDb에서 성능이 최적화 된 ObjectId을 만들었습니다. 긴 UUID보다 선호합니다. 그것은 분명히에 그 방법을 고객에게 혼란을하기 때문에 정말, 전혀 그 디자인 좋아하지 않는다, 그러나

public AddressId changeAddress(AddressId addressId, AddressChangeDto dto, AddressIdFactory idFactory) { 
    Address address = nullableAddress(addressId); 

    if (address == null) { 
     // Doesn't matter for this question 
    } else if (address.hasChanged(dto)) { 
     address = changeAddress(address, dto, idFactory);    
    } 
} 

private Address changeAddress(Address address, AddressChangeDto dto, AddressIdFactory idFactory) { 
    if (!addressCopyNeeded(address)) { 
     address.change(dto); 
     return address; 
    } 

    // Address is both Shipping- & BillingAddress, hence it must be copied to 2 separate 
    // entities. 
    Address copiedAddress = address.copy(idFactory.nextAddressId()); 
    ... 
} 

:

하나는 형식 매개 변수 같은으로 그 의존성을 추가하여 그것을 쉽게 해결할 수 첫 번째 광경 : "기존 주소를 변경하려면 왜 AddressIdFactory을 전달해야합니까?" 클라이언트는 없습니다. 은 내부적으로 진행되고있는 작업과 특정 별자리에 새 주소를 만들어야 할 가능성이 있음을 알고 할 필요가 없습니다. 이것은 또한 다음 단락으로 이어질 수 있습니다. 우리는 또한 여러분이 말할 수있는 전체 메소드를 리팩터링 할 수 있지만, 이는 클라이언트가 새로운 AddressId를 전달하거나 메소드 매개 변수로 xyz DomainService를 전달할 책임이있는 디자인으로 이어질 것입니다. 내 생각에 단순히 최적이 아니다.

왜냐하면 나는 예외적 인 경우로서 그 문제에 대한 의존성 주입을 생각하면 "언제"와 "어떻게"라는 논리를 유지하면서 간단하게하기 위해 집계에 새 주소를 만들 수 있을지 생각하고 있습니다. 집계 고객.

질문은 입니다.입니다. 예, 우리는 Spring을 사용하고 있습니다. 전체 집계가 지속되는 Spring Data MongoDb 객체이기 때문에 DomainService를 자동으로 사용할 수 없습니다.이 객체는 런타임 중에 JSON에서 DomainObject를 deserialize합니다. 내가 "다운"집계에 공개 세터에 대해서만이 때문에 물론

@Aggregate 
@Document(collection = "useraddresses) 
public class UserAddresses extends Entity { 

    // When reading from MongoDb the object's creation lays in the responsibility of 
    // Spring Data MongoDb, not Spring, therefore Spring cannot inject the dependency 
    // at all.   
    @Autowired 
    private AddressIdFactory addressIdFactory; 

    @PersistenceConstructor 
    public UserAddresses(String id, Map<String, Address> userAddresses) { 
    setUserAddressesId(new UserAddressesId(id)); 

    if (userAddresses != null) { 
     this.userAddresses.putAll(userAddresses); 
    } 
} 

, 그러나 나는이뿐만 아니라 못생긴, 세터() 또는 뭔가를 호출하여 리포지토리에 수동으로 의존성을 주입 할 수 기술적 인 관심사의 목적.

또 다른 아이디어는 리플렉션을 사용하여 사적인 필드에 직접 종속성을 설정하는 것입니다. 그러나 이것 역시 추악하고 약간의 성능 단점이 있습니다.

귀하의 접근 방법이 궁금합니다.

답변

1

그래서 "제어 반전"입니다. 이는 "인수를 취하는 것"을 말하기 만하면됩니다.

나는 디자인을 전혀 좋아하지 않는다고 비난하지 않는다. 모델의 구현 세부 사항이 애플리케이션으로 누출되는 것처럼 느껴집니다.

수수께끼에 대한 대답의 일부는 다음과 같습니다. 구현의 세부 사항에서 응용 프로그램을 보호하기 위해 인터페이스 할 수 있습니다.

interface UserAddresses { 
    AddressId changeAddress(AddressId addressId, AddressChangeDto dto); 
} 

그래서 저장소에서 응용 프로그램이로드 "총"는,이 인터페이스를 구현하는 뭔가를 얻을 때.

{ 
    UserAddresses root = repository.get(...) 
    root.changeAddress(addressId, dto) 
} 

그러나 는 응용 프로그램이 집계의 "루트 엔티티"직접 이야기되는 경우가 될 필요가 없습니다; 응용 프로그램이 어댑터와 통신 중일 수 있습니다.

Adapter::changeAddress(AddressId addressId, AddressChangeDto dto) { 
    this.target.changeAddress(addressId, dto, this.addressFactory); 
} 

와 마찬가지로 응용 프로그램이 얘기하고 있는지 "를"저장소 어댑터에 주소 공장을 주입 추가 배관과, 데이터 저장소에 액세스하는 저장소의 주위에 어댑터입니다.

+0

당신의 대답은 내가 stackoverflow를 좋아하는 이유 중 하나입니다. 나는 이것에 대해 전혀 생각하지 않았고 확실히 PoC 할 것이다, 고마워! –

+0

나는 이것을 poced했다. 나는 그것이 sig 메소드를 확장 할 수있게 해 준 것을 좋아한다. Impl을 사용하고 인터페이스를 단순하게 유지하십시오. 그러나 나는 어댑터가 실제로 인터페이스를 구현해야하며 더 이상 자체적으로 집계를 구현해야한다는 것을 싫어한다. "코드 찾기"의 경우 IDE에서 두 번째 단계를 수행하여 어댑터에서 집계로 이동해야합니다. UserAddressesImpl은 필드 종속성을 주입하기위한 공용 설정자를 제공하지만 두 솔루션 모두 장단점을 구현하는 두 번째 솔루션을 마련했습니다. 두 솔루션 모두 장점과 단점을 모두 지니고 있습니다. –