メインコンテンツまでスキップ

第7章:Application層入門🎮(ユースケースの置き場)

進行役

この章では「Application層って何するの?🤔」を、**“手順書(ユースケース)”**の感覚でつかんで、実際にC#で形にしていくよ〜!✨

※2026年1月18日時点だと、開発の“今”は .NET 10(最新更新 10.0.2 / 2026-01-13)C# 14 が最新世代だよ🧁(あとで少しだけ触れるね)(Microsoft)


7-1. Application層って結局なに?📋✨

Application層 = “やり方”を並べる層だよ!

たとえば「ToDoを追加する」ってボタンを押したとき、裏ではだいたいこうなるよね👇

  • 入力を受け取る
  • ルールに沿ってモデルを作る
  • 保存する
  • 結果を返す

この “順番・調整・つなぎ込み” を担当するのが Application層🎮✨

✅ Application層の主な仕事(覚えやすい版)

  • ユースケース(UseCase)を置くCreateTodo, CompleteTodo みたいに “操作1つ=1手順書”
  • 境界の整形:入力DTO→ドメインに渡しやすい形に変換、戻り値も整える
  • **トランザクション境界(まとめて成功/失敗)**の中心になりやすい
  • 外部の詳細(DB/HTTP/ファイル)には直接触れない(触るならインターフェース越し)

7-2. Application層に「入れちゃダメ」なもの🙅‍♀️💥

ここが最初に事故りやすいポイント!

❌ 入れない

  • ドメインルール(例:タイトルは3文字以上、期限は未来のみ…みたいな“本質ルール”)

    • それは Domain層の担当💎
  • UI都合ロジック(画面の表示形式、色、ボタン制御)

  • DB直叩き(EF CoreのDbContext直利用)

    • いまは **「保存はインターフェース越し」**で我慢!💪(Repository章で気持ちよく回収するよ)

✅ 入れてOK(むしろ入れる)

  • 「この順番で呼ぶ」
  • 「どの層の何を使うかの調整」
  • 「入力→ドメインへ渡す」
  • 「例外やエラーを“境界の形”に整える(後の章で深掘り)」

7-3. “ユースケース”の設計ルール(超・実戦向け)🧠✨

ルールA:1操作 = 1UseCase 🎯

  • CreateTodoUseCase(追加)
  • CompleteTodoUseCase(完了)
  • GetTodoListUseCase(一覧取得)

これだけで迷子が減るよ〜!🧭✨

ルールB:入力と出力は“専用の型”にする📦

  • 入力:Command(record推奨)
  • 出力:Result(record推奨)

ルールC:UseCaseは「依存を受け取る」📌

  • UseCaseの中で new Repository() しない(DI章で超おいしくなる😋)
  • コンストラクタで受け取る(テストもしやすい🧪)

7-4. ミニ題材:ToDoでApplication層を作ってみよう🪴📝

ここでは最小構成でいくよ!(後の章で育てる前提✨)

フォルダ案(Applicationプロジェクト内)📁

  • Todos/Create/
  • Todos/Complete/
  • Todos/GetList/

7-5. まずはDomainを“薄く”用意(ルールはDomainへ💎)

(Domainの深掘りは8〜9章でやるけど、Application層の例のために最低限だけ置くよ😊)

// Domain/Todos/TodoItem.cs
namespace MyApp.Domain.Todos;

public sealed class TodoItem
{
public Guid Id { get; }
public string Title { get; private set; }
public bool IsCompleted { get; private set; }

public TodoItem(Guid id, string title)
{
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title is required.");
if (title.Length < 3) throw new ArgumentException("Title must be at least 3 chars.");

Id = id;
Title = title;
IsCompleted = false;
}

public void Complete()
{
if (IsCompleted) return;
IsCompleted = true;
}
}

ポイント👀

  • 「3文字以上」みたいな 本質ルールはDomain に置いたよ💎✨
  • Application層は 手順を担当、Domain層は 正しさを担当、って分担が気持ちいい💖

7-6. Application層:UseCase(追加)を作る🎮✨

① 依存の“窓口”インターフェース(いまはApplicationに置く案)🚪

(Repository章で置き場を整理するよ〜!今は前に進むのが大事🙆‍♀️)

// Application/Todos/ITodoRepository.cs
using MyApp.Domain.Todos;

namespace MyApp.Application.Todos;

public interface ITodoRepository
{
Task AddAsync(TodoItem item, CancellationToken ct);
Task<TodoItem?> FindAsync(Guid id, CancellationToken ct);
Task SaveChangesAsync(CancellationToken ct);
}

② 入力(Command)と出力(Result)📦

// Application/Todos/Create/CreateTodoCommand.cs
namespace MyApp.Application.Todos.Create;

public sealed record CreateTodoCommand(string Title);

// Application/Todos/Create/CreateTodoResult.cs
namespace MyApp.Application.Todos.Create;

public sealed record CreateTodoResult(Guid Id);

③ UseCase本体(手順書📋)

// Application/Todos/Create/CreateTodoUseCase.cs
using MyApp.Domain.Todos;

namespace MyApp.Application.Todos.Create;

public sealed class CreateTodoUseCase
{
private readonly ITodoRepository _repo;

public CreateTodoUseCase(ITodoRepository repo)
{
_repo = repo;
}

public async Task<CreateTodoResult> ExecuteAsync(CreateTodoCommand cmd, CancellationToken ct)
{
// Application層でやるのは「入力の衛生チェック」くらいでOK(本質ルールはDomainへ)
if (cmd is null) throw new ArgumentNullException(nameof(cmd));

var id = Guid.NewGuid();

// ルールはDomainに任せる(不正ならDomainが止める)
var todo = new TodoItem(id, cmd.Title);

// 手順:作る→保存→確定
await _repo.AddAsync(todo, ct);
await _repo.SaveChangesAsync(ct);

return new CreateTodoResult(id);
}
}

ここがApplication層の“おいしいところ”😋✨

  • 順番だけ書いてる(だから読みやすい)
  • ルールはDomain(だから変更に強い)
  • 保存はインターフェース越し(だから後でDB差し替え楽)

7-7. Presentation層から呼ぶ例(最小イメージ)🖥️📞

たとえばMinimal APIで「薄いPresentation」を作るとこういう雰囲気👇 (※ここは雰囲気が伝わればOK!Presentationの責務は“受けて呼ぶだけ”🎨)

// Presentation/Program.cs (イメージ)
using MyApp.Application.Todos.Create;

app.MapPost("/todos", async (CreateTodoCommand cmd, CreateTodoUseCase useCase, CancellationToken ct) =>
{
var result = await useCase.ExecuteAsync(cmd, ct);
return Results.Created($"/todos/{result.Id}", result);
});

7-8. 「UseCase = トランザクション境界」ってどういうこと?🗃️🔒

感覚としてはこれ👇

  • ユースケース内で行う一連の処理

    • 全部成功したらコミット
    • 途中で失敗したらロールバック

実際のDBを使う章(12〜14章)で本格導入するけど、考え方は今から持っておくと強い💪✨

.NET側には TransactionScope みたいに「この範囲をトランザクションとして扱う」仕組みもあるよ🧠(説明も公式にある)(Microsoft Learn) ※ただし、闇雲に使うより「UseCase単位で境界を作る」発想が先にあるとスッキリするよ〜😊


7-9. UseCaseのテストが“めっちゃ簡単”になる🧪💖

Repositoryを偽物(インメモリ)にすれば、UseCase単体テストがスイスイ!

// Tests/Fakes/FakeTodoRepository.cs
using MyApp.Application.Todos;
using MyApp.Domain.Todos;

public sealed class FakeTodoRepository : ITodoRepository
{
public readonly List<TodoItem> Items = new();

public Task AddAsync(TodoItem item, CancellationToken ct)
{
Items.Add(item);
return Task.CompletedTask;
}

public Task<TodoItem?> FindAsync(Guid id, CancellationToken ct)
=> Task.FromResult(Items.FirstOrDefault(x => x.Id == id));

public Task SaveChangesAsync(CancellationToken ct) => Task.CompletedTask;
}
// Tests/CreateTodoUseCaseTests.cs (xUnit想定イメージ)
using MyApp.Application.Todos.Create;
using Xunit;

public class CreateTodoUseCaseTests
{
[Fact]
public async Task Create_adds_item_and_returns_id()
{
var repo = new FakeTodoRepository();
var useCase = new CreateTodoUseCase(repo);

var result = await useCase.ExecuteAsync(new CreateTodoCommand("Buy milk"), default);

Assert.NotEqual(Guid.Empty, result.Id);
Assert.Single(repo.Items);
Assert.Equal("Buy milk", repo.Items[0].Title);
}
}

7-10. AI(Copilot/Codex)を“相棒”にするプロンプト例🤖💡

① UseCase雛形を作らせる🧱

  • 「CreateTodoUseCase を、入力Command/出力Result含めて、DomainルールはDomainに寄せる方針で作って」
  • 「UseCase内にUI都合・DB直叩きが混ざってないかレビューして」

② “層違反”チェックをさせる🚨

  • 「このUseCaseの責務がApplication層として適切か、Domainに移すべきルールが入っていないか指摘して」
  • 「Presentation層にビジネスルールが漏れてないか、漏れてたら移動案を出して」

③ テスト生成🧪

  • 「Fake repositoryを作って、このUseCaseのユニットテストを書いて」
  • 「異常系(Titleが短い)のテストも追加して」

章末チェックリスト✅✨(ここだけでも超大事)

  • Application層は「順番」を書いてる?📋
  • ドメインルール(正しさ)がApplicationに紛れてない?💎
  • DB/HTTP/ファイルなど“詳細”に直接触ってない?📦
  • 入力/出力の型(Command/Result)を用意できてる?📦
  • UseCaseがテストしやすい形(依存を受け取る形)になってる?🧪

ミニ課題🎯(次章がラクになるやつ)

  1. CompleteTodoUseCase を作ってみよう✅
  • 入力:CompleteTodoCommand(Guid Id)
  • 手順:Findtodo.Complete()SaveChanges
  1. “どこまでがApplicationの責務?”をAIにレビューさせて、指摘をメモしておこう📝🤖

必要なら、この章の続きとして「第7章の完成形(Create/Complete/GetListの3UseCase+Presentation呼び出し+FakeRepo+テスト一式)」まで、まるっと教材用に整形して出すよ〜!🥰💪