第07章:モジュール内レイヤー:Domain / Application / Infrastructure 🧅✨
今日のゴール🎯
- 「このコード、どこに置くべき?」が迷わなくなる😊
- **Domain(ルール)とApplication(ユースケース)とInfrastructure(外部との接続)**を、ふわっとじゃなく“手で分けられる”ようになる✂️✨
- そして最後に、ミニECの Ordering モジュールで「置き場所の正解」を体験するよ🛒💕
まず超ざっくりイメージ🧠💡

モジュールの中って、だいたいこの3層に分けると事故が減るの✨
- Domain(ど真ん中):ビジネスのルール❤️(純度高め、外部に依存しない)
- Application(司会進行):ユースケースを回す🎬(注文する・支払う等)
- Infrastructure(現実世界担当):DB・API・ファイル・メールなど🌍(泥臭いの全部ここ)
この分け方は、いわゆるクリーンアーキテクチャ系の考えと相性がよくて、内側は外側を知らないが基本だよ😊 (Microsoft Learn)
3層それぞれ「何を置く?」早見表📦✨
1) Domain(ルールの中心)❤️
ここに置くもの👇
- Entity / Value Object(例:
Order,OrderId,Money) - 集約(Aggregate)関連(※第15章で超やるけど、今は雰囲気でOK😊)
- ドメインルール(例:「注文明細が0件なら注文できない🙅♀️」)
- ドメインサービス(※“ルール”だけで完結する計算や判定)
置いちゃダメなもの🙅♀️
DbContext/ EF Core属性 / SQLHttpClient/ API呼び出し- 画面・Web・Consoleの事情
Domainは「物語のルールブック」📕✨ ルールブックに「DB接続手順」書き始めると地獄になるよ😇💥
2) Application(ユースケースを回す)🎬✨
ここに置くもの👇
- ユースケース(例:
PlaceOrder、CancelOrder) - Command/Query、Handler(CQSは第16章で気持ちよくなるやつ✍️👀)
- DTO(モジュール外へ渡す形、入力/出力の形)
- ポート(インターフェース):
IOrderRepository、IPaymentGatewayみたいな「こういう機能がほしい」宣言📣
置いちゃダメなもの🙅♀️
- SQL直書き、EF Coreの具体実装
- 外部APIの認証・リトライの泥臭い処理
Applicationは「台本+進行役」🎭✨ ルール自体はDomain、現実処理はInfrastructureに任せるのがコツ😊
3) Infrastructure(現実世界との接着剤)🔌🌍
ここに置くもの👇
- DBアクセス(EF Core、Dapper、SQL、Migration)
- 外部APIクライアント(決済、配送、メール送信📩)
- ファイル、時計、乱数、環境変数…“現実”の全部🧱
やることの本質👇
- Applicationが定義した interface を実装する(例:
IOrderRepositoryの実装) - 実行時にDIで差し込む(この「差し込み」思想が強い✨) (Microsoft Learn)
ありがち事故😇💥(ダメ例→良い例)
ダメ例:DomainがDBを知ってしまう🪦
「注文保存」をDomainに書いちゃうパターン👇
// ❌ Domain層に置いちゃダメ
public class Order
{
public void Save()
{
using var db = new OrderingDbContext(); // EF Coreに依存😵
db.Orders.Add(this);
db.SaveChanges();
}
}
何がヤバい?😵💫
- DomainがEF Core(外部事情)に縛られる
- テストが重くなる&差し替えできない
- “ルールの中心”が汚れて、設計が崩れていく…🫠
良い例:Applicationに「欲しい機能」を宣言して、Infrastructureが実装する✨
// ✅ Application層:欲しい機能(ポート)を宣言
public interface IOrderRepository
{
Task AddAsync(Order order, CancellationToken ct);
}
// ✅ Infrastructure層:実装(アダプタ)
public sealed class EfOrderRepository : IOrderRepository
{
private readonly OrderingDbContext _db;
public EfOrderRepository(OrderingDbContext db) => _db = db;
public async Task AddAsync(Order order, CancellationToken ct)
{
_db.Orders.Add(order);
await _db.SaveChangesAsync(ct);
}
}
この分け方は、内側が外側の具体実装を知らなくていい構造に近づくよ😊 (Microsoft Learn) (EF Coreをどう使うかは流派があるけど、DbContext直でもRepositoryでも、“置き場所”の考え方が大事!) (Microsoft Learn)
手を動かす(C#)⌨️✨:Orderingモジュールを3層で作るよ🛒
ここでは「注文する」ユースケースを超ミニで作るよ😊 ポイントは “どこに置くか” だけに集中!✨
① Domain:Order(ルールを守る)❤️
namespace Ordering.Domain;
public sealed class Order
{
private readonly List<OrderLine> _lines = new();
public Guid Id { get; } = Guid.NewGuid();
public IReadOnlyList<OrderLine> Lines => _lines;
public void AddLine(string sku, int quantity)
{
if (string.IsNullOrWhiteSpace(sku)) throw new ArgumentException("SKU is required.");
if (quantity <= 0) throw new ArgumentOutOfRangeException(nameof(quantity), "Quantity must be > 0.");
_lines.Add(new OrderLine(sku.Trim(), quantity));
}
public void EnsurePlaceable()
{
if (_lines.Count == 0) throw new InvalidOperationException("Order must have at least one line.");
}
}
public sealed record OrderLine(string Sku, int Quantity);
ここが大事🥰
- 「数量は正の数」みたいなルールはDomainに置く❤️
- Applicationは“進行役”なので、ルール自体はDomainに寄せるのが気持ちいい✨
② Application:PlaceOrder(ユースケースを回す)🎬
namespace Ordering.Application;
using Ordering.Domain;
public sealed record PlaceOrderRequest(IReadOnlyList<PlaceOrderLine> Lines);
public sealed record PlaceOrderLine(string Sku, int Quantity);
public sealed record PlaceOrderResult(Guid OrderId);
public interface IOrderRepository
{
Task AddAsync(Order order, CancellationToken ct);
}
public sealed class PlaceOrderHandler
{
private readonly IOrderRepository _repo;
public PlaceOrderHandler(IOrderRepository repo) => _repo = repo;
public async Task<PlaceOrderResult> HandleAsync(PlaceOrderRequest req, CancellationToken ct)
{
// ユースケースの進行役✨(DomainルールはDomainが持つ)
var order = new Order();
foreach (var line in req.Lines)
{
order.AddLine(line.Sku, line.Quantity);
}
order.EnsurePlaceable();
await _repo.AddAsync(order, ct);
return new PlaceOrderResult(order.Id);
}
}
ここが大事🥳
- Applicationは「注文する」という“流れ”を組み立てる🎬
- DBの話はしない(Repository interfaceで“お願い”するだけ)🙏✨
③ Infrastructure:まずはInMemoryで“置き場所確認”🧠(DBは後でOK)
namespace Ordering.Infrastructure;
using Ordering.Application;
using Ordering.Domain;
public sealed class InMemoryOrderRepository : IOrderRepository
{
private readonly List<Order> _orders = new();
public Task AddAsync(Order order, CancellationToken ct)
{
_orders.Add(order);
return Task.CompletedTask;
}
}
Infrastructureは「現実担当」なので、最初はこういう仮実装でもOK😊 (DBに差し替えるのは第12〜14章で気持ちよくやろう🗃️✨)
④ Host(実行する側)でDIして動かす🚀
using Microsoft.Extensions.DependencyInjection;
using Ordering.Application;
using Ordering.Infrastructure;
var services = new ServiceCollection();
// Applicationが欲しい機能を、Infrastructureが提供する✨
services.AddSingleton<IOrderRepository, InMemoryOrderRepository>();
services.AddTransient<PlaceOrderHandler>();
var sp = services.BuildServiceProvider();
var handler = sp.GetRequiredService<PlaceOrderHandler>();
var result = await handler.HandleAsync(
new PlaceOrderRequest(new[]
{
new PlaceOrderLine("SKU-001", 2),
new PlaceOrderLine("SKU-777", 1),
}),
CancellationToken.None);
Console.WriteLine($"Order placed! 🎉 OrderId={result.OrderId}");
「interfaceは内側、実装は外側、実行時に差し込む」って流れが見えたら勝ち!😊✨ (Microsoft Learn)
ミニ演習📝✨(10〜20分でOK)
演習1:仕分けクイズ📦
次のクラスはどこ?(Domain / Application / Infrastructure)
MoneyPlaceOrderHandlerEfOrderRepositoryOrderShippedEmailSenderOrder
👉 自分の答えを書いたら、理由も1行つけてね😊
演習2:ルール追加❤️
DomainのOrderにルール追加してみて!
-
「同じSKUはまとめて数量加算する」✨
- 例:
SKU-001を2回追加したら、行は1つで数量合計にする
- 例:
演習3:外部サービスっぽいものを追加🔌
Applicationに
INotificationSender(interface)を追加して InfrastructureにConsoleNotificationSender(実装)を作って 注文後に「通知」を飛ばしてみよう📩✨
AI活用プロンプト例🤖✨(Copilot / Codex向け)
そのままコピペでOKだよ😊
- 置き場所相談(最強)
- 「この機能はDomain/Application/Infrastructureのどこに置くべき?理由も添えて。候補ファイル名も提案して」
- Domainルールの実装補助
- 「Orderに“同じSKUは数量加算”ルールを追加したい。副作用が少ない実装例を提案して」
- interface設計
- 「Application層に置くIOrderRepositoryのメソッド設計を、ユースケース(PlaceOrder/CancelOrder)から逆算して提案して」
- Infrastructure差し替え
- 「InMemoryOrderRepositoryをEfOrderRepositoryに差し替えるときの注意点(依存関係・DTO・永続化モデルの分離)を箇条書きで」
まとめ(覚える1行)📌✨
Domainはルール❤️ / Applicationは進行🎬 / Infrastructureは現実🌍 —— “置き場所”を守るとモジュールが強くなる! 💪😊
次の第8章では、この3層をさらに強くするために「参照していい方向(依存関係ルール🚦)」をカチッと決めるよ〜!✨