導入
クリーンアーキテクチャは、ソフトウェアの構造を明確にし、保守性や拡張性を高めるための手法です。本記事では、TypeScriptを用いて実際の業務で遭遇する具体的なシチュエーションを通じて、クリーンアーキテクチャの実践的な応用を考察します。特に、依存関係の逆転や、ビジネスロジックとUIの分離について掘り下げます。
教科書レベルの解説(クリーンアーキテクチャ)
重要な概念の整理
クリーンアーキテクチャでは、ソフトウェアの各層が明確に分かれており、それぞれが独立して機能することが求められます。データベースやUIの詳細がビジネスロジックに影響を与えないように設計することが重要です。この考え方により、システムの変更が容易になり、新しい技術の導入もスムーズに行えます。
コード例(TypeScript)
interface User {
id: number;
name: string;
}
interface UserRepository {
findById(id: number): Promise;
}
class GetUser {
constructor(private userRepository: UserRepository) {}
async execute(id: number): Promise {
return await this.userRepository.findById(id);
}
}
コードの行ごとの解説
- interface User: ユーザーのデータ構造を定義しています。IDと名前を持つことが求められます。
- interface UserRepository: ユーザー情報を取得するためのリポジトリインターフェースを定義しています。データ取得の抽象化を行い、実装の詳細に依存しない設計を実現します。
- class GetUser: ユーザー情報を取得するユースケースを表現するクラスです。リポジトリインターフェースを受け取ることで、依存性の逆転を実現しています。
- async execute: ユーザーIDを受け取り、リポジトリからユーザー情報を取得するメソッドです。ビジネスロジックがリポジトリの実装に依存しないことを示しています。
練習問題編
以下に練習問題を用意しました。各問題に対する模範解答と解説も記載しています。
- 問題1: UserRepositoryインターフェースに新しいメソッドを追加し、全ユーザーを取得する機能を実装してください。
- 模範解答:
interface UserRepository { findById(id: number): Promise; findAll(): Promise ; } - 問題2: GetUserクラスにエラーハンドリングを追加してください。
- 模範解答:
async execute(id: number): Promise{ try { return await this.userRepository.findById(id); } catch (error) { console.error("Error fetching user:", error); return null; } } - 問題3: UserRepositoryのモック実装を作成し、GetUserクラスのテストを行ってください。
- 模範解答:
class MockUserRepository implements UserRepository { private users: User[] = [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]; async findById(id: number): Promise{ return this.users.find(user => user.id === id) || null; } async findAll(): Promise { return this.users; } }
まとめ
- クリーンアーキテクチャの設計原則を実践することで、ソフトウェアの保守性が向上します。
- 依存性の逆転により、ビジネスロジックが具体的な実装に影響されない設計が可能です。
- 練習問題を通じて、実際の開発現場での応用力を高めることができます。