Do you use unique DTOs per use case?
Updated by Brady Stroud [SSW] 2 months ago. See history
123
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: 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: Figure: Good example - A simple `OrderSummaryDto` designed specifically for an endpoint that lists orders.
acknowledgements
related rules