第05章:フォルダ構成と“依存の向き”ルール🧱➡️
0. この章でできるようになること🎯✨
- 「どこに何を書くか」を迷わないフォルダの“住所”が決まる🏠📁
- **依存の向き(内向きルール)**が説明できる🧭➡️
- 「DBやAPIの都合」がドメインを汚さない形にできる🧼✨
- ルール違反を ESLintで機械的に止めるところまでいける🚦🤖
1. なんでフォルダ構成を先に決めるの?🤔📁
最初にフォルダ構成を決めるのは、「未来の自分が泣かない」ためです😭➡️😂 とくに集約(Aggregate)を学ぶときは、**ドメイン(業務ルール)を守る“箱”**が先に必要になります📦🔒
よくある事故💥
- 画面やAPIの都合でドメインの型がぐちゃぐちゃになる🫠
- DBの都合(カラム・JOIN)でドメインのクラスが歪む😵💫
- 「どこで何してるの?」が分からなくなって改修が怖い😱
そこでこの章は、先に “片付く部屋” を作ります🧹✨
2. まずは結論:この4つに分ける📦📦📦📦
この教材では、最初は迷わないように 4分割 でいきます😊
domain/:業務ルールの中心(集約・値オブジェクト・不変条件)👑🔒application/:ユースケースの手順(取得→変更→保存の指揮)🎮🧩infrastructure/:DB・外部API・メールなど「外の世界」🌍🔌tests/:テスト(壊れてない保証)🧪✅
3. フォルダの中身(住所表)📘🗺️

3.1 ざっくり全体図🧱
- ドメインが中心🟣
- 外側(DBやAPI)は後から差し替えOK🔁✨
- 依存は 外→内 に向かう🧭➡️
外側(変わりやすい)🌪️
infrastructure ─────┐
│ depends on
application ───────┤ (外から内へ)
│
domain <──────┘
内側(守りたい)🛡️
この「内側を守る」考え方は、いわゆるクリーン/オニオン的な発想です🧅✨(ドメインが他に依存しないのが肝!)
3.2 具体的なフォルダ例(ミニEC)🛒📦💳
src/
domain/
order/
Order.ts // 集約ルート(例)
OrderItem.ts
Money.ts // Value Object(例)
OrderId.ts
errors.ts // ドメインエラー(例)
OrderRepository.ts // “契約”だけ(interface)
application/
order/
PlaceOrder.ts // ユースケース
PayOrder.ts
dtos.ts // 外へ出す形(DTO)
infrastructure/
order/
InMemoryOrderRepository.ts // 実装(後でDB版に差し替え)
PrismaOrderRepository.ts // 例:DB実装(将来)
tests/
domain/
Order.test.ts
application/
PlaceOrder.test.ts
4. “依存の向き”ルール(超だいじ)🧭🚦
4.1 ルールはこれだけ覚えてOK✅
内側(domain)は、外側を知らない 🙈🔒 つまり…
domainはapplicationやinfrastructureを importしない🚫applicationはinfrastructureを importしない🚫infrastructureはdomainとapplicationを importしてOK✅
表にするとこう👇📊
| from \ to | domain | application | infrastructure |
|---|---|---|---|
| domain | ✅ | 🚫 | 🚫 |
| application | ✅ | ✅ | 🚫 |
| infrastructure | ✅ | ✅ | ✅ |
5. “契約は内側、実装は外側”テクニック🪄📦
ここが気持ちよく効きます😆✨
5.1 Repositoryは「domainにinterface」「infraに実装」🧩🔌
domain側(契約):
// src/domain/order/OrderRepository.ts
import { Order } from "./Order.js";
import { OrderId } from "./OrderId.js";
export interface OrderRepository {
findById(id: OrderId): Promise<Order | null>;
save(order: Order): Promise<void>;
}
infrastructure側(実装):
// src/infrastructure/order/InMemoryOrderRepository.ts
import { OrderRepository } from "../../domain/order/OrderRepository.js";
import { Order } from "../../domain/order/Order.js";
import { OrderId } from "../../domain/order/OrderId.js";
export class InMemoryOrderRepository implements OrderRepository {
private store = new Map<string, Order>();
async findById(id: OrderId): Promise<Order | null> {
return this.store.get(id.value) ?? null;
}
async save(order: Order): Promise<void> {
this.store.set(order.id.value, order);
}
}
ポイント💡
- domainは 「保存できる」という契約だけ知ってればいい😊
- DBがPrismaでもSQLでも、あとから差し替えできる🔁✨
6. TypeScript(2026)で迷いがちな“モジュール設定”のコツ🧠⚙️
2026年のTypeScriptは、Nodeのモジュール事情に合わせた設定がどんどん整っています📦✨
TypeScriptは moduleResolution に nodenext を使う説明も公式にあります📘✨ (typescriptlang.org)
さらに TypeScript 5.6 では .mts / .cts の扱い(ESM/CJSの出し分け)がより明確になっています🧩 (typescriptlang.org)
この教材では、まずは混乱しにくい“無難セット”でOKです😊
例:tsconfig.json(一例)
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"outDir": "dist",
"rootDir": "src",
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src", "tests"]
}
※Nodeは 2026年1月時点で v24がActive LTS、v25がCurrentとして案内されています📌 (nodejs.org) (セキュリティ更新も継続的に出ています🛡️) (nodejs.org)
7. 手を動かす:骨組みを一気に作る🛠️💨
7.1 フォルダ作成(PowerShell)🪟✨
mkdir src, tests
mkdir src\domain, src\application, src\infrastructure
mkdir src\domain\order, src\application\order, src\infrastructure\order
mkdir tests\domain, tests\application
7.2 “依存の向き”が守れてるか一発で確認する方法👀✅
チェック質問(これだけ!)
- domainの中で
infrastructureって単語を検索して出たらアウト🚫😇 - applicationの中で
infrastructureをimportしてたらアウト🚫😇
(VS Code の検索でOK🔍✨)
8. 依存ルールを“機械で止める”🚦🤖(ESLint)
目視チェックは疲れるし漏れます😵💫 だから ESLintで強制 が強い💪✨
8.1 まずはESLint標準ルールで止める(シンプル版)🧼
ESLintには no-restricted-imports という「このimport禁止ね🚫」ができます🛑 (eslint.org)
例:applicationからinfrastructureを禁止
// eslint.config.js(例:フラット設定の一部イメージ)
export default [
{
files: ["src/application/**/*.ts"],
rules: {
"no-restricted-imports": [
"error",
{
patterns: ["../infrastructure/*", "../../infrastructure/*", "**/infrastructure/*"]
}
]
}
},
{
files: ["src/domain/**/*.ts"],
rules: {
"no-restricted-imports": [
"error",
{
patterns: ["**/application/*", "**/infrastructure/*"]
}
]
}
}
];
8.2 境界チェック専用プラグインを使う(しっかり版)🧱✨
@boundaries/eslint-plugin は「層の境界を守る」目的のESLintプラグインです📏✨ (npmjs.com)
大きくなってきたらこっちが便利になりやすいです😊
9. 小テスト:このimport、どこに置く?クイズ🎮🧠
次のコードがあったとして…どの層が正解?👇✨
-
Order.pay()の中でfetch("https://pay.example.com")を呼んでる💳🌍 → 正解:domainじゃない! 🚫(外部I/Oは外側へ🔌) -
PlaceOrderがPrismaClientを直接触ってる🗄️ → 正解:applicationじゃない! 🚫(DB都合はinfrastructureへ🏃♀️) -
InMemoryOrderRepositoryがOrderRepositoryを実装してる📦 → 正解:infrastructure✅(外側が内側の契約を満たすのはOK✨)
10. AI活用🤖✨(コピペで使えるプロンプト集)
10.1 「層の仕分けクイズ」を作ってもらう🎮
- 「ミニECの機能を10個出して、domain/application/infrastructureのどこに置くべきかクイズにして。解答と理由もつけて😊」
10.2 既存コードの“住所間違い”を指摘してもらう🧯
- 「このファイルはどの層に置くべき?理由は“依存の向き”で説明して。違反importがあれば候補修正案も出して🧠✨」
10.3 “契約(interface)”抽出を手伝ってもらう🧩
- 「domainが知っていいのは“契約”だけ。外部I/Oを分離するために必要なinterface候補を列挙して😊」
11. まとめ🧷✨
- フォルダは domain / application / infrastructure / tests の4つ📁
- 依存の向きは 外→内 🧭➡️
- domainは “外部都合” を知らない🙈🔒
- ルールはESLintで止めると強い🚦✨ (eslint.org)
次章からは、この骨組みの上で「型で守る」「不変条件」「集約」の中身を気持ちよく作っていきます🛒📦💳✨