Dziękuję wszystkim za komentarze i dyskusję.
Po to się stosuje DTO żeby kompletnie ukryć encję i odseparować je od wpływu świata zewnętrznego. Wprowadzają dziedziczenie rezygnujesz z benefitów które ta sepraracja zapewnia i wprowadzasz tylko dodatkową złożoność. Równie dobrze możesz kompletnie zrezygnować z DTO i pchać encje na zewnątrz, i wyjdzie na to samo przy mniejszej złożoności.
W jakim kontekście piszesz o złożoności? Czy chodzi Ci o to, że dziedziczenie coś "kosztuje" czy że projekt jest bardziej złożony?
To jest słabe rozwiązanie.
Co jak będziesz chciał zmienić typ pola z int na double w widoku, ale nie w bazie?
To wtedy usuwam tą zmienną z base i w DAO dodaję zmienną int a w DTO dodaję double. Ale szczerze to nie kojarzę potrzeby zmiany double na int - szczególnie w obiektach typu DTO.
Nie mam dużego doświadczenia w pisaniu API etc. ale wydaje mi się, że ogólną koncepcję rozdzielenia DTO i DAO jako tako rozumiem. Natomiast nie jestem pewny czy dobrze zobrazowałem cel mojego działania i korzyści.
Nie ważne jakie rozwiązanie przyjmę to i tak muszę stworzyć jakiś mechanizm, który zmieni mi DTO -> DAO oraz DAO -> DTO.
Poza drobnymi wyjątkami praktycznie zawsze oba obiekty są prawie identyczne (różnice są niewielki i głównie sprowadzają się do wyboru właściwości DAO, które mają być umieszczone w DTO).
Do mapowania tych pól są różnego rodzaju narzędzia typu automappery etc., ale pytanie jest po co kopiować jeden typ do drugiego, jeśli można zrobić tak jak pokazałem poniżej (przykład z podaną książką, którą zasugerował @Riddle):
Kod może nie jest idealny, ale chodzi o pokazanie idei. Nie ma tu automappera, martwimy się tylko o różnice między DTO a DAO - cała reszta się sama kopiuje.
W dalszym ciągu mam dwie klasy (DTO i DAO), ale część wspólna należy do obu klas. Bo jaki jest sens, aby to rozdzielać?
Dodatkowo każdą z tych klas mogę mieć w oddzielnym folderze / projekcie / bibliotece etc.
Gdy klasy są małe lub różnic między DTO a DAO jest dużo to korzyści są mniejsze, ale dla dużych klas, gdzie część wspólna jest duża to takie rozwiązanie wydaje mi się całkiem spoko - przynajmniej moim zdaniem.
BookDAO bookDAO = new BookDAO();
bookDAO.Author = "Mickiewicz";
bookDAO.Title = "Dziady";
bookDAO.Price = 12345; //int
bookDAO.IsBook = true; //jakaś inna zmienna
//DAO -> DTO
BookDTO bookDTO = bookDAO.GetDTO(); //tworzenie nowego obiektu i kopiowanie części wspólnej
bookDTO.ReleaseDate = DateOnly.FromDateTime(DateTime.Now); //jakaś zmienna
//DTO -> DAO
BookDAO bookDAO2 = bookDTO.GetDAO(); //tworzenie nowego obiektu i kopiowanie części wspólnej
bookDAO2.Price = 321;
bookDAO2.IsBook = false;
public record BookBase
{
//część wspólna dla DAO i DTO
public string Author { get; set; }
public string Title { get; set; }
}
public record BookDAO : BookBase
{
public BookDAO() { }
public BookDAO(BookBase book_base) : base(book_base) { }
//właściwości typowe dla DAO
public int Price { get; set; }
public bool IsBook { get; set; }
public BookDTO GetDTO()
{
var tmp = new BookDTO(this);
tmp.Price = Price / 100.00;
return tmp;
}
}
public record BookDTO : BookBase
{
public BookDTO() { }
public BookDTO(BookBase book_base) : base(book_base) { }
//właściwości typowe dla DTO:
public double Price { get; set; }
public DateOnly ReleaseDate { get; set; }
public BookDAO GetDAO()
{
var tmp = new BookDAO(this);
tmp.Price = (int)(Price * 100.00);
return tmp;
}
}