Do you use unique DTOs per use case?


In Clean Architecture, it is normally better to have a unique Data Transfer Object (DTO) for each endpoint or use case.

While sharing DTOs across multiple endpoints might seem like a way to save some code, it often leads to several problems:

  • Unnecessary Data Transfer: Endpoints sending more data than what is actually needed, this can lead to performance issues.
  • Increased Coupling: When multiple endpoints share the same DTO, a change required by one endpoint can inadvertently affect others.
  • Developer Confusion: Developers will find it hard to understand which properties are relevant for a specific endpoint, leading to potential misuse or misunderstanding of the data structure.

By creating unique DTOs tailored to each endpoint's specific requirements, you ensure that:

  • Endpoints only deal with the data they need.
  • Performance is optimized by avoiding the transfer of superfluous data.
  • Endpoints are decoupled, making them easier to develop, test, and maintain independently.
namespace Northwind.Trading.Application.Contracts.Models;

public class OrderItemModel
{
    public int OrderId { get; set; } 
    public string CustomerId { get; set; }
    public DateTime OrderDate { get; set; }
    public decimal TotalAmount { get; set; }
    public OrderStatus Status { get; set; }

    /// <summary>
    /// Used for GetOrderListEndpoint. Ignore when updating
    /// </summary>
    public string? ShipFromCountry { get; set; }

    /// <summary>
    /// Used only for GetOrdersEndpoint. 
    /// </summary>
    public DateTimeOffset ModifiedDateUtc { get; set; }

    /// <summary>
    /// Detailed list of order items. Only for GetOrderDetails.
    /// Not used for GetOrderList
    /// </summary>
    public List<OrderItemViewModel> OrderItems { get; set; } = [];
}

Figure: Bad example - OrderViewModel is used for multiple purposes (e.g., GetOrderList, GetOrderDetails, CreateOrder) and has accumulated many properties, making it hard to read and maintain.

namespace Northwind.Trading.Application.Contracts.OrderQueries.Models;

public class GetOrderListItemDto
{
    public int OrderId { get; set; }
    public string CustomerId { get; set; }
    public DateTime OrderDate { get; set; }
    public decimal TotalAmount { get; set; }
    public OrderStatus Status { get; set; }
}

Figure: Good example - A simple OrderSummaryDto designed specifically for an endpoint that lists orders.


We open source.Loving SSW Rules? Star us on GitHub. Star
Stand by... we're migrating this site to TinaCMS