C# DTO 是什麼?

DTO(Data Transfer Objects) 中文叫做「資料傳輸物件」,就是單純用來「傳輸」用的,例如在不同的系統層之間,或是與外部系統溝通使用的。

為什麼要使用 DTO?

假設使用 Entity Framework Core 存取資料庫,在資料庫對應模型(Model)中我們很常會看到類似下面的定義:
    
[Table("Users")]
public partial class User
{
    [Key]
    public  string Id { get; set; } = null!;

    [StringLength(256)]
    public string UserName { get; set; } = null!;
    
    [InverseProperty("User")]
    public virtual ICollection<UserClaim> UserClaims { get; set; } = new List<UserClaim>();
}
    

上面的內容是指 Users 和 UserClaims 資料表關聯,如果直接把 User 這個 Model 當作 API 的回應物件,其他人就可以知道我們系統的架構,並且還會因為包含了多餘的內容,浪費系統資源。並且假設今天 API 有要多傳幾個欄位,那我們又會修改到和這件事情本身完全無關的 Model,本來資料庫不需要多幾個欄位,但是因為 API 需要,所以又開了欄位,但其實單獨建立一個 API 用的 class,也就是 DTO 就可以解決問題。

並且還不會讓各服務都過於依賴單個的物件,讓一個物件只負責一件事情,達成解耦合和單一職責。

DTO 要保持簡單

DTO 應該只是用來傳輸資料的,例如傳輸使用者個人資訊的 UserProfileDto:
    
public class UserProfileDto
{
    public int Id { get; set; }

    public string Username { get; set; }

    public string Email { get; set; }

    public string FullName { get; set; }

    public string PhoneNumber { get; set; }
}
    

我們可以利用 getter, setter 或是 Attribute 加上一些驗證:
    
public class UserProfileDto
{
    public int Id { get; set; }

    [Required]
    [MaxLength(10)]
    public string Username { get; set; }

    public string Email { get; set; }

    public string FullName { get; set; }

    public string PhoneNumber { get; set; }
}
    

但是不會包含邏輯或是行為,也不需要被封裝,不會有私有(private)或受保護(protected)的屬性,他應該只是單純的用來「傳輸資料」,所以結構應該盡可能的簡單。

DTO 命名

DTO 會專注於單一功能,而這個功能應該反映在名稱上,例如接收使用者登入請求的 DTO: UserLoginRequest
    
public class UserLoginRequest
{
    public string UserName { get; set; }

    public string Password { get; set; }
}
    

我們很常會在命名結尾加入 DTO 或是使用符合 C# 大駝峰命名方式的 Dto,不過這也不是必須的,甚至有些人推崇不要加上 DTO 字眼。在使用功能分組時,通常會建立一個 DTO 資料夾(或是 Dto),在裡面放入本功能的所有相關 DTO ,而如果這些 DTO 的命名足夠清楚,那就不需要在 DTO 的名稱後面加上 DTO 了。

留言

張貼留言

如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com