Czy DTO powinno być płaskie?

0

Mam aplikację wielowarstwową i oddzieliłem modele domenowe(staram się DDD) oraz modele DTO do trasnferu między warstwami.

Moje DTO:

  1. Jest zdefiniowane w projekcie Core wraz z modelami domenowymi
  2. Wychodzi i wchodzi do repozytorium
  3. Jest przekształcane na modele domenowe w projekcie Services gdzie są operacje na domene
  4. Wychodzi jaki response z WebAPI

I teraz się zastanawiam czy ze względu na punkt 4 moje DTO powinno być płaskie skoro idzie do klientów(angular, mobile) - A może to zła praktyka wypuszczać DTO, które chodzi po warstwach poza aplikacje?

Płaskie:

 public class CashCase
    {
        public string Id;
        public string Title;
        public DateTime Opened;

        public decimal DifferenceAmount;
        public decimal DifferenceOriginalAmount;
        public decimal DifferenceOriginalFcyAmount;
        public CashCaseDifferenceCurrency DifferenceCurrency;
        public CashCaseDifferenceSource DifferenceSource;
        public CashCaseDifferenceType DifferenceType;
    }


    public enum CashCaseDifferenceCurrency
    {
        PLN
    }

    public enum CashCaseDifferenceSource
    {
        ATM,
        [EnumMember(Value = "ATM Offsite")]
        ATMOffsite,
        Cash
    }

    public enum CashCaseDifferenceType
    {
        Surplus, Deficiency
    }  

Zorganizowane:

    public class CashCase
    {
        public string Id;
        public string Title;
        public DateTime Opened;

        public CashCaseDifference Difference;
    }

    public class CashCaseDifference
    {
        public decimal Amount { get; set; }
        public decimal OriginalAmount;
        public decimal OriginalFcyAmount;
        public CashCaseDifferenceCurrency Currency { get; set; }
        public CashCaseDifferenceSource Source { get; set; }
        public CashCaseDifferenceType Type { get; set; }
    }

    public enum CashCaseDifferenceCurrency
    {
        PLN
    }

    public enum CashCaseDifferenceSource
    {
        ATM,
        [EnumMember(Value = "ATM Offsite")]
        ATMOffsite,
        Cash
    }

    public enum CashCaseDifferenceType
    {
        Surplus,
        Deficiency
    }
0

Na pierwszy rzut oka łamiesz SRP, skoro tych samych struktur danych używasz do repozytoriów oraz jako zewnętrzne kontrakty swojego API. Dziwne jest to, że są w tym samym projekcie, co encje domenowe, wygląda jakby to całe DDD było na siłę. Zwłaszcza, że w DDD repozytoria powinny operować właśnie na encjach domenowych, a nie na DTO. Na moje oko, robisz coś źle.

Co do głównego pytania, to ja mam takie podejście, że moje API to mój produkt, udostępniam do niego jakiś interfejs i decyduję jak on wygląda (staram się, żeby był samo opisujący oraz logiczny). W żadnym stopniu nie interesuje mnie w czym i jak jest napisany klient tego API, bo jak będę się miał dostosowywać do żądań wszystkich możliwych klientów, to nigdy niczego nie napiszę, bo jedna połowa zawsze będzie chciała czegoś przeciwnego niż druga. To klienci mają dostosować się do API, nie odwrotnie.

0

Dzięki za odpowiedź.

Gdzie zabłądziłem?
W projekcie "Domain" mam modele domenowe, dto i klasy reprezentujące zapytania i komendy(CQR).
W projekcie "Infrastruktura" mam repozytoria, które ściągają dane z mikroserwisów i mapuje ich kontrakty na moje DTO.
W projekcie Web mam kontrolery WebAPI:

Zapytania:
Metody WebAPI przyjmują obiekty zapytań, które są w projekcie "Domain" lub tworzą je i wysyłają je do query hanlderów, które są w projekcie "Services" i wywołują repozytoria z "Infrastruktury". Dla zapytań pomijam całkowicie mapowanie DTO na modele domenowe.

Komendy:
Metody WebAPI przyjmują komendy, które są w projekcie "Domain" i wysyłają je do command handlerów, które są w projekcie "Services" a tam pobieram DTO mapuje na Domene, robię jakieś operacje na domenie i mapuje na DTO a na końcu wysyłam do repozytorium.

0

W zasadzie to powinieneś mieć w corze\domenie tylko encje domenowe i interfejsy repozytoriów, a klasy DTO (okrojone do tego co moze widziec API) w infrastrukturze. Tam tez klasy implementujace te interfejsy. W zalozeniach DDD jest nacisk aby API nie mialo mozliwosci bezposredniej komunikacji z domena. A wydaje mi sie ze u Ciebie w projekcie jest referencja nazwijmy ja "using Projekt.Domena" bezposrednio w API.

0

Dzięki za odpowiedzi.

Dlaczego w "Core" powinny być interfejsy repozytoriów? Czy tylko repozytoriów?

Naniosłem zmiany. Teraz wygląda to tak:

  1. Projekt "Core" ma rzeczy, ana których opiera się aplikacja. Czyli modele domenowe i zdarzenia.
    Oraz metody rozszerzeń dla Enum, string.

  2. Projekt "Infrastruktura" ma repozytoria, które zwracają modele domenowe(póki co mam tylko GETy).
    W tym projekcie też mam puste klasy zapytań, które wchodzą do metod GET repozytoriów.
    Tutaj też mam mapowanie z DTO mikro-serwisu na modele domenowe.

  3. Projekt "Services" rozszerza komendy z "Infrastruktury" o bibliotekę "MediatR". M.in. dodaje typy zwracane - DTO.
    W tym projekcie też mam klasy DTO oraz mapowanie z modeli domenowych na moje DTO.
    Tutaj też są query handlery, które wołają repozytoria i mapują domenę na dto(1)

  4. Projekt Web zawiera WebAPI, które zwraca DTO zdefiniowane w "Services". Woła tylko query handlery.

(1) Czy dla zapytań pominąć repozytorium i walić bezpośrednio do mikro-serwisu?
Mój flow wygląda tak, że mapuje DTO mikro-serwisu na moją domenę następnie na moje DTO i zwracam. Ponieważ idzie to przez repozytorium, które teraz operuje tylko na modelach domenowych.

0
Błękitny Orzeł napisał(a):
  1. Projekt "Core" ma rzeczy, ana których opiera się aplikacja. Czyli modele domenowe i zdarzenia.
    Oraz metody rozszerzeń dla Enum, string.

Ja bym to rozdzielił, czemu jakieś helpery mieszać z esencją całej aplikacji?

  1. Projekt "Infrastruktura" ma repozytoria, które zwracają modele domenowe(póki co mam tylko GETy).
    W tym projekcie też mam puste klasy zapytań, które wchodzą do metod GET repozytoriów.
    Tutaj też mam mapowanie z DTO mikro-serwisu na modele domenowe.

Czym są "puste klasy zapytań"?

(1) Czy dla zapytań pominąć repozytorium i walić bezpośrednio do mikro-serwisu?

Ale jak bezpośrednio? Myślę, że warto mieć swoją warstwę abstrakcji nad zewnętrznymi źródłami danych (niekoniecznie repozytoria).

Mój flow wygląda tak, że mapuje DTO mikro-serwisu na moją domenę następnie na moje DTO i zwracam. Ponieważ idzie to przez repozytorium, które teraz operuje tylko na modelach domenowych.

A te modele domenowe wykonują jakąś logikę? Bo jeśli nie, to może ich istnienie tutaj jest zbędne?

1 użytkowników online, w tym zalogowanych: 0, gości: 1