Should a DTO be generated by a domain entity or persistence?

When it comes to multi-tier applications with modern ORMs, I often don’t know how to create specific classes in order to adhere to the so-called “best practices”, and also pay attention to performance requirements.

Please note that an application can have any number of the following types of objects:

  • Domain objects are rich classes that contain business logic (right?) And, depending on the capabilities of ORM, can be directly associated with a persistence construct.

  • DTOs are simpler classes that share business logic to pass data to internal and external clients. Sometimes they flatten, but not always.

  • View models - they are similar to DTO because they are simpler and devoid of business logic, but they are usually quite flat and often contain additional bits related to the user interface that they “serve”.

The problem is that in some cases, matching objects in a domain or any retention-oriented class with a simpler object, such as a DTO or ViewModel, does not allow you to make important performance optimizations.

For example:

Let's say I have some domain objects that look like this:

public class Event
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime EventDate { get; set; }

    // These would be reference types in most ORMs
    // Pretend in the setter I have logic to ensure the headliner =/= the opener
    public Band Headliner { get; set; }
    public Band Opener { get; set; }
}

public class Band
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Genre Genre { get; set; }
}

In the real world, this can be much more complicated, with different business logic, possibly with some validation challenges, etc.

If I open an open API, my DTO can be very similar to this example, without any business logic.

, , - MVC, , , :

public class EventViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime EventDate { get; set; }

    public int HeadlinerId { get; set; }
    public string HeadlinerName { get; set; }
    public int OpenerId { get; set; }
    public string OpenerName { get; set; }
}

, .

, , . ORM, , ( , , ). , .

?

, , , , , , .

Event -ish, , , DTO , ?

, ? , , ? DTO - ?

+4
8

, , . .

, . , , . :

  • . DTO , , , .
  • . DTO "" DTO "" .
  • DTO, , . API. DDD, DDD , .
  • . , . .
  • ORM . , , DTO.

  • .
  • .
  • . , DTO - , , .

, . , "-" - , . , , .

(, , ) . , .

+5

ORM, , - , DDD. DTO - , . , , .

+1

Event-ish, ,...

, . EventViewModel Event. Event - Event, EventViewModel , () Event. - EventViewModelProjection Event ( ) , EventViewModels.

DTOS ,...

, DTO , - .

, ... , ... DTO - lite?

, - . , , , .

cqrs eventual-consistency, .

0

DTO , .

ViewModels , DTO. DTO , - .

ORM . , , - , , , . ORM . .

, DTO , - . - value, , , . , , , .. .

. , DTO . .

0

, .

, , . ORM , ( , , ). .

:

  • (, 100 )

  • (, 100 )

  • ViewModels ( ViewModel , , , ViewModel , )

, , Object DTO Entity /.

0

(DDD) , , , . ( ). SOLID, :

S - ()
O (OCP)
L (LSP)
I (ISP)
D (DIP)

0

, , , DDD: .

, , - , , , . , , , , , Command-Query (CQRS).

( , ), EventViewModel. , , , (.. ).

-1

/. , . Escrow . Entity , .

public class Escrow
{
public Guid AccountId {get; set;}
public decimal GetBalance()
}

Domain DTO, , . , ..

DTO ORM

. DTO, ORM , - , POCOs. ORM DTO .

, ORM , DTO, . ORM. , ORM , POCO , .

For example, if I need a computed value that I want to transfer to a user interface level that is not available in ORM entities (since they are not saved), I create new POCOs with only required fields.

I use AutoMapper to copy data between objects

-1
source

Source: https://habr.com/ru/post/1622009/


All Articles