TypeScript中級

中級 TypeScriptで学ぶクリーンアーキテクチャ|アンチパターン編

導入

クリーンアーキテクチャは、ソフトウェア開発においてコードの可読性や保守性を高めるための重要なアプローチです。しかし、実際の開発現場では様々なアンチパターンが存在し、クリーンアーキテクチャの理念が損なわれることがあります。本記事では、TypeScriptを用いた具体的なケーススタディを通じて、よく見られるアンチパターンとその改善策について考察します。

教科書レベルの解説(クリーンアーキテクチャ)

重要な概念の整理

クリーンアーキテクチャは、ソフトウェアの各レイヤーを明確に分離することを目的としています。一般的には、エンティティ、ユースケース、インターフェース、フレームワークの4つのレイヤーに分かれます。この分離により、ビジネスロジックが外部の影響を受けにくくなり、テストや保守が容易になります。

コード例(TypeScript)


class User {
    constructor(public id: number, public name: string) {}
}

class UserRepository {
    private users: User[] = [];

    addUser(user: User) {
        this.users.push(user);
    }

    getUserById(id: number): User | undefined {
        return this.users.find(user => user.id === id);
    }
}

class UserService {
    constructor(private userRepository: UserRepository) {}

    registerUser(id: number, name: string) {
        const user = new User(id, name);
        this.userRepository.addUser(user);
    }

    findUser(id: number): User | undefined {
        return this.userRepository.getUserById(id);
    }
}

コードの行ごとの解説

  1. Userクラス: ユーザーの基本情報を保持するシンプルなデータモデルです。
  2. UserRepositoryクラス: ユーザーの追加や取得を行うリポジトリです。データの永続化を担う役割を持ちます。
  3. UserServiceクラス: ユーザーに関するビジネスロジックを実装するサービスです。リポジトリを利用してユーザーを管理します。

アンチパターン編

このコード例には、一見すると問題がないように見える部分がありますが、実際にはいくつかのアンチパターンが潜んでいます。特に、リポジトリのインスタンスを直接Serviceに持たせるという設計は、テストの難易度を上げ、依存性の管理が煩雑になる原因となります。

この問題を解決するためには、依存性注入を用いることが有効です。例えば、UserServiceのコンストラクタにRepositoryのインターフェースを受け取るようにすることで、テスト用のモックを簡単に差し替えられるようになります。


interface IUserRepository {
    addUser(user: User): void;
    getUserById(id: number): User | undefined;
}

class UserRepository implements IUserRepository {
    private users: User[] = [];

    addUser(user: User) {
        this.users.push(user);
    }

    getUserById(id: number): User | undefined {
        return this.users.find(user => user.id === id);
    }
}

class UserService {
    constructor(private userRepository: IUserRepository) {}

    registerUser(id: number, name: string) {
        const user = new User(id, name);
        this.userRepository.addUser(user);
    }

    findUser(id: number): User | undefined {
        return this.userRepository.getUserById(id);
    }
}

このように、インターフェースを利用することで、実装を簡単に差し替えられるため、テストが容易になり、コードの柔軟性が向上します。

まとめ

  • クリーンアーキテクチャの原則を守ることが、ソフトウェアの保守性を高める。
  • 依存性注入を活用することで、テストの容易さとコードの柔軟性を確保できる。
  • 具体的なアンチパターンを理解し、改善策を実践することで、より良いアーキテクチャを構築できる。