社内レビューで、こんなコードに出会うことがあります。
customer.Id.NormalizeCustomerId();
一見問題なさそうですが、実はこれ string が 「顧客IDを正しくする」責務を負っている という状態です。
業務の主体は Customer なのに、振る舞いがプリミティブ側へ流れてしまっている。
これは現場でよく起きる “小さなアンチパターン” で、積み重なるとコードの意味がどんどん読めなくなります。
この記事では、
「どの型に振る舞いを持たせるべきか」
をテーマに、Extension の設計方針を整理していきます。
結論:主体は「意味のある型」に寄せるべき
-
stringやintはただの記号の入れ物 - 振る舞いを載せるべきは Customer / Order / Amount など“業務として意味を持つ型”
- DB の型に引きずられてはいけない
この視点を外すと、
“誰が何をするのか” がコードから消えます。
プリミティブ型に振る舞いを寄せると何が起きるか
以下のような “本来ドメイン側にあるべき処理” を string に載せ始めると危険です。
- 顧客IDの整形
- 顧客コードの検証
- 注文番号の生成
どんどん “記号を入れるための箱” に処理が集まり、次のような状態に。
-
stringにドメインの知識が混入 - 処理を探す場所が分からなくなる
- 「これは誰の仕事か?」が曖昧になる
これが、静かに設計を壊していきます。
実例での比較
❌ 悪い例:string が顧客IDを整えてしまう
public static string NormalizeCustomerId(this string rawId)
{
return rawId.Trim().ToUpper();
}
// 呼び出し側
var id = customer.Id.NormalizeCustomerId();
読むと 「string が顧客IDを整える」 という不自然な文脈に。
✅ 良い例:Customer が自分のIDを整える
public static Customer NormalizeId(this Customer customer)
{
customer.Id = customer.Id.Trim().ToUpper();
return customer;
}
// 呼び出し側
customer.NormalizeId();
自然に読めます。
「Customer が、自分の ID を整える」
コードと業務の構造が一致しています。
なぜ主体を Customer に寄せるのが正しいのか?
以下の前提構造があるためです。
- DB の都合で
stringが前に出てくる - でも“ID の意味”を持つのは
Customer - 「ID を整える責務」も Customer 側
この一致を保つことで、
- DB 型に設計を引きずられない
- 読み手が迷わない
- コードを読むだけで業務構造が見える
という強いメリットが得られます。
Extension の設計方針まとめ
Extension は「本来の主体」に寄せる。
プリミティブ型には安易に生やさない。
とても小さな意識の違いですが、これを徹底するだけで
- 責務の置き場が整う
- コードの意味づけが揃う
- 設計のぶれが減る
といった効果が出ます。
さいごに
今回の話は Extension の書き方というテクニックではなく、
「誰に責務を持たせるべきか」という設計の基礎の話です。
プリミティブ型に振る舞いを寄せ続けると、
コードから“意味を持つ主体”が消えていきます。
逆に、Customer や Order に責務を戻していくと、
ソースコードが自然と業務の構造を語り始めます。
現場の実装で Extension を使うとき、
ぜひ「その処理は本当にその型の仕事?」と一度立ち止まってみてください。