2009-02-27 3 views
8

저는 현재 프로젝트의 일부 코드를 리팩터링하고 있습니다. 결국 도메인 객체가 아닌 서비스 클래스에 많은 비즈니스 로직을 넣었습니다. 이 시점에서 대부분의 도메인 개체는 데이터 컨테이너입니다. 필자는 대부분의 비즈니스 로직을 서비스 객체에 작성하고 나중에 모든 것을 리팩터링하여 더 좋고, 재사용 가능하며, 더 읽기 쉬운 형태로 만들기로 결정했습니다. 그런 식으로 어떤 코드가 도메인 객체에 배치되어야하는지, 어떤 코드가 새로운 객체로 분리되어야하는지, 어떤 코드가 서비스 클래스에 남겨 져야 하는지를 결정할 수 있습니다. 이 도메인 객체에 좋은 재료를 만들 것처럼 만 입력 매개 변수는 도메인 개체 자체이기 때문에도메인 기반 디자인에서 다른 개체의 repostiories에 대한 호출을 도메인 개체에 넣는 것이 DDD 위반일까요?

public decimal CaculateBatchTotal(VendorApplicationBatch batch) 
{ 
    IList<VendorApplication> applications = AppRepo.GetByBatchId(batch.Id); 

    if (applications == null || applications.Count == 0) 
      throw new ArgumentException("There were no applications for this batch, that shouldn't be possible"); 
    decimal total = 0m; 
    foreach (VendorApplication app in applications) 
      total += app.Amount; 
    return total; 
} 

이 코드는 것 같다 그래서 몇 가지 코드가 있습니다. 일부 리팩터링을위한 완벽한 후보자처럼 보입니다. 그러나 유일한 문제는이 객체가 다른 객체의 저장소를 호출한다는 것입니다. 그래서 나를 서비스 수업에 남겨두고 싶습니다.

내 질문에 따라서 다음과 같습니다

  • 은 어디에서이 코드를 넣어 것
      ?
    1. 이 기능을 중단 하시겠습니까?
    2. 엄격한 도메인 기반 디자인을 따르는 사람이 어디에 두겠습니까?
    3. 왜? 시간에 대한

    감사합니다.

    편집 주 :이 한 상에 ORM을 사용할 수 없습니다, 그래서 나는 게으른 로딩 솔루션을 사용할 수 없습니다.

    편집 주 2 : 나는 때문에-될 데이터 층 반사 (나의 생각)를 사용하여 도메인 개체를 인스턴스화하는 방법의 매개 변수에 걸릴 생성자를 변경할 수 없습니다.

    편집 주 3 : 나는 단지 특정 배치에 총 응용 프로그램 수 있어야 것처럼 보인다 배치 객체가 응용 프로그램을 그냥 목록을 총 할 수 있어야한다고 생각하지 않는다. 그렇지 않으면 서비스 클래스에 함수를 남겨 두는 것이 나에게 더 합리적입니다.

  • +0

    파스칼 기반 로컬 변수. 왝. – yfeldblum

    +0

    @ Justice, 나는 너의 고통을 느낀다 – mbillard

    답변

    5

    도메인 개체의 리포지토리에 액세스하지 않아야합니다.

    당신이 할 수있는 일은 도메인 개체에 적절한 정보를 제공하거나 서비스 또는 생성자에 의해 설정된 도메인 개체에 대리인이 있어야한다는 것입니다.

    public DomainObject(delegate getApplicationsByBatchID) 
    { 
        ... 
    } 
    
    +0

    좋은 생각, 나는 그것을 좋아한다. –

    +0

    동의합니다. 따라서이 인스턴스의 경우 서비스 클래스에서 저장소 호출이 필요한 작업을 수행 한 다음 계산이 필요하면 컬렉션 또는 단일 개체를 엔터티에 전달합니다. –

    +0

    예, 도메인 개체는 대리자를 사용하여 데이터를 가져옵니다 필요한 경우, 위임자는 도메인 객체를 만드는 것에 의해 설정됩니다. 위임은 직접 서비스 메소드 또는 저장소 메소드가 될 수 있습니다. – mbillard

    2

    이유는 매개 변수 대신 VendorApplicationBatch로 IList의 <VendorApplication>에 통과하지? 이것에 대한 호출 코드는 아마도 AppRepo에 액세스 할 수있는 서비스에서 왔을 것입니다. 그렇게하면 도메인 기능이 그 데이터가 어디서 왔는지를 모르는 채로 유지할 수있는 동안 저장소 액세스가 속하는 위치에 올라 가게됩니다.

    +0

    저장소에 일괄 처리하지만 getbybatchid가 아닌 코드를 넣으면 그 일괄 처리에서 전달 된 응용 프로그램이 있다는 보장이 없습니다. 나는 일괄 처리가 포함하고있는 애플리케이션을 합계 할 수 있어야한다고 생각합니다. –

    +0

    은 저장소의 일괄 처리를 합계합니다 -> 일괄 처리 된 도메인 개체의 일괄 처리를 합칩니다. –

    +0

    잘 모르겠습니다. VendorApplicationBatch를 전달하는 대신 응용 프로그램 목록에서 전달하는 것 외에는 코드를 그대로 유지하는 것이 좋습니다. GetByBatchID는 여전히 저장소에 있으며 코드 합계는 여전히 도메인에 있습니다. –

    5

    나는 DDD에 어떤 전문가는 아니지만 그러나 나는 나를 위해 바로이 질문에 대답 위대한 제레미 밀러 기사를 기억한다. 일반적으로 도메인 객체와 관련된 로직을 원할 것입니다. 이러한 객체 내부에서는 서비스 클래스가이 로직을 포함하는 메소드를 실행합니다.예

    I :이

    편집 (나 자신이 당신이 언급 한 같은 서비스 클래스 내부에 많은 로직에 넣어 발견) 나 엔티티 클래스로 도메인 특정 논리를 밀어, 적은 부피 내 서비스 클래스를 유지 도움 내가 지금과 같은 속성을 설정합니다 엔티티 클래스, 그래서 간단한 확인을 위해 엔터프라이즈 라이브러리를 사용 : 엔티티 각 개체는 사항을 충족하는지 확인합니다 다음 "IsValid"메서드가 기본 클래스에서 상속

    [StringLengthValidator(1, 100)] 
    public string Username { 
        get { return mUsername; } 
        set { mUsername = value; } 
    } 
    

    유효성 확인 기준

     public bool IsValid() 
        { 
         mResults = new ValidationResults(); 
         Validate(mResults); 
    
         return mResults.IsValid(); 
        } 
    
        [SelfValidation()] 
        public virtual void Validate(ValidationResults results) 
        { 
         if (!object.ReferenceEquals(this.GetType(), typeof(BusinessBase<T>))) { 
          Validator validator = ValidationFactory.CreateValidator(this.GetType()); 
          results.AddAllResults(validator.Validate(this)); 
         } 
         //before we return the bool value, if we have any validation results map them into the 
         //broken rules property so the parent class can display them to the end user 
         if (!results.IsValid()) { 
          mBrokenRules = new List<BrokenRule>(); 
          foreach (Microsoft.Practices.EnterpriseLibrary.Validation.ValidationResult result in results) { 
           mRule = new BrokenRule(); 
           mRule.Message = result.Message; 
           mRule.PropertyName = result.Key.ToString(); 
           mBrokenRules.Add(mRule); 
          } 
         } 
        } 
    

    다음으로 우리는 그렇게 같은 방법 저장 서비스 클래스에서이 "IsValid"방법을 실행해야합니다

    public void SaveUser(User UserObject) 
    { 
        if (UserObject.IsValid()) { 
         mRepository.SaveUser(UserObject); 
        } 
    } 
    

    더 복잡한 예를 들어 은행 계좌 수 있습니다. deposit 로직은 계정 객체 내에 있지만 서비스 클래스는이 메소드를 호출합니다.

    1

    VendorApplicationBatch는 도메인 객체 내에 Lazy loaded IList를 포함해야하며 논리는 도메인에 있어야합니다.

    예 (에어 코드)의 경우 :

    public class VendorApplicationBatch { 
    
        private IList<VendorApplication> Applications {get; set;}; 
    
        public decimal CaculateBatchTotal() 
        { 
         if (Applications == null || Applications.Count == 0) 
          throw new ArgumentException("There were no applications for this batch, that shouldn't be possible"); 
    
         decimal Total = 0m; 
         foreach (VendorApplication App in Applications) 
          Total += App.Amount; 
         return Total; 
        } 
    } 
    

    이 쉽게 NHibernate에 같은 ORM으로 수행하고 나는 그것이 최선의 해결책이 될 것이라고 생각한다.

    +0

    이것이 최선의 해결책이라는 것에 동의합니다. 불행히도 데이터 레이어를 디자인하는 방법에 대한 결정이 내려 졌을 때 ORM을 사용할 수 없기 때문에 주변에 있지 않았습니다. 따라서 ORM을 사용할 수 없으며 장소에 킥 - 엉 저장소 시스템을 만들었습니다. –

    +0

    그러면 대답은 서비스가 VendorApplication 목록을 가져오고 Batch 객체가 로직을 수행하도록하는 것입니다. 예 : VendorApplicationBatch.CaculateBatchTotal (IList applications) – gcores

    +0

    다른 배치에 속할 수도있는 전체 응용 프로그램에 배치를 허용하는 것이 옳지 않다고 생각합니다. 배치가 자신의 공급 업체 만 합계 할 수 있음을 보증 할 수있는 방법이 있어야합니다. 응용 프로그램. 따라서 나는이 기능을 배치에 넣을 수 없다. –

    0

    당신의 CalculateTotal이 VendorApplication의 모음을위한 서비스이며, 즉 일괄 위해 VendorApplication의의 컬렉션을 반환하는 일괄 클래스의 속성으로 자연스럽게 맞는 나에게 보인다. 그래서 일부 다른 서비스/컨트롤러/뭐든간에 VendorApplication의 적절한 컬렉션을 일괄 처리에서 검색하여 VendorApplicationTotalCalculator 서비스 (또는 비슷한 것)로 전달합니다. 하지만 이는 일부 DDD 집계 루트 서비스 규칙이나 내가 모르는 (DDD 초심자) 일부를 해칠 수 있습니다.

    관련 문제