第22章 Inbound Port設計①:ユースケースの入口をinterface化🚪📝
(Past chat)(Past chat)(Past chat)(Past chat)(Past chat)(Past chat)(Past chat)
この章は「ユースケース(UseCase)を外側から呼ぶための“入口”を、interfaceとして固定しよう!」がテーマだよ〜😊🔌
ここをちゃんとやると、ControllerやCLIみたいな入口を増やしても、中心(Core)のルールがブレにくくなる✨
※ちなみに今どきのC#は C# 14 / .NET 10 が最新世代だよ🆕✨(Visual Studio 2026に .NET 10 SDK が入ってるよ) (Microsoft Learn) Visual Studio 2022を使ってる場合も、2026年1月時点で 17.14 系が現役サポートだよ〜🧰 (Microsoft Learn)
1) Inbound Portってなに?🤔🔌

Inbound Portは一言でいうと…
「外側(Web/画面/CLI)が、アプリの中心(Application)を呼ぶための“呼び出し口”」 だよ🚪✨
- 外側:Controller / API / 画面 / CLI / バッチ など🌐💻
- 中心:UseCase(アプリがやりたい手順)🧭
- その間の“約束”が Inbound Port(interface)📝
イメージ👇
[Web Controller] ---> (ICreateOrderUseCase) ---> [CreateOrderUseCase]
外側 Adapter Inbound Port 中心 Application
2) なんでわざわざ interface 化するの?😵💫➡️😊
✅ 理由A:入口が増えても中心が守れる🛡️
今日はWeb、明日はCLI、来週はバッチ…って入口が増えても、 「呼び方(契約)」が1つに固定されてると、中心が散らからない✨
✅ 理由B:Controllerが“薄く”なりやすい🌿
Controllerは「受け取って、呼ぶ」だけに寄せやすい😊 (次の章で“薄いController最高✨”をやるよ!)
✅ 理由C:テストがラクになる🧪💖
UseCaseをinterfaceで扱えると、外側のテストでも差し替えやすい! (Fake/Stubを作るときもキレイにハマる🎯)
3) Inbound Portはどこに置く?📦🧠
置き場所は“中心側(Application)” が基本だよ😊 なぜなら「外側に置く」と、中心が外側に引っ張られやすいから⚠️
おすすめ例👇
-
src/CafeOrder.Application/Ports/Inbound/ICreateOrderUseCase.cs- (増えてきたら)
IGetOrdersUseCase.csなど
4) 署名(メソッド)をどう設計する?📝✨
ここがこの章のキモだよ〜!💪😆 方針は 「ユースケース1つにつき、interface1つ + メソッド1つ」 が分かりやすい👍
✅ 4-1) 入力は「Command」、出力は「Result」📦✨
この教材では(前の章の流れ的にも)こうするのがキレイ:
- 入力:
CreateOrderCommand - 出力:
CreateOrderResult
ポイント:WebのDTO(Request/Response)をApplicationに入れない🚫
DTOは次章で作って、Adapter側で ToCommand() 変換するよ🔁✨
✅ 4-2) 非同期は基本 Task に寄せとく⚡
将来DBや外部APIが入ったら、非同期に寄りたくなることが多いよね😊 だから最初からこうしておくと“後で地獄”を避けられる😂
✅ 4-3) CancellationToken をつける(今どき感)🧯
Webやバッチでキャンセルが効くようになる✨ 「あとで付け足す」のもアリだけど、最初から付けるのがラク!
5) 実装してみよう(この章のゴール)🛠️☕🧾
ここでは「注文作成」の入口を interface 化するよ!✨
(※クラス本体の詳細ロジックは前章〜で作ってる前提でOK👌)
5-1) Inbound Port(interface)を作る🚪📝
src/CafeOrder.Application/Ports/Inbound/ICreateOrderUseCase.cs
using System.Threading;
using System.Threading.Tasks;
namespace CafeOrder.Application.Ports.Inbound;
public interface ICreateOrderUseCase
{
Task<CreateOrderResult> HandleAsync(CreateOrderCommand command, CancellationToken ct = default);
}
メソッド名は
HandleAsync/ExecuteAsyncどっちでもOK😊 大事なのは チーム内で統一 することだよ〜📌✨
5-2) UseCase本体が interface を実装する🧩✨
CreateOrderUseCase がある前提で、こうするだけ!
using System.Threading;
using System.Threading.Tasks;
using CafeOrder.Application.Ports.Inbound;
using CafeOrder.Application.Ports.Outbound;
namespace CafeOrder.Application.UseCases;
public sealed class CreateOrderUseCase : ICreateOrderUseCase
{
private readonly IOrderRepository _orderRepository;
public CreateOrderUseCase(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public async Task<CreateOrderResult> HandleAsync(CreateOrderCommand command, CancellationToken ct = default)
{
// ここは前章〜で作った「手順」を入れる場所🧭✨
// 例:Order を作る → 保存する → 結果を返す
// (詳細はこの章の主題じゃないので最小でOK👌)
await Task.CompletedTask;
return new CreateOrderResult(orderId: "TEMP"); // 仮(後でちゃんと)
}
}
ここでの主役は 「入口が interface になった」 ことだよ😊🔌 中身は最小でOK(後で積み上げる)👍
5-3) Command / Result(なければ最小で用意)📦✨
もしまだ無いなら、まずは最小でOK!
namespace CafeOrder.Application;
public sealed record CreateOrderCommand(
string CustomerName,
IReadOnlyList<CreateOrderLineCommand> Lines
);
public sealed record CreateOrderLineCommand(
string ItemName,
int Quantity
);
public sealed record CreateOrderResult(
string OrderId
);
次章でDTOを作ったら、Web側DTO → Command へ変換していくよ🔁✨
6) テストはどうする?(この章っぽいやり方)🧪💖
「interface作っただけ」ってテストしにくいんだけど、 “interfaceとして扱える”ことをテストで固定すると気持ちいいよ😊✨
例:ICreateOrderUseCase sut = new CreateOrderUseCase(...) が通ることを確認!
using CafeOrder.Application.Ports.Inbound;
using CafeOrder.Application.UseCases;
using Xunit;
public class CreateOrderUseCaseTests
{
[Fact]
public void Can_be_used_via_inbound_port_interface()
{
// Arrange
var repo = new FakeOrderRepository();
// Act
ICreateOrderUseCase sut = new CreateOrderUseCase(repo);
// Assert
Assert.NotNull(sut);
}
}
7) AI(Copilot/Codex)に頼むときのコツ🤖✨
“こう頼む”ときれいに出やすいよ😊
-
🪄 プロンプト例
- 「
CreateOrderユースケースの Inbound Port をICreateOrderUseCaseとして作って。引数はCreateOrderCommand、戻り値はTask<CreateOrderResult>、CancellationTokenも付けて」
- 「
-
⚠️ 注意
- 「WebのRequest/Response DTOをApplicationに置かないで」って一言を足すと事故りにくい🚫🙂
8) よくある落とし穴(超あるある)⚠️😂
- ❌
HttpContextやControllerBaseがUseCaseに入ってくる → それは“外側の都合”だからダメ〜!🧼 - ❌ UseCaseが
Request/Response DTOを直接受け取る → DTOはAdapter側!次章でやるよ🔁 - ❌ interfaceが巨大化(メソッド10個とか) → 「ユースケースごとにinterface分ける」 がスッキリ😊✨
9) この章のミニ練習🎯✨
-
「注文一覧」のInbound Portを作ってみてね😊
IGetOrdersUseCaseTask<GetOrdersResult> HandleAsync(GetOrdersQuery query, CancellationToken ct = default);
-
interfaceの置き場所をちゃんと
Application/Ports/Inboundにする📦
10) 章末チェックリスト✅✨
- ✅ Inbound Port(interface)が Application側 にある
- ✅ UseCase本体がそのinterfaceを 実装している
- ✅ 入口(外側)は interfaceを呼ぶ設計 にできる準備ができた
- ✅ Web DTO は まだ中心に入れてない(次章でAdapter側に置く)🧼🔁
次の第23章で、いよいよ DTO(入力/出力) を作って、 「外側のデータ」をキレイに受け渡しできる形にしていくよ〜📦✨😆