場面
1781年、プロイセン王国のケーニヒスベルク。バルト海に近い港湾都市に、一人の哲学者が10年間の沈黙を破って本を出版した。57歳のイマヌエル・カントは、哲学の世界に地殻変動をもたらそうとしていた。
本のタイトルは『純粋理性批判』。しかし内容を説明するのに、カントは哲学用語を使わずに天文学の比喩を使った。「私がやろうとしていることは、コペルニクスが天文学でやったことと同じだ」。
コペルニクスが転倒させたのは何か。それまでの天文学は地球を中心に置いていた。太陽が地球の周りを回る(天動説)。しかし惑星の動きを計算すると奇妙な「逆行運動」が現れ、モデルは複雑になる一方だった。コペルニクスの逆転:太陽が中心で、地球が動く(地動説)。中心と周縁を入れ替えただけで、惑星の動きが見事に説明できた。
カントが認識論でやろうとしたのはこれと同じ転倒だった。
従来の認識論の前提:心(主体)が世界(客体)を受動的に受け取る。世界がどうあるかを心が写す。ちょうど蝋に印が押されるように、外の世界の形が心の中に刻まれる。
カントの逆転:心が世界を受け取るのではなく、心の構造が経験を成形する。空間・時間・因果性——私たちが「世界の性質」だと思っているものは、実は私たちの認識装置が経験に課している形式だ。心は受け皿ではなく、鋳型だ。
どちらが中心に来るかで、何が何に従うかが変わる。地動説では地球が太陽に従う。カントの転倒では、経験が心の形式に従う。「何が何に合わせて形を変えるか」という方向が、理解全体を決定する。
カントが生きた時代から200年以上後。同じ転倒が、まったく違う文脈で繰り返された。
答え
「何が何に従うか」という従属の方向が、設計の全てを決める。コードがテストに従うのか、テストがコードに従うのか。ビジネスロジックがデータベースに従うのか、データベースがビジネスロジックに従うのか。この方向を逆転させることで、関係全体が組み直される。カントが認識論でやったことを、ソフトウェア設計でも繰り返せる。
CSへの翻訳
TDDにおけるコペルニクス的転倒
テスト駆動開発(Test-Driven Development)は、ケント・ベックが2002年の著書『Test-Driven Development By Example』で体系化した(実践はそれ以前からあった)。
従来の開発の方向:実装を書く → テストを書いて検証する。テストはコードに従う。テストは、コードが「すでにやっていること」を記述するドキュメントだ。
TDDの転倒:テストを先に書く → テストを通るように実装する。コードがテストに従う。テストは、コードが「やるべきこと」を定義する仕様だ。
この転倒は小さく見えて根本的だ。「実装を先に書いてからテストを書く」スタイルのテストは、知らず知らずのうちに実装のバイアスを引き受ける。実装が間違えていれば、テストも同じ間違いを学習してしまう。しかし「テストを先に書く」スタイルでは、テストは実装に汚染されていない。意図が純粋に記述される。
依存性逆転原則(DIP)における転倒
1996年、ロバート・C・マーティンが依存性逆転原則(Dependency Inversion Principle)を定式化した。
従来の依存の方向(転倒前):高レベルのモジュール(ビジネスロジック)が低レベルのモジュール(データベース、フレームワーク)に依存する。ビジネスロジックがインフラに従う。
DIPの転倒(転倒後):高レベルのモジュールがインターフェースを定義する。低レベルのモジュールがそのインターフェースを実装する。インフラがビジネスロジックに従う。
ヘキサゴナルアーキテクチャにおける転倒
2005年、アリスター・コバーンが「ポート&アダプター(ヘキサゴナルアーキテクチャ)」を発表した。
従来のアーキテクチャでは、アプリケーションコアが外部システム(DB・メッセージキュー・HTTP API)に合わせて設計された。外側が中心を決める。
ヘキサゴナルアーキテクチャの転倒:アプリケーションコアが「ポート(インターフェース)」を定義する。外部システムが「アダプター(インターフェースの実装)」を提供する。外側がコアに合わせる。コア(ドメイン)が太陽になり、外部システム(DB・UI・API)が地球になる。
設計への示唆
カントの転倒をソフトウェア設計に当てるとき、問うべき問いはひとつだ。「何が何に従うか?」
❌ 転倒前(インフラが中心)
// データベースが先にある。ビジネスロジックはDBに従う。
class UserService {
private pg: PostgresClient; // 具体的なDBクライアントに依存
async updateEmail(userId: string, newEmail: string): Promise<void> {
// ビジネスロジックがSQL構文と結合している
await this.pg.query(
'UPDATE users SET email = $1 WHERE id = $2',
[newEmail, userId]
);
}
}
// テストにはPostgresが必要。テストDBのセットアップが必要。
// テストが遅い。テストが脆い(DB接続が切れると落ちる)。
// コードはDBに従っている。
✅ 転倒後(ドメインが中心)
// ビジネスロジックが先に要件を定義する。インフラはそれに従う。
interface UserRepository {
findById(id: string): Promise<User | null>;
save(user: User): Promise<void>;
}
class UpdateEmailUseCase {
constructor(private repo: UserRepository) {} // インターフェースに依存
async execute(userId: string, newEmail: string): Promise<void> {
const user = await this.repo.findById(userId);
if (!user) throw new UserNotFoundError(userId);
if (!isValidEmail(newEmail)) throw new InvalidEmailError(newEmail);
const updated = user.withEmail(newEmail);
await this.repo.save(updated);
}
}
// テスト用:インメモリ実装(DBなし)
class InMemoryUserRepository implements UserRepository {
private store = new Map<string, User>();
async findById(id: string) { return this.store.get(id) ?? null; }
async save(user: User) { this.store.set(user.id, user); }
}
// テストはインメモリを使う。DBなし。高速。
describe('UpdateEmailUseCase', () => {
it('有効なメールアドレスへの更新を許可する', async () => {
const repo = new InMemoryUserRepository();
const useCase = new UpdateEmailUseCase(repo);
// ... テストの本体。DBなしで実行できる。
});
});
// 本番用:PostgreSQL実装
class PostgresUserRepository implements UserRepository { /* ... */ }
転倒後のコードでは、UpdateEmailUseCaseの単体テストにデータベースは不要だ。ビジネスロジックがインフラに依存していないから、インフラなしで動かせる。テスト速度が桁違いになる。
カントが「コペルニクス的転回」と名づけたのは、中心と周縁を入れ替える転倒を示すためだった。天文学では太陽と地球が入れ替わった。ソフトウェアでは、ドメインとインフラが入れ替わる。
プラトンとカントの分業
第7話のプラトンも「方向の転倒」を語っていた——個物(影)はイデア(Form)に従う。これはあくまで論理的な順序の主張だ:「抽象が具体に先行する」。
カントの転倒はそこから一歩深い。何が何を構成するかの方向を入れ替える。プラトンが「個物はイデアの不完全な模倣だ(実在の階層)」と言ったのに対し、カントは「経験そのものが心の形式に従って構成される(構成の方向)」と言った。
ソフトウェアにあてはめると:
- **プラトン的:**インターフェース(イデア)が先で、実装(影)が後(DIP)
- **カント的:**ユースケースの要請(心の形式)が、インフラの形(経験対象)を構成する(TDD・ヘキサゴナル)
二つは矛盾せず、入れ子になっている。プラトンが「抽象が具体より根本だ」と言い、カントが「では何が抽象を決めるのか——それは認識する主体だ」と問いを更新した。設計でも、まずインターフェースを先に書き(プラトン)、次にそのインターフェースの形を決めるのはユースケース側の必要だと自覚する(カント)——この二段の転倒で、依存関係は完全に逆向きになる。
graph TB
subgraph "❌ 転倒前:インフラが中心"
BL1[ビジネスロジック] -->|依存| DB1[PostgreSQL]
BL1 -->|依存| HTTP1[HTTPクライアント]
BL1 -->|依存| MQ1[メッセージキュー]
note1[インフラを変えるとビジネスロジックが壊れる\nテストにインフラが必要]
end
subgraph "✅ 転倒後:ドメインが中心(ヘキサゴナル)"
PORT1[UserRepository\nport/interface]
PORT2[EmailService\nport/interface]
DOMAIN[ビジネスロジック\nドメイン] --> PORT1
DOMAIN --> PORT2
DB2[PostgresAdapter] -.->|implements| PORT1
MOCK[InMemoryAdapter] -.->|implements| PORT1
SMTP[SmtpAdapter] -.->|implements| PORT2
note2[ドメインはポートだけを知る\nインフラはアダプターとして差し替え可能]
end
style DOMAIN fill:#fffacd,stroke:#d4a017
style PORT1 fill:#e8f5e9
style PORT2 fill:#e8f5e9
style note2 fill:#e8f5e9
style note1 fill:#ffebee
従属の方向が設計を決める。インフラ(具体的なDB・フレームワーク・外部API)を中心に置けば、ビジネスロジックはインフラに引きずられる。変更に弱く、テストが重い。ドメイン(ビジネスロジック・ユースケース)を中心に置けば、インフラは交換可能なアダプターになる。変更に強く、テストが軽い。
コペルニクスが太陽を中心に置いたとき、惑星の動きが単純になった。ドメインを中心に置いたとき、テストが単純になる。どちらも同じ構造の転倒だ。
カントは「この転倒なしには形而上学は進歩できない」と主張した。現代のソフトウェアにおいても、「この転倒なしには、ビジネスロジックはインフラの奴隷のままだ」と言える。テストを先に書くこと。インターフェースを先に定義すること。ドメインにインフラを従わせること——これらはすべて、同じコペルニクス的転倒の異なる表現だ。
問い:あなたのビジネスロジックは何に従っているか。DBのスキーマに引きずられていないか、フレームワークの制約に形を決められていないか。コペルニクス的転倒がまだ起きていないなら、地球はまだ宇宙の中心だ。