第09章:事故例③「万能Userクラス」爆誕💣
0. 今日のゴール🎯✨
この章では、ありがちな事故パターン👇を“体験”して、境界(Bounded Context)が無いと何が起きるかを腹落ちさせます😊🧠💥
- 「Userって便利だし、全部まとめちゃお♪」➡️ 最初は速い🚀
- でも…機能が増えるほど Userが何者なのか分からなくなる😵💫
- 結果:変更が怖い/影響範囲が読めない/ifだらけ/テスト地獄🧪🔥
1. 事故のはじまり:「とりあえずUserで」🧨
ミニECの世界で、こんな要望が順番に来るとします🛒📦
- ログインできる(会員)🔐
- 配送先が持てる(お届け先)🏠📮
- 請求情報が持てる(請求先)🧾💳
- 管理画面の権限が必要(運営スタッフ)🧑💻🔧
- サポート履歴も見たい(問い合わせ)☎️💬
ここでありがちなのが…👇
「全部“ユーザー”でしょ?Userクラスに追加しよっか☺️」
この瞬間、“万能User”への道が開通します🚧💣
2. 万能Userクラス、爆誕💥👤

最初はスッキリだったはずなのに、だんだんこうなります👇😇
public class User
{
// ログイン系(アカウントっぽい)
public Guid Id { get; init; }
public string Email { get; set; } = "";
public string PasswordHash { get; set; } = "";
public bool IsDisabled { get; set; }
// 会員プロフィールっぽい
public string DisplayName { get; set; } = "";
public DateOnly? BirthDate { get; set; }
public int LoyaltyPoints { get; set; }
// 配送っぽい
public string? DefaultShippingZip { get; set; }
public string? DefaultShippingAddress1 { get; set; }
public string? DefaultShippingAddress2 { get; set; }
// 請求っぽい
public string? InvoiceName { get; set; }
public string? InvoiceAddress { get; set; }
public string? TaxId { get; set; }
// 管理者っぽい
public bool IsAdmin { get; set; }
public string? PermissionsJson { get; set; }
// サポートっぽい
public string? SupportNotes { get; set; }
}
見てほしいポイントはこれ👇🥺
- 「っぽい」項目が同居してる(アカウント/会員/配送/請求/管理/サポート)🧩🧩🧩
- nullだらけ(使う場面が限定的=文脈が違うサイン)⚠️
- 追加のたびに 既存機能に影響しそうで怖い😱
3. “if増殖”が始まる😵💫🌱
万能Userはだいたい「条件分岐の巣」になります🕸️
public static bool CanUseCoupon(User user)
{
if (user.IsDisabled) return false;
// 管理者はOK…?(え、注文の話なのに?)
if (user.IsAdmin) return true;
// 会員ランク…?ポイント…?誕生日…?
if (user.LoyaltyPoints >= 1000) return true;
// そもそも配送先が無いとダメ…?
if (string.IsNullOrWhiteSpace(user.DefaultShippingAddress1)) return false;
return true;
}
ここが地味に怖いところ👇😇
- 「注文のルール」なのに「管理者フラグ」が混ざる(責任が混線)🧯🔥
- 変更要望が来るたびに、別のifに刺さる🪡
- 「どこを直せばいい?」が読めなくなる📉😵
4. 事故の正体:Userの“意味”が1つじゃない🌀
同じ「User」という単語でも、実はこんな別物が混ざってます👇✨
- 🔐 ログインする人(アカウント)
- 🛒 買う人(購入者)
- 🏠 受け取る人(受取人)
- 🧾 請求される人/会社(請求先)
- 🧑💻 運営する人(スタッフ)
- ☎️ 問い合わせる人(問い合わせ主)
つまり、事故はこう言い換えられます👇💥
“User”という1語に、別々の世界(文脈)を押し込んでる😵💫
これが 境界(Bounded Context)が無いサインです🚨
5. 「境界が無いサイン」チェックリスト✅🚨
万能Userの匂い、これで嗅ぎ分けられます👃🔍✨
- ✅ プロパティが増え続ける(しかも用途がバラバラ)📈
- ✅
IsAdmin/IsVendor/IsSomething...が増殖する🏷️ - ✅ nullが多い(画面・機能ごとに必要項目が違う)🕳️
- ✅
UserTypeenumが太る(種類が増えるたび修正)🍔 - ✅ 「この変更、どこに影響する?」が即答できない😨
- ✅ テストが書きづらい(前提条件が多すぎる)🧪💦
6. どう直す?:まず“呼び名”を分ける🏷️✨
まだBCを本格的に切る前でも、言葉を分けるだけで世界が見えます👀✨
例👇(同じ人でも、文脈で呼び名を変える)
- 🔐
Account(ログイン文脈) - 🛒
Buyer(購入文脈) - 🏠
Recipient(配送文脈) - 🧾
BillTo(請求文脈) - 🧑💻
Staff(運営文脈)
「User」って言葉を封印すると、混線が減ります🔒✨
7. “境界の雰囲気”だけ先に味わう:分けるとこうなる🍱✨
万能Userを「文脈ごと」に分けると、こういう形が自然に見えてきます👇
public readonly record struct AccountId(Guid Value);
public readonly record struct BuyerId(Guid Value);
public class Account // 🔐ログインの世界
{
public AccountId Id { get; init; }
public string Email { get; private set; } = "";
public string PasswordHash { get; private set; } = "";
public bool IsDisabled { get; private set; }
}
public class BuyerProfile // 🛒注文の世界
{
public BuyerId Id { get; init; }
public string DisplayName { get; private set; } = "";
public int LoyaltyPoints { get; private set; }
}
ここで大事なのは👇😊
- 同じ人物でも、モデルは別でOK🙆♀️
- “境界を越える”ときは IDやDTOで受け渡す📨
- 「便利だから共有しよ」が事故の入口⚠️
C# 14 は .NET 10 上で使える最新のC#リリースとして整理されています(Visual Studio 2026 / .NET 10 SDK で試せるよ、という公式案内あり)(Microsoft Learn) .NET 10 は 2025-11-11 にリリースされ、サポート表も公式に掲載されています(Microsoft)
8. つまずきポイント集🌀(初心者あるある)
つまずき①:「DRYだから共有しなきゃ!」と思う🧻
- DRYは大事だけど、**“意味が違うのに同じ型で共有”**は逆効果😵💫
- 境界が違うなら、似てても分けるのが安全🛡️
つまずき②:「Userが中心だから、全部Userで!」と思う👑
- “中心”に見えるのは罠🥺
- 実際は 機能ごとに“中心”が違う(注文の中心は注文、請求の中心は請求)🧭✨
つまずき③:「Roleで全部解決できそう」と思う🎭
- Roleは便利だけど、モデルの意味の混線まで解決しないことが多いよ⚡
- Roleで押し切ると、if増殖が再発しがち😇
9. ミニ演習🧩✍️(10〜15分)
お題:この万能Userを“文脈”で仕分けしよう🎒
次の項目を、グループに分けてください👇(紙でもメモでもOK📝✨)
- Email / PasswordHash / IsDisabled
- DisplayName / LoyaltyPoints
- DefaultShippingAddress1 / DefaultShippingZip
- InvoiceName / TaxId
- IsAdmin / PermissionsJson
- SupportNotes
ステップ🎯
- グループ名を付ける(例:ログイン、注文、配送、請求、運営、サポート)🏷️
- 各グループで「この世界の主語は誰?」を1行で書く👀
- 「同じ単語だけど意味が違う」候補を丸で囲む⭕(例:“住所”)
答え合わせの観点✅
- グループ間で、ルール(制約)の種類が違ってたら境界候補🌈
- nullが多い項目は「別世界の可能性」高い⚠️
10. お助けAIプロンプト🤖✨(Copilot/AI拡張でそのまま使える)
※ Visual Studio では GitHubアカウントで Copilot を有効化でき、無料で一部機能が使える案内もあります(Visual Studio)(運用はチーム方針に合わせてね😊)
- 「この
Userクラスのプロパティを“責任”でグルーピングして、グループ名も付けて」🧺 - 「
nullが多いプロパティを列挙して、“なぜ混ざっているサインか”を説明して」🕳️ - 「“注文の文脈”だけで必要な最小フィールドを提案して。不要なものは理由付きで除外して」🥗
- 「分割後に、境界を越えるDTO案(最小)を作って」📨
まとめ🌸
万能Userクラスの事故は、「クラスが巨大」なのが問題というより… **“別々の文脈(世界)を1つに押し込んだ”**のが本体です💥🗺️
次の章では、なぜこれが「変更が怖い」に直結するのか、もっとやさしく解剖していきます😵💫➡️😊✨