第11章:Sharedの扱い:共有は“最小”が正解⚠️🍱
第11章 Sharedの扱い:共有は“最小”が正解⚠️🍱
今日のゴール🎯
- Sharedを「便利箱」にしないルールを作れるようになる🙅♀️🧰
- Sharedに入れてOK/NGを判断できるようになる✅❌
- “Shared地獄”を回避しつつ、ちゃんと再利用もできる形にする✨
(いまの最新環境だと .NET 10 / C# 14 / Visual Studio 2026 が軸だよ🪟🛠️)(マイクロソフトサポート)
まずダメ例😇 → こうしてSharedが腐る💥
😇ダメ例あるある
Shared/Utilsに なんでも入れる(文字列整形、日付、拡張メソッド、DBヘルパ…)Shared/DTOに 全部のDTOを置く(注文DTO、商品DTO、会員DTO…)Shared/Enumsに ドメインの状態を置く(OrderStatusとか) → こうなると「Sharedを変える=全モジュールが壊れる」になりがち😵💫💣
😎なにが問題?
Sharedは みんなが参照する中心になりやすいから、肥大化すると「結合の塊」になるの。 DDDでは、大きいモデルを Bounded Context に分割して、相互関係を“明示”するのが超重要って話になってるよ📌 (DevIQ)
1) Sharedに入れていいもの✅ / ダメなもの🙅♀️

✅入れていい(だいたいコレだけで十分🍱)
ポイント:ドメインの匂いがしない、安定してる、依存が薄い
-
超基礎プリミティブ
Result<T>/Error(技術的な成功・失敗表現)Guard(引数チェックの共通)- “型安全ID”の土台(例:
StrongId<T>)
-
横断関心の“薄い”抽象(薄い=業務用語がない)
IClock(時間の取得)みたいなやつ⏰
-
**Sharedに置くなら「小さく・壊れにくく・テストしやすく」**が絶対条件🧪✨
🙅♀️入れちゃダメ(ここが地雷原💣)
-
ドメイン知識そのもの
- 例:
OrderStatus,PaymentMethod,ShippingAddressなど(OrderingのものはOrderingへ!)
- 例:
-
モジュール間連携のDTOをSharedに集約
- 連携は第10章の「公開API(窓口)」でやるのが筋🪟📣(Sharedは逃げ場にしない!)
-
インフラ実装の共通化(DB、HTTP、EFの共通リポジトリ…)
- 依存が増えてSharedが“巨大な土台”になって死ぬ😇
迷ったときの判断フロー🔍✨(これだけ覚えて)
次の質問に「YES」が多いほど Shared に入れない方が安全だよ🛡️
- 名前にドメイン用語が入ってる?(Order, Catalog, User…) → YESなら そのモジュールへ 🧱
- 変更頻度が高い? → YESなら共有しない(共有=調整コスト増)📈
- 参照先(依存)が重い?(EF/HTTP/外部SDK…) → YESならSharedは避ける(薄く保てない)🚫
- 本当に2モジュール以上で“同じ意味”で使う? → 「似てるけど意味が違う」なら分ける(Bounded Contextの考え方🧱🌍)
共有のやり方は3種類🍱✨(おすすめ順)
-
**あえて複製する(Copy)**📄✨
- 小さいコードなら、共有より複製の方が安全なこと多いよ(結合が増えない)
-
モジュールの公開APIで渡す(Published Languageっぽいやつ)🪟📦
- “窓口DTO”は そのモジュールが責任を持つ(Sharedに置かない)
-
Shared Kernel(最終手段)⚠️
- 複数の境界で“共通の核”を共同管理するやつ。範囲は極小にが鉄則(DevIQ)
手を動かす(C#)⌨️✨:Shared.Primitives を“最小”で作る
ここでは Shared を 2つに分ける発想でいくよ(最初は1個でもOKだけど、考え方が大事😊)
Shared.Primitives:薄い土台(依存ほぼ無し)- (必要になったら)
Shared.Abstractions:薄いinterface類
1) Shared.Primitives を追加する🛠️
- ソリューションに Class Library を追加
- 名前:
Shared.Primitives - ターゲット:
.NET 10(いまはC# 14が標準だよ)(マイクロソフトサポート)
2) “成功/失敗”を表す Result を置く✅
(この章では「Sharedに入れてOKな例」として扱うよ。深掘りは第17-18章でガッツリやる😎)
namespace Shared.Primitives;
public readonly record struct Error(string Code, string Message)
{
public static Error None => new("", "");
public bool IsNone => string.IsNullOrEmpty(Code);
}
public readonly record struct Result<T>(T? Value, Error Error)
{
public bool IsSuccess => Error.IsNone;
public static Result<T> Ok(T value) => new(value, Error.None);
public static Result<T> Fail(string code, string message) => new(default, new Error(code, message));
}
3) Guard(引数チェック)を“薄く”置く🧯
namespace Shared.Primitives;
public static class Guard
{
public static string NotNullOrWhiteSpace(string? value, string name)
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException($"{name} is required.", name);
return value;
}
}
4) Ordering側で使ってみる🛒✨(例)
「注文を作る」みたいなユースケースで、Sharedの薄い道具だけ使うよ🙂
using Shared.Primitives;
namespace Ordering.Application;
public static class PlaceOrder
{
public static Result<string> Execute(string? customerId)
{
var id = Guard.NotNullOrWhiteSpace(customerId, nameof(customerId));
// ここでは仮に注文IDを返すだけ
var orderId = $"ORD-{id}-{DateTime.UtcNow:yyyyMMddHHmmss}";
return Result<string>.Ok(orderId);
}
}
✅ここでのコツ
- Sharedは「道具箱(プリミティブ)だけ」
OrderStatusとかOrderとか、ドメイン本体は絶対にSharedへ逃がさない🧱🔒
ミニ演習📝✨(3問)
-
次のうち Shared に入れてよさそうなのはどれ?(理由も一言で)
- A
OrderStatus - B
IClock - C
ProductDto - D
Guard.NotNullOrWhiteSpace
- A
-
Shared/DTOにOrderDtoが山盛りです😇 どう分解する?- ヒント:「窓口DTOはそのモジュールの公開API側に置く」🪟
-
“共有したい衝動”が出たら、判断フローの質問を4つ全部答えてみてね🔍
AI活用プロンプト例🤖✨(コピペOK)
- 「このクラスは Shared に置くべき?置かないべき?理由と移動先候補を3つ出して」
- 「Shared が肥大化している。依存関係を減らす分割案(Shared.Primitives / Shared.Abstractions / 各モジュール)を提案して」
- 「
Shared/DTOを廃止したい。各モジュールの公開API(窓口)にDTOを移す手順と、移行中に壊さない作戦を出して」 - 「‘ドメイン用語がSharedに混入’している箇所を検出するルール案(命名・参照・レビュー観点)を作って」
まとめ(覚える1行)📌✨
Sharedは“みんなの便利箱”じゃなくて、超薄い“道具箱”だけにする⚠️🍱
次の章は、データ境界(Repository)で「DBの都合をドメインに持ち込まない」話に進むよ🗃️✨