42章:DB/ライブラリの“詳細”は外側で完結させる🧰
(※本章の前提に合うように、2026-01-13 時点の最新パッチ版として .NET 10.0.2 / EF Core 10.0.2 を前提に話を組み立てます📌) (Microsoft)
1) この章のゴール🎯💖
この章を終えると、次ができるようになります☺️✨
- Core(Entities/UseCases)がDBやEF Coreを一切知らない状態にできる🧼
- 接続文字列・マイグレーション・EF設定などの“濃い話”を 外側(Frameworks側)に隔離できる🧱
- 「DBを変えたい」「ORMを変えたい」「設定を変えたい」が来ても、Coreが無傷でいられる🛡️
2) そもそもDBやライブラリは“詳細”ってどういう意味?🤔🧩

クリーンアーキ的には、DB・ORM(EF Core)・ログ基盤・外部SDKなどは “いつでも変わり得る都合”=詳細 です🌀
- DBの種類(SQL Server / PostgreSQL / SQLite…)が変わるかも🗄️
- EF Coreの設定や癖が変わるかも⚙️
- マイグレーション運用が変わるかも🚚
- 接続情報の置き方が変わるかも🔐
だから 中心(Core)に入れない のが勝ち筋です🏆✨ 中心が汚れると、変更がドミノ倒しで広がります💥
3) 置き場所の鉄板パターン🏠✨(ざっくり図)
イメージはこう👇
- Core:純粋なC#(Entity/VO/UseCase/Port)🧼
- Adapters:変換(Controller/Presenter/Repository実装の“手前”)🔄
- Frameworks(外側):EF Core、DbContext、接続、マイグレ、外部SDK、設定…🧰
たとえばプロジェクト分割はこんな感じが分かりやすいです😊
MyApp.Core(Entities/UseCases/Ports)MyApp.Infrastructure(EF Core / DbContext / Repository実装)MyApp.Web(ASP.NET Core / DI / appsettings / 起動)
ポイントは EF Coreの参照(NuGet)を Core に入れない ことです❗️
4) 実装でやること(最短ルート)🏃♀️💨
4-1. Core側:インターフェイスだけ置く🔌✨
Core(UseCases)に 出口(Port) を置きます。DBの話はゼロでOK☺️
// MyApp.Core
public interface IMemoRepository
{
Task AddAsync(Memo memo, CancellationToken ct);
Task<Memo?> FindAsync(MemoId id, CancellationToken ct);
}
✅ ここで DbContext とか Entity Framework とか言い出したらアウトです🙅♀️💦
4-2. Infrastructure側:EF Core “だけ”で完結させる🗄️🧰
Infrastructure にだけ EF Core を入れて、DbContextと実装を置きます✨
// MyApp.Infrastructure
using Microsoft.EntityFrameworkCore;
public sealed class MemoDbContext : DbContext
{
public MemoDbContext(DbContextOptions<MemoDbContext> options) : base(options) { }
public DbSet<MemoRecord> Memos => Set<MemoRecord>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MemoRecord>(e =>
{
e.HasKey(x => x.Id);
e.Property(x => x.Title).HasMaxLength(100);
});
}
}
public sealed class MemoRecord
{
public Guid Id { get; set; }
public string Title { get; set; } = "";
}
そして Repository 実装も Infrastructure に置きます👇
// MyApp.Infrastructure
using Microsoft.EntityFrameworkCore;
public sealed class EfMemoRepository : IMemoRepository
{
private readonly MemoDbContext _db;
public EfMemoRepository(MemoDbContext db) => _db = db;
public async Task AddAsync(Memo memo, CancellationToken ct)
{
_db.Memos.Add(new MemoRecord { Id = memo.Id.Value, Title = memo.Title.Value });
await _db.SaveChangesAsync(ct);
}
public async Task<Memo?> FindAsync(MemoId id, CancellationToken ct)
{
var rec = await _db.Memos.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id.Value, ct);
return rec is null ? null : Memo.Rebuild(new MemoId(rec.Id), new Title(rec.Title));
}
}
✅ EFの都合(Record/DbContext/AsNoTracking) は全部外側に閉じ込められました🎉
4-3. Web(Composition Root):DIと設定値はここで配線🧵✨
Program.cs 側で 接続文字列 → DbContext → Repository を組み立てます🪄
(EF Core は ASP.NET Core のDIに AddDbContext で追加するのが基本です) (Microsoft Learn)
// MyApp.Web - Program.cs
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MemoDbContext>(options =>
{
var cs = builder.Configuration.GetConnectionString("MainDb");
options.UseSqlServer(cs);
});
builder.Services.AddScoped<IMemoRepository, EfMemoRepository>();
var app = builder.Build();
app.Run();
✅ 接続文字列はコードに直書きしない(appsettings / 環境変数 / シークレットへ)🔐✨ この「設定の置き場所」も 詳細だから外側です👍
5) マイグレーションは“外側の都合”そのもの🚚🧱
マイグレーションは DBスキーマ運用の話なので、Coreに近づけないのが吉です☺️
5-1. 「マイグレーション用プロジェクト」を分離できる✨
EF Coreは マイグレーションを別プロジェクトに置く公式手順があります📚 (Microsoft Learn)
MyApp.Infrastructure:DbContext(本体)MyApp.Migrations:マイグレーションだけ(必要なら)MyApp.Web:起動プロジェクト(Startup)
「DbContextがある場所」と「起動プロジェクト」が違うときは、CLIで明示できます🔧 (Microsoft Learn)
例(概念)👇
--project:マイグレーションを生成するプロジェクト--startup-project:実行時の設定を読むプロジェクト(appsettings等)
dotnet ef migrations add Init --project MyApp.Migrations --startup-project MyApp.Web
dotnet ef database update --project MyApp.Migrations --startup-project MyApp.Web
(EF Core CLIのリファレンスもここで確認できます📖) (Microsoft Learn)
6) DbContextの寿命問題も“外側で吸収”できる🧯✨
通常のWebアプリは AddDbContext(scoped)が素直でOK👍 (Microsoft Learn)
でも、UIが長寿命になりやすい種類(例:Blazor Serverなど)では DbContextの使い回しが事故りやすいため、AddDbContextFactory を使うケースもあります🧪 (Microsoft Learn)
ここでもポイントは同じで、 Coreは寿命の話を知らない → 外側で注入の仕方を変えるだけ🪄✨
7) よくある事故パターン🚨(秒で診断できる)
❌事故1:CoreがEF Core参照してる
MyApp.Core.csprojにMicrosoft.EntityFrameworkCoreが入ってる → 即アウト🙅♀️💦 ✅ 対処:EFパッケージはInfrastructureだけに移動
❌事故2:Entityに [Table] とかEF属性が付いてる
- DomainがDB形状に引っ張られる😵 ✅ 対処:永続化用Record(DBモデル)を外側に作ってマッピング(34章の発想)🔁✨
❌事故3:接続文字列がコード直書き
- 環境差分・漏洩・テストが地獄😱 ✅ 対処:設定へ退避(appsettings / 環境変数 / シークレット)🔐
8) ミニ課題🎮💖(30〜60分)
Coreから EF参照ゼロにする(NuGetもusingも)🧼✨InfrastructureにDbContextとEfMemoRepositoryを置く🗄️WebのProgram.csでDI配線して動かす🧵🪄- 余裕があれば:マイグレーションを別プロジェクトへ分離🚚 (Microsoft Learn)
9) Copilot/Codexに頼むと強いプロンプト例🤖✨
- 「CoreにEF参照が混ざってないか、プロジェクト構造と参照をチェックして」🔎
- 「DbContext/Repository実装をInfrastructureに寄せたうえで、Coreのinterfaceだけで成立する形にリファクタして」🧹
- 「マイグレーションを別プロジェクトに移す手順と、dotnet ef コマンド例をこの構成向けに出して」🚚
まとめ🎁✨
- DB・EF・設定・マイグレは 全部“詳細” → 外側に閉じ込める🧰🧱
- Coreは ルールと手順(Entity/UseCase/Port)だけでピカピカに保つ🧼✨
- 変更が来たときに、直す場所が Infrastructure/Webに限定されるのが最大の勝ち🏆💖
次(43章)は、いよいよ 中心(Entities)のテストで“速くて気持ちいい安心感”を作っていくよ〜🧪🍰💕