Używanie obiektów biznesowych w modelach widoku

11

Kiedy używa się obiektów biznesowych wielokrotnego użytku, co uważa się za najlepszą praktykę przy budowaniu modeli widoków?

Używamy obiektu, który nazywamy, Builderdo budowy naszych modeli widoków. Jeden konstruktor dla każdej logicznej jednostki widoków (zamówienia, użytkownicy itp.), Przy czym każda jednostka może zawierać wiele różnych modeli widoków (zamówienia zawierają podsumowanie, wiersze zamówień itp.).

Konstruktor może pobierać dane przez jeden lub więcej standardowych obiektów biznesowych w celu zbudowania modelu widoku.

Jaka jest najlepsza praktyka, jeśli chodzi o stosowanie obiektów / modeli biznesowych w modelach widoku?

Podejście 1

Zezwolić na użycie obiektów biznesowych w modelu widoku?

//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
}

Podejście 2

Pobieraj tylko niezbędne dane z obiektów biznesowych

//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
}

Widzę zalety i wady obu, ale zastanawiam się, czy istnieje zaakceptowane podejście? W podejściu 1 nie występuje powielanie kodu wokół modeli, ale tworzy zależność od logiki biznesowej. W podejściu 2 pobierasz tylko dane potrzebne do widoku, ale kopiujesz kod wokół modeli.

Andy Hunt
źródło

Odpowiedzi:

12

Opcja 1 tworzy ścisłe powiązanie między modelem domeny a widokiem. Jest to sprzeczne z bardzo problematycznymi modelami widoku zaprojektowanymi do rozwiązania.

Widok modeluje „powód do zmiany”, jeśli sam widok się zmienia. Umieszczając obiekt modelu domeny w modelu widoku, wprowadzasz kolejny powód do zmiany (np. Domena została zmieniona). Jest to wyraźny sygnał naruszenia zasady pojedynczej odpowiedzialności. Posiadanie dwóch lub więcej powodów do zmiany prowadzi do modeli podglądu, które wymagają dużo konserwacji - prawdopodobnie więcej niż postrzegane koszty utrzymania duplikacji w modelach domeny / widoku.

Zawsze opowiadałbym się za podejściem 2. Często zdarza się, że modele widoków mogą wyglądać bardzo podobnie, nawet identycznie jak obiekty modeli domen, ale rozróżnienie, o którym wspomniałem, jest ich różnymi przyczynami zmian.

MattDavey
źródło
Czy mam rację sądząc, że przez „powód zmiany” rozumiesz zmianę w sensie utrzymania, a nie zmianę w sensie aktualizacji (np. Zdarzenia interfejsu użytkownika)?
Andy Hunt
@AndyBursh tak, to prawda - zobacz ten artykuł , w szczególności wiersz „Robert C. Martin definiuje odpowiedzialność jako powód zmiany i stwierdza, że ​​klasa lub moduł powinien mieć jeden i tylko jeden powód do zmiany”.
MattDavey,
Podoba mi się twoja odpowiedź, ale kilka myśli ... Model widoku niekoniecznie się zmienia tylko dlatego, że zmienia się model. Problem byłby związany tylko z tym, że wiązałeś lub korzystałeś z określonej właściwości, która się zmieniła, ponieważ Twoje odwołanie dotyczy całego obiektu. Odwołanie do obiektu domeny ułatwia wprowadzanie zmian i zapisywanie go ponownie. Twoje metody składowania są również zależne od obiektu domeny, więc musisz przekonwertować model widoku z powrotem lub skonfigurować metodę biznesową, aby akceptowała modele widoku, które też nie są dobre. Nadal uważam, że numer 2 ma sens, ale tylko o dwa centy.
KingOfHypocrites,
Jeśli nie możesz mieć obiektów domeny na maszynie wirtualnej, to w jaki sposób reprezentowałbyś coś bardziej skomplikowanego, jak tablica zamówień?
Jeff
Czy to oznacza, że ​​na przykład formatowanie znacznika czasu do wyświetlania przez użytkownika powinno należeć do warstwy widoku, a nie do warstwy domeny, a obiekty na poziomie domeny powinny zwracać tylko surowy, niesformatowany znacznik czasu do obiektów widoku, a te ostatnie powinny być tym, co powinno zawierać logikę formatyzatora?
The_Sympathizer
2

Opcja 1 jest preferowana, ponieważ pozwala uniknąć duplikacji kodu. Otóż ​​to.

Jeśli model domeny zmieni się znacząco, jest prawie pewne, że widok i tak będzie musiał się zmienić. W przypadku opcji 2 musisz zmienić model widoku ORAZ konstruktora, a także sam widok. Tego rodzaju rzeczy są absolutną trucizną za łatwość utrzymania. YAGNI.

Istotą posiadania osobnego modelu widoku jest utrzymanie stanu, który ma znaczenie tylko dla widoku (np. Która karta jest aktualnie wybrana), odrębnego od modelu biznesowego. Ale same dane biznesowe powinny być ponownie wykorzystywane, a nie duplikowane.

Michael Borgwardt
źródło
YAGNI - tajny zabójca rozwiązujący większość problemów związanych z projektowaniem oprogramowania.
Martin Blore,
6
Przykro mi, ale jest to okropna rada dla wszystkich oprócz najbardziej trywialnych aplikacji. Wyświetl modele nie mają stanu. Są obiektami przesyłania danych. Ta wybrana karta jest częścią STRUKTURY widoku i nie ma nic wspólnego z DANYMI w modelu widoku. Konserwacja nie jest koszmarem, jeśli odpowiednio ustrukturyzujesz program i użyjesz czegoś takiego jak Automapper, aby nawodnić swoje modele widoków.
Lucyfer Sam
„Jeśli model domeny ulegnie znacznej zmianie, jest prawie pewne, że widok i tak będzie musiał się zmienić”. - Zgoda. Ale co powiesz na małą zmianę w domenie? W przypadku opcji pierwszej każda drobna zmiana w domenie (nawet zmiana nazwy właściwości) wymaga odpowiedniej zmiany w widoku. Jest to również absolutna trucizna dla łatwości utrzymania.
MattDavey,
@MattDavey: jeśli zmienisz nazwę właściwości, to w przypadku oddzielnego modelu widoku musisz również zmienić widok (lub dowolne mapy między domeną a modelem widoku), a teraz masz dwie różne nazwy dla tej samej rzeczy, co z pewnością spowoduje zamieszanie.
Michael Borgwardt,
@Lucifer Sam: oczywiście mamy bardzo różne koncepcje tego, czym jest model widoku. Twój dźwięk brzmi dla mnie bardzo, bardzo dziwnie, jakbyś opisywał aplikacje mainframe dla głupich terminali, ale z pewnością nie jest to nowoczesne aplikacje internetowe lub gruby klient.
Michael Borgwardt,
2

Zasady i mantry są czasem cenne przy projektowaniu przewodnim ... ale oto moja praktyczna odpowiedź:

Wyobraź sobie, że twoje modele widoków są serializowane do JSON lub XML. Jeśli spróbujesz serializować swoje modele domen, skończysz z ohydnym bałaganem tekstu i najprawdopodobniej napotkasz problemy z okólnikami i innymi problemami.

Celem modelu widoku nie jest grupowanie modeli domen, aby widok mógł je wykorzystać. Zamiast tego model widoku powinien być całkowicie płaskim modelem widoku ... rzeczywistą rzeczą, na którą patrzysz na ekranie. Twoja logika widoku powinna dotyczyć tylko strukturyzacji danych obecnych w modelu widoku.

Idealnie twój model widoku powinien składać się prawie całkowicie ze wstępnie sformatowanych ciągów. Pomyśl o tym ... nie chcesz nawet DateTime ani wartości dziesiętnych w modelu widoku, ponieważ utkniesz w logice formatowania w C #, JavaScript, Objective-C itp.

Lucyfer Sam
źródło
2
Nigdy nie miałem problemów z serializacją modeli domen. A konwertowanie wszystkiego na ciągi w modelu? Poważnie?
Michael Borgwardt,
3
@MichaelBorgwardt Tak, właśnie takim POWINIEN być model widokowy. Nie chcesz serializować swoich modeli domen i wysyłać ich wszędzie. Cała logika biznesowa powinna pozostać bezpiecznie w domu w jednym miejscu. Widoki powinny być jednak elastyczne i można je renderować na dowolnym urządzeniu, dlatego chcesz całkowicie oddzielić STRUKTURĘ, DANE i STYL.
Lucyfer Sam
Przepraszamy, ale TO jest okropna rada dla każdej aplikacji, kropka. Prowadzi to do nadinżynieryjnych aplikacji pełnych duplikatów kodu, które są dokładnym przeciwieństwem elastyczności.
Michael Borgwardt,
1
@MichaelBorgwardt brzmi to tak, jakbyś był przyzwyczajony do pracy z anemicznymi modelami domenowymi, w których byty to niewiele więcej niż torby własności o małym lub zerowym zachowaniu. W takim przypadku tak, model DTO / View byłby w zasadzie duplikatem. Jednak jeśli masz bogaty model domeny ze złożonymi relacjami, warstwa DTO / modeli widoków staje się konieczna i nie będą one tak podobne do jednostek domeny.
MattDavey,
@MattDavey: Wygląda na to, że modele domen, z którymi przywykłeś do pracy, to nie tylko bogaci, ale prawdziwi kleptokraci. Nie lubię też modeli anemicznych, ale nadal są modelami, a ich zachowanie powinno ograniczać się do reprezentowania domeny. Zasada jednej odpowiedzialności i tak dalej ...
Michael Borgwardt,