第62章:DTO(Data Transfer Object)✨ 〜 画面に出すデータと、内部のデータを分ける理由 〜 📦🧠

今日のゴール 🎯
- DTOが「何のためにいるのか」をスッキリ理解する 😌✨
- 「ドメインのオブジェクトをそのまま返しちゃダメな理由」が腹落ちする 🙅♀️
- ASP.NET Coreでよくある形(Request/Response DTO)を作れるようになる 💪😊
1) DTOってなに?🤔📦
DTOはひとことで言うと、
「データの受け渡し専用の、軽い入れ物」 🧺✨
- 画面(APIのレスポンス)に返す用
- 画面(APIのリクエスト)から受け取る用
つまり “運ぶための箱” です📦🚚 ビジネスルール(正しさの判断)を持たせない のが超大事ポイントです❗️
2) なんで分けるの?(超重要)🔥
「ドメインのクラスをそのまま画面に返せば早くない?」って思いがちですが… 分けないと、未来の自分が泣きます😭💥
理由A:画面に合わせると、ドメインが汚れる 😵💫🌀
画面は都合がコロコロ変わります📱🖥️
- 一覧では「名前だけ」
- 詳細では「住所も電話も」
- 管理画面では「内部フラグも」
この“画面都合”をドメインに混ぜると、 **ドメインが「画面の奴隷」**になります🥲⛓️
👉 DTOを使うと、画面の形はDTOで自由に変えてOK ✨ ドメインはビジネスに集中できます🧠💎
理由B:セキュリティ(勝手に更新される)事故を防ぐ 🛡️😱
「更新API」で、受け取ったJSONをそのままエンティティに当てると…
- ユーザーが送ってはいけない項目まで送れてしまう
(例:
IsAdmin=trueとか…😱)
DTOにすると ✅ 受け取っていい項目だけを定義できる → “危険な項目は存在しない” 状態にできます🔒✨
理由C:外に出したくない情報を漏らさない 🙈💦
ドメインのエンティティには内部用の情報が混ざりがちです。
- 内部メモ
- 非公開フラグ
- 個人情報の元データ
- 監査用の値(CreatedBy とか)
DTOなら、公開していい形だけを返せます✨
理由D:シリアライズ地獄(循環参照・巨大レスポンス)を避ける 🌀😇
ドメインやEFのエンティティをそのまま返すと 「関係が辿れて無限に膨らむ」みたいな事故が起きがちです😵💫 DTOは必要な形だけに固定できるので安心です👌✨
3) DTOはどこに置く?🏠📁
ざっくりおすすめはこの2種類です😊
-
Request DTO(入力):Web層(API)側に置くことが多い
-
Response DTO(出力):Application層に置くことが多い
- 「このユースケースはこの形で返す!」が明確になるから✨
※プロジェクトの流儀で変えてOKです🙆♀️ 大事なのは ドメイン層にDTOを持ち込まない ことが多いです(混ざりやすいから)⚠️
4) 例で理解しよ!「ユーザー登録」🧑💻✨
ドメイン側(ルールの世界)🧠
Emailは値オブジェクトで「正しい形式じゃないと生まれない」Userは「ビジネス的に意味のある存在」
// Domain
public sealed record Email
{
public string Value { get; }
private Email(string value) => Value = value;
public static Email Create(string value)
{
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Email is required.");
if (!value.Contains("@")) throw new ArgumentException("Email format is invalid.");
return new Email(value.Trim());
}
public override string ToString() => Value;
}
public sealed class User
{
public Guid Id { get; }
public string DisplayName { get; private set; }
public Email Email { get; private set; }
public User(Guid id, string displayName, Email email)
{
Id = id;
DisplayName = displayName;
Email = email;
}
}
Web側(入力の箱:Request DTO)📩
「APIで受け取る形」を固定します📦✨ ここには **バリデーション(必須チェック)**くらいは置いてもOKなことが多いです(でもビジネスルールは置かない)🙂
// Web (Request DTO)
public sealed record RegisterUserRequest(
string DisplayName,
string Email
);
Application側(出力の箱:Response DTO)📤
画面に返す形を“明確に”します✨
ドメインの Email みたいな型を外に出さず、文字列にして返すのが定番です😊
// Application (Response DTO)
public sealed record UserDto(
Guid Id,
string DisplayName,
string Email
);
5) DTOへのマッピング(変換)🔁✨
DTOは箱なので、中身(ドメイン)との変換が必要です📦➡️🧠
まずは手動が最強(読みやすい&事故りにくい)💪😊
AIにも作らせやすいです🤝✨
// Application (Mapping)
public static class UserMapping
{
public static UserDto ToDto(this User user) =>
new(user.Id, user.DisplayName, user.Email.Value);
}
6) “DTOを使う流れ”のイメージ図 🌊✨
- 画面 → API → Application → Domain
- Domain → Application → API → 画面
🧡ポイント:画面はDTOだけ見てればOK ドメインの都合に引きずられません😊
7) よくある事故パターン 😭⚠️(ここ超大事)
❌事故1:ドメインのエンティティをそのまま返す
- 内部情報漏れる🙈
- 画面都合でドメインが歪む😵💫
- シリアライズ地獄🌀
❌事故2:DTOにビジネスルールを書き始める
DTOは「箱」📦 ルールはドメイン🧠 これが混ざるとカオスになります😇
❌事故3:1個のDTOで全部やろうとする
- 更新用
- 参照用
- 一覧用
- 詳細用
これ、全部欲しい情報が違います😅 DTOは 用途ごとに分けてOK 🙆♀️✨
8) AI(Copilot/Codex)で爆速にするコツ ⚡️🤖
DTOとマッピングは「定型作業」になりやすいのでAIが得意です🎉
便利プロンプト例(そのまま貼ってOK)📝✨
- 「
UserエンティティからUserDtoを作って。外部公開はId, DisplayName, Email(string)だけ」 - 「
RegisterUserRequestを受けてEmail値オブジェクトを作る変換コードを書いて。例外が出る想定で」 - 「DTOとドメインの変換を Extension Method にして、null安全も考えて」
🧠コツ:“公開していい項目”を必ず列挙して指示すると事故りにくいです🔒✨
9) ミニ演習(サクッと)✍️🌸
演習A:一覧画面用DTOを作ろう 📃✨
お題:注文(Order)の一覧画面
- 一覧に必要:
OrderId,OrderedAt,TotalPrice,Status
✅ やること
OrderListItemDtoを作るOrderからOrderListItemDtoへのToDto()を作る- 「詳細用DTO」は別にする(一覧と同じにしない)😆
演習B:更新用DTOを作ろう ✏️🔧
お題:表示名の変更
- 入力DTO:
ChangeDisplayNameRequest(string DisplayName) - 受け取った値で、ドメインのメソッド
ChangeDisplayName()を呼ぶ(ここがドメインの仕事🧠✨)
10) まとめ 🌈✨
- DTOは データ受け渡し専用の箱 📦
- 画面の都合をドメインに入れない のが最大の価値🧠💎
- セキュリティ・漏洩・シリアライズ事故も防げる🛡️✨
- DTOとマッピングはAIで爆速化しやすい⚡️🤖
次の章(AutoMapper vs 手動マッピング)につながる下準備としては完璧です😊✨ もし「DTO設計の命名ルール(Request/Response/Command/Query)」もセットでテンプレ化したいなら、その形でサンプル雛形も作れますよ📁💖