2009-03-16 2 views
7

저는이 전체 DDD \ LinqToSql 비즈니스의 약간의 혼란에 빠져있는 것처럼 보입니다. 나는 POCOS와 linq를 사용하여 SQL에 시스템을 구축하고 있으며 집계 루트에 대한 저장소가 있습니다. 예를 들어 Order-> OrderLine 클래스가있는 경우 Order는 집계의 루트이므로 OrderLine이 아닌 Order에 대한 저장소가 있습니다. 저장소에는 Order를 삭제하기위한 delete 메소드가 있지만 OrderLines을 어떻게 삭제합니까? OrderLines 컬렉션에서 행을 제거했지만 기본 l2s 테이블에서 OrderLine을 삭제해야하는 RemoveOrderLine이라는 메소드가 있다고 생각했을 것입니다. OrderLine 저장소가 없으므로 어떻게해야할까요?도메인 기반 디자인 (Linq to SQL) - 집계의 일부를 어떻게 삭제합니까?

아마도 도메인 개체가 집계 내에서 항목을 삭제하는 데 실제로 사용하는 루트 및 내부 일반 리포지토리를 쿼리하기위한 특수 공개 게시판이 있습니까?

public class OrderRepository : Repository<Order> { 
    public Order GetOrderByWhatever(); 
} 

public class Order { 
    public List<OrderLines> Lines {get; set;} //Will return a readonly list 
    public RemoveLine(OrderLine line) { 
     Lines.Remove(line); 
     //************* NOW WHAT? *************// 
     //(new Repository<OrderLine>(uow)).Delete(line) Perhaps?? 
     // But now we have to pass in the UOW and object is not persistent ignorant. AAGH! 
    } 
} 

내가이 고민 유일한 .... 나는 희망이 될 어차피 같은 다른 사람들이 한 일을 알고 싶어요 .... 감사합니다

답변

2

당신은 주문에 RemoveOrderLine를 호출하는 관련 논리를 호출하십시오. 여기에는 지속 된 버전의 변경 작업이 포함되지 않습니다.

나중에 수정 된 주문을받는 저장소에서 저장/업데이트 메소드를 호출합니다. 도메인 객체에서 변경된 사항을 알면 특정 문제가 발생합니다. 몇 가지 옵션이 있습니다. (목록에있는 것 이상 있습니다) :

  • 도메인 객체가 변경 사항을 추적하도록하십시오. x가 주문 라인에서 삭제 될 필요가 있음을 추적하는 것을 포함합니다. 엔티티 추적과 비슷한 것이 고려 될 수 있습니다.
  • 지속 된 버전을로드하십시오. 지속 된 버전과 메모리 내 버전 간의 차이점을 인식하는 저장소에 코드를 가지고 변경 사항을 실행하십시오.
  • 지속 된 버전을로드하십시오. 루트 집계에 코드가 있으면 원래 루트 집계가 주어진 차이점을 얻을 수 있습니다.
+0

이 설명은 도메인 엔터티에서 직접 ORM을 사용하지 않는다고 가정합니다. 내가 linq SQL에 자동으로 도메인 개체에 변경 사항을 유지하게함으로써 위에서 설명한 추가 작업을 피할 수 있기를 바랬습니다 ... –

+0

y, 그게 진짜 문제입니다. 나는 위와 같이 해왔다. Nhibernate에서 더 많은 고급 시나리오를 지원하는 의견을 항상 볼 수 있지만이 시나리오 유형 (POCO 만 사용하는 것은 아닙니다)을 사용하는 방식을 살펴 보지 않았습니다. – eglasius

+0

감사합니다. 비록 몹시 이상적이지는 않지만 (LTS의 잘못, 당신의 대답이 아닙니다.) 이것이 어떻게해야 할 지에 대한 내 자신의 점심을 확인하는 것 같습니다 ... – Funka

1

먼저 인터페이스를 공개하여 누계 루트 (즉, 주문())에 대한 참조를 가져와야합니다. 팩토리 패턴을 사용하여 집계 루트 (즉, 주문())의 새 인스턴스를 새로 만듭니다.

그렇다면 Aggregate Root의 메소드는 자체적으로가 아니라 관련된 객체에 액세스합니다. 또한 집계 루트 (예 : 사용자가 명시한 Lines() IList 컬렉션)에서 복잡한 유형을 공용으로 공개하지 마십시오. 이것은 decremeter (sp ck)의 법칙을 위반합니다. 즉, "Dot Walk"방법으로 Order.Lines.Add()와 같은 방법을 사용할 수 없다고 말합니다.

또한 클라이언트가 집계 루트의 내부 개체에 대한 참조에 액세스 할 수있게하는 규칙을 위반합니다. 집계 루트 내부 개체의 참조를 반환합니다. 외부 클라이언트가 해당 객체에 대한 참조를 보유 할 수없는 한. 즉, "OrderLine"을 RemoveLine()에 전달합니다. 외부 클라이언트가 모델의 내부 상태 (예 : Order() 및 OrderLines())를 제어하도록 허용 할 수 없습니다. 따라서 OrderLine이 그에 따라 작동 할 새 인스턴스가 될 것으로 기대해야합니다.의 참조를 취득 할 수있는 인터페이스를 통해 외부 클라이언트가 직접 IOrderLinesRepository에 액세스 할 수있는 권한이 있는가

public class OrderFactory 
{ 
    public Order CreateComponent(Type type) 
    { 
    // Create your new Order.Lines() here, if need be. 
    // Then, create an instance of your Order() type. 
    } 
} 

:

public interface IOrderRepository 
{ 
    Order GetOrderByWhatever(); 
} 

internal interface IOrderLineRepository 
{ 
    OrderLines GetOrderLines(); 
    void RemoveOrderLine(OrderLine line); 
} 

public class Order 
{ 
    private IOrderRepository orderRepository; 
    private IOrderLineRepository orderLineRepository; 
    internal Order() 
    { 
    // constructors should be not be exposed in your model. 
    // Use the Factory method to construct your complex Aggregate 
    // Roots. And/or use a container factory, like Castle Windsor 
    orderRepository = 
      ComponentFactory.GetInstanceOf<IOrderRepository>(); 
    orderLineRepository = 
      ComponentFactory.GetInstanceOf<IOrderLineRepository>(); 
    } 
    // you are allowed to expose this Lines property within your domain. 
    internal IList<OrderLines> Lines { get; set; } 
    public RemoveOrderLine(OrderLine line) 
    { 
    if (this.Lines.Exists(line)) 
    { 
     orderLineRepository.RemoveOrderLine(line); 
    } 
    } 
} 

은 주문()의 새로운 인스턴스를 생성하여 공장을 잊지 마세요 집계 루트 내의 값 개체를 그러나 나는 집계 루트의 모든 방법을 사용하여 참조를 강제로 차단하려고합니다. 따라서 IOrderLineRepository를 내부로 표시하여 노출되지 않도록 할 수 있습니다.

전 실제로 모든 집계 루트 생성을 여러 팩터 리로 그룹화합니다. 나는 "어떤 집계 된 뿌리는 복잡한 유형의 공장을 갖을 것이고 다른 것들은 그렇지 않을 것"이라는 접근 방식을 좋아하지 않았다. 도메인 모델링 전반에 걸쳐 동일한 논리를 유지하는 것이 훨씬 쉽습니다. "오, Sales()는 Order()와 같은 집계 루트이기 때문에 공장도 있어야합니다."

마지막으로 SalesOrder()와 SalesOrder()의 두 모델을 사용하는 경우 SalesOrder()의 해당 인스턴스를 만들고 작동시키는 서비스를 사용하지 않을 것입니다. Sales() 또는 Order() Aggregate Root 나 저장소 또는 공장은 SalesOrder() 엔터티를 제어합니다.

도메인 드라이브 디자인 (DDD)에 Abel Avram과 Floyd Marinescu가 this free book을 매우 강력히 추천합니다. 귀하의 질문에 바로 답을 얻을 수 있습니다. 도메인 엔티티를 모듈 등으로 더 잘 분리하는 방법과 함께

편집 : 추가 더 많은 코드로

+0

응답 해 주셔서 감사합니다.주문에 대한 방법을 통해 노출 된 주문 행의 동작에 대해서는 확실하지 않습니다. 그것은 GetOrderLineVatAmount 라인에 있어야 그렇지 않으면 순서에 함수에 라인을 전달해야 보인다. 또한, 왜 Order에는 OrderRepos에 대한 ref가 있습니까? –

+0

모두 "주문 회선"이 어떻게 표시되는지에 따라 다릅니다. 위의 코드는 "가치 객체"접근법을 반영합니다. Value Object는 ID가없는 방식으로 Entity와 다릅니다. 각 OrderLine에 ID가 있습니까? 그렇다면 정말로 그것에 대해 생각하면됩니까? – eduncan911

+0

OrderLine.LineNumber OrderLine.Description OrderLine.Cost OrderLine.PartNumber 여기에 값 개체의 정의는 정체성에없는; 그러나 그것의 가치의 합계. 위의 답에서 알 수 있듯이 IOrderLineRepository()를 노출 할 수 있으며 원격 클라이언트는 액세스 권한이 있습니다. – eduncan911

0

....까지 내가 (대신 SQL 링크보다)하지만 효과는 OrderLine에 대한 REPOS를 필요가없는 자 NHibernate를 사용하여 전환 한 을 따르십시오. Order에서 컬렉션에서 OrderLine을 제거하면 데이터베이스에서 OrderLine 만 삭제됩니다 (매핑이 올바르게 수행되었다고 가정 할 때). 메모리 리포지토리로 교체 할 때, 특정 주문 라인을 검색하려면 (주문 상위 정보를 알지 못함) orderlineid = 값을 orderline에 연결하는 nhibernate 쿼리에 linq를 쓸 수 있습니다. 그런 식으로 db와 메모리에서 쿼리 할 때 작동합니다. 그럼 거기에 ...

+0

버머, 나는 누군가 Linq를 Sql로 포기하지 않고도 (매우 훌륭하고 매우 적절한!) 질문에 대해 더 나은 대답을 얻길 바랬습니다! – Funka

1

이 정확한 문제로 고심하고 나면 해결책을 찾았습니다. 디자이너가 l2sl로 생성 한 것을 살펴본 결과, 솔루션은 주문과 주문 사이의 양방향 연결에 있다는 것을 깨달았습니다. 주문에는 많은 주문 라인이 있고 주문 라인에는 단일 주문이 있습니다. 해결책은 양방향 연관과 DeleteOnNull이라는 맵핑 속성을 사용하는 것입니다 (이 정보는 완전한 정보를 얻기 위해 Google로 보낼 수 있습니다). 마지막으로 놓친 부분은 엔티티 클래스가 l2s 엔티티 집합의 Add 및 Remove 이벤트에 등록해야한다는 것입니다. 이러한 핸들러에서 주문 행의 Order 연관을 null로 설정해야합니다. l2s 디자이너가 생성 한 코드를 보면이 예를 볼 수 있습니다.

나는 이것이 실망 스럽다는 것을 알고 있지만, 고난을 겪은 후에 나는 그것을 작동 시켰습니다.