뷰 모델에서 비즈니스 오브젝트 사용


11

재사용 가능한 비즈니스 오브젝트를 사용할 때 뷰 모델을 빌드 할 때 가장 좋은 방법은 무엇입니까?

우리는 Builder뷰 모델을 구축하기 위해 호출하는 객체를 사용합니다 . 각 논리적 뷰 단위 (주문, 사용자 등)에 대해 하나의 빌더로, 각 단위는 여러 가지 다른 뷰 모델을 포함 할 수 있습니다 (주문에는 요약, 오더 라인 등이 포함됨).

빌더는보기 모델을 빌드하기 위해 하나 이상의 표준 비즈니스 오브젝트를 통해 데이터를 가져올 수 있습니다.

뷰 모델에서 비즈니스 오브젝트 / 모델을 사용할 때 더 나은 방법은 무엇입니까?

접근법 1

뷰 모델에서 비즈니스 오브젝트를 사용할 수 있습니까?

//Business object in some library
public class Order
{
    public int OrderNum;
    public int NumOrderLines;
    //...
}

//Order builder in website
public class OrderBuilder
{
    public OrderSummary BuildSummaryForOrder(int OrderNum)
    {
        Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
        //Any exception handling, additional logic, or whatever

        OrderSummary obModel = new OrderSummary();
        obModel.Order = obOrder;

        return obModel;
    }
}

//View model
public class OrderSummary
{
    public Some.Business.Logic.Order Order;
    //Other methods for additional logic based on the order
    //and other properties
}

접근법 2

비즈니스 오브젝트에서 필요한 데이터 만 가져옵니다.

//Business object in some library
public class Order
{
    public int OrderNum;
    public int NumOrderLines;
    //...
}

//Order builder in website
public class OrderBuilder
{
    public OrderSummary BuildSummaryForOrder(int OrderNum)
    {
        Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
        //Any exception handling, additional logic, or whatever

        OrderSummary obModel = new OrderSummary()
        {
            OrderNum = obOrder.OrderNum,
            NumOrderLnes = obOrder.NumOrderLines,
        }

        return obModel;
    }
}

//View model
public class OrderSummary
{
    public int OrderNum;
    public int NumOrderLines
    //Other methods for additional logic based on the order
    //and other properties
}

두 가지 장점과 단점을 볼 수 있지만 허용되는 접근법이 있는지 궁금합니다. 접근법 1에서는 모델을 중심으로 코드를 복제하지 않지만 비즈니스 로직에 대한 종속성을 만듭니다. 접근법 2에서는 뷰에 필요한 데이터 만 가져 오지만 모델 주위에 코드를 복제합니다.

답변:


12

옵션 1은 도메인 모델과보기를 밀접하게 연결합니다. 이것은 문제가되는 뷰 모델이 해결하도록 설계된 문제에 위배됩니다.

뷰 모델 "변경 이유"는 뷰 자체가 변경되는 경우입니다. 도메인 모델 객체를 뷰 모델에 넣으면 다른 변경 이유 (예 : 도메인이 변경됨)가 발생합니다. 이는 단일 책임 원칙을 위반했음을 분명히 나타냅니다. 변경해야 할 두 가지 이상의 이유가 있으면 도메인 / 뷰 모델에서 중복 된 유지 보수 비용보다 많은 유지 보수가 필요한 뷰 모델이 나타납니다.

저는 항상 접근법 2를 옹호합니다. 종종 뷰 모델이 도메인 모델 객체와 매우 유사 해 보일 수 있지만, 앞에서 언급 한 차이점은 변경 이유가 다릅니다.


"변경 이유"가 업데이트 (예 : UI 이벤트) 변경이 아니라 유지 보수의 변경을 의미한다고 생각하는 것이 맞습니까?
Andy Hunt

@AndyBursh 맞습니다. 이 기사 , 특히 "Robert C. Martin은 책임을 변경 이유로 정의하고 클래스 또는 모듈에는 변경해야 할 이유가 하나만 있어야한다"는 결론을 내립니다.
MattDavey

나는 당신의 대답을 좋아하지만 몇 가지 생각은 ... 모델이 바뀌기 때문에 뷰 모델이 반드시 변경되는 것은 아닙니다. 변경 한 특정 속성을 바인딩하거나 사용하는 경우에만 참조가 전체 개체에 대한 것이기 때문에 문제가됩니다. 도메인 개체에 대한 참조가 있으면 쉽게 변경하고 다시 저장할 수 있습니다. 저장 방법도 도메인 개체에 종속되므로 뷰 모델을 다시 변환하거나 비즈니스 방법을 설정하여 좋지 않은 뷰 모델을 허용해야합니다. 나는 여전히 # 2가 가장 타당하다고 생각하지만 단지 2 센트입니다.
KingOfHypocrites

VM에 도메인 개체를 가질 수 없다면 Orders 배열과 같이 더 복잡한 것을 어떻게 표현 하시겠습니까?
Jeff

즉, 사용자 표시를위한 타임 스탬프 형식 지정 은 도메인 계층이 아닌 보기 계층에 속해야 하며 도메인 수준 오브젝트는 형식화되지 않은 원시 타임 스탬프 만보기 오브젝트에 반환해야합니다. 포맷터 로직을 포함합니까?
The_Sympathizer

2

옵션 1은 코드 중복을 피하기 때문에 바람직합니다. 그게 다야.

도메인 모델이 크게 변경되면 뷰가 변경되어야합니다. 옵션 2를 사용하면 뷰 자체와 뷰 모델 및 빌더를 변경해야합니다. 그런 종류의 것은 유지 보수에 대한 절대 독입니다. 야 그니.

개별 뷰 모델을 갖는 요점은 비즈니스 모델과 분리 된 뷰 (예 : 현재 선택된 탭)에 대해서만 의미있는 상태를 유지하는 것입니다. 그러나 비즈니스 데이터 자체는 복제하기보다는 재사용해야합니다.


YAGNI-대부분의 소프트웨어 디자인 문제를 해결하는 비밀 암살자.
Martin Blore

6
미안하지만 이것은 가장 사소한 응용 프로그램을 제외하고는 끔찍한 조언입니다. 뷰 모델에는 상태가 없습니다. 그것들은 데이터 전송 객체입니다. 선택된 탭은 뷰 구조의 일부이며 뷰 모델의 데이터와 관련이 없습니다. 프로그램을 올바르게 구성하고 Automapper와 같은 것을 사용하여 뷰 모델을 수화하면 유지 관리는 악몽이 아닙니다.
Lucifer Sam

"도메인 모델이 크게 변경되면 뷰가 변경되어야 할 것입니다." -동의합니다. 그러나 도메인을 약간 변경 한 경우는 어떻습니까? 옵션 1을 사용하면 도메인을 조금만 변경할 때도 (속성 이름 바꾸기 만해도) 해당하는보기 변경이 필요합니다. 이것은 또한 유지 보수성을위한 절대 독입니다.
MattDavey

@MattDavey : 별도의 뷰 모델을 사용하여 속성의 이름을 바꾸면 뷰도 변경해야하고 (또는 도메인과 뷰 모델 사이의 맵이 무엇이든) 이제 같은 일에 대해 서로 다른 두 개의 이름이있어 혼동을 일으킬 수 있습니다.
Michael Borgwardt

@Lucifer Sam : 분명히 뷰 모델의 개념이 매우 다릅니다. 멍청한 터미널을위한 메인 프레임 앱을 설명하고 있지만 현대 웹이나 팻 클라이언트 앱은 아닙니다.
Michael Borgwardt

2

원칙과 진언은 때때로 디자인을 인도하는 데 가치가 있지만 여기에 내 실제적인 답변이 있습니다.

뷰 모델이 JSON 또는 XML로 직렬화되는 것을 상상해보십시오. 도메인 모델을 직렬화하려고하면 텍스트가 엉망이되어 순환 참조 및 기타 문제가 발생할 가능성이 큽니다.

뷰 모델의 목적은 도메인 모델을 그룹화하여 뷰에서 사용할 수 없도록하는 것입니다. 대신 뷰 모델은 완전히 평평한 뷰 모델이어야합니다. 실제 화면에서보고있는 것입니다. 뷰 로직은 뷰 모델에있는 데이터 구조화에만 관심이 있어야합니다.

이상적으로는 뷰 모델이 거의 전부 사전 형식화 된 문자열로 구성되어야합니다. 생각해보십시오 ... 뷰 모델에서 DateTime 또는 10 진수를 원하지 않아도 C #, Javascript, Objective-C 등의 형식 논리를 수행하지 못하기 때문에


2
도메인 모델을 직렬화하는 데 아무런 문제가 없었습니다. 그리고 모델의 모든 것을 문자열로 변환합니까? 진심이야?
Michael Borgwardt

3
@MichaelBorgwardt 네, 뷰 모델이되어야합니다. 도메인 모델을 직렬화하여 모든 곳으로 전송하고 싶지는 않습니다. 모든 비즈니스 로직은 한 곳에 안전하게 집에 있어야합니다. 그러나 뷰는 유연하고 모든 장치에서 렌더링 할 수 있어야하므로 구조, 데이터 및 스타일을 완전히 분리해야합니다.
Lucifer Sam

미안하지만, 그것은 모든 응용 프로그램, 기간에 대한 끔찍한 조언입니다. 이는 유연성과 정반대 인 중복 코드로 가득 찬 오버 엔지니어링 된 애플리케이션으로 이어집니다.
Michael Borgwardt

1
@MichaelBorgwardt 그것은 엔티티가 행동이 거의 없거나 전혀없는 속성 백보다 작은 빈혈 도메인 모델 작업에 익숙한 것처럼 들립니다. 이 경우, DTO / 뷰 모델은 기본적으로 복제본이됩니다. 그러나 복잡한 관계가있는 풍부한 도메인 모델이있는 경우 DTO / 뷰 모델 계층이 필요하며 도메인 엔터티와 유사하지 않습니다.
MattDavey

@MattDavey : 작업에 익숙한 도메인 모델이 부자 일뿐 아니라 진정한 kleptocrats처럼 들립니다. 나는 빈혈 모델을 좋아하지 않지만 여전히 모델이며 그들의 행동은 도메인을 나타내는 것으로 국한되어야합니다. 단일 책임 원칙과 그 모든 것
Michael Borgwardt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.