Skip to content

Data Transfer Objects

  • Do define DTOs in the application contracts package.
  • Do inherit from the pre-built base DTO classes where possible and necessary (like EntityDto<TKey>, CreationAuditedEntityDto<TKey>, AuditedEntityDto<TKey>, FullAuditedEntityDto<TKey> and so on).
    • Do inherit from the extensible DTO classes for the aggregate roots (like ExtensibleAuditedEntityDto<TKey>), because aggregate roots are extensible objects and extra properties are mapped to DTOs in this way.
  • Do define DTO members with public getter and setter.
  • Do use data annotations for validation on the properties of DTOs those are inputs of the service.
  • Do not add any logic into DTOs except implementing IValidatableObject when necessary.
  • Do mark all DTOs as [Serializable] since they are already serializable and developers may want to binary serialize them.

List Results

  • Do use the Input postfix for the request DTO class name (ex: GetUsersInput).
  • Do use ListResultDto as response DTO which makes your List<...Dto>(ex: ListResultDto<UserDto>) wrapped into another object as an Items property.

Request example;

[Serializable]
public class GetUsersInput : PagedAndSortedResultRequestDto
{
    public string FilterText { get; set; }

    public string Name { get; set; }
}

Response example;

We don't need to define a new class in the application contracts layer. We can use it directly in applications services.

UserRepository service must implement your repository interface which includes GetListAsync. If the best practices efcore integration in the document are followed, these methods should already be in place.

public virtual async Task<ListResultDto<OdmsUserDto>> GetUserLookupAsync()
{ 
    # Get entities from the repository
    var users = await UserRepository.GetListAsync();

    return new ListResultDto<OdmsUserDto>(
        ObjectMapper.Map<List<OdmsUser>, List<OdmsUserDto>>(users)
    );
}

Paged & Sorted List Results

  • Do use the Input postfix for the request DTO class name (ex: GetUsersInput).
  • Do inherit from PagedAndSortedResultRequestDto base DTO class for your request DTO class.
  • Do use PagedResultDto as response DTO which makes your PagedResultDto<...Dto>(ex: PagedResultDto<UserDto>) wrapped into another object as an Items and adds another Total count property.

Request example;

[Serializable]
public class GetUsersInput : PagedAndSortedResultRequestDto
{
    public string FilterText { get; set; }

    public string Name { get; set; }
}

Response example;

We don't need to define a new class in the application contracts layer. We can use it directly in applications services

UserRepository service must implement your repository interface which includes GetCountAsynce and GetListAsync. If the best practices efcore integration in the document are followed, these methods should already be in place.

public virtual async Task<PagedResultDto<OdmsUserDto>> GetListAsync(GetUsersInput input)
{
    var totalCount = await UserRepository.GetCountAsync(
        filterText: input.FilterText,
        name: input.Name,
    );

    var users = await UserRepository.GetListAsync(
        filterText: input.FilterText,
        name: input.Name,
        sorting: input.Sorting,
        maxResultCount: input.MaxResultCount,
        skipCount: input.SkipCount
    );

    List<OdmsUserDto> userDtos = 
        ObjectMapper.Map<List<OdmsUser>, List<OdmsUserDto>>(users);

    return new PagedResultDto<OdmsUserDto>
    {
        TotalCount = totalCount,
        Items = userDtos
    };
}