【Typescript】Clean ArchitectureでToDo APIを実装する

【Typescript】Clean ArchitectureでToDo APIを実装する

2021年10月2日
アーキテクチャ

Clean Architectureを読んで、実際にTSでToDoアプリのAPIを実装してみました。

クリーンアーキテクチャとは

クリーンアーキテクチャはソフトウェアをレイヤーに分割することで、関心事の分離を実現し、以下の特性を持ったシステムを生み出します。

  • フレームワーク非依存:システムをフレームワークの制約で縛るのではなく、フレームワークをツールとして使用する
  • テスト可能:ビジネスルールはUIやDB、サーバー、その他の外部要素がなくてもテストできる
  • UI非依存:UIはシステムの他の部分を変更することなく、簡単に変更できる
  • データベース非依存:ビジネスルールはDBに束縛されていない
  • 外部エージェント非依存:ビジネスルールは外界のインターフェイスについて何も知らない

一般的に縁の中央に近づくほどソフトウェアのレベルが上がり、縁の外側は仕組みで内側は方針となっています。

縁の内側は外側について何も知らず、外側で宣言された関数やクラス、変数などは内側にあるコードで触れてはならず、依存性は外から中だけに向かっていきます。

 

Clean ArchitectureでAPIを実装する

今回はToDoアプリのAPIをClean Architectureで実装しました。

具体的にはTaskテーブルを作成し、そこにToDoのデータが格納され、一覧や詳細、登録などをこのAPIで実現できるようになっています。

中身はシンプルなCRUDで、どの処理をどこに書くかを意識して実装しました。また、ドメイン層ではDDDを意識した作りにしました。といっても今回はドメインがTaskしかないため振る舞いはほどんどなく、簡易的なものになっています。

 

Enterprise Business Rules(企業のビジネスルール)

このレイヤーにはアプリケーション全体の最重要ビジネスルールをカプセル化したドメインを格納しています。

今回はToDoアプリを想定したAPIとなっており、ビジネスロジックは更新時のみで、データ構造とアクセサメソッドのみ定義しています。

またDDDを意識した実装にし、それぞれのプロパティを値オブジェクトとして定義しています。

コンストラクタの引数が関数内で変更されないため、readonlyプロパティをつけています。こうすることで、引数が読み込み専用で変更されないことが担保されます。

・Domain

 

Application Business Rules(アプリケーションのビジネスルール)

このレイヤーにはアプリケーション固有のビジネスルールが含まれ、入出力するデータの流れを調整します。

このレイヤーの変更がドメインに影響を与えることはなく、またこのレイヤーがDBやUI、共通のフレームワークなどの外部の変更の影響を受けることもありません。

ここでは依存関係を中心に向かうようにしたいため、依存関係逆転の原則(DIP)を使ってPortにインターフェイスを定義しています。そうすることでUseCaseがPortというインターフェイスに依存することになり、Portのインスタンス化をGatewayで行うようにしています。

具体的にはコンストラクタでTaskGatewayをTaskPortとして受け取ることで、インターフェイスを継承しています。

・UseCase

 

・Port

 

Interface Adaptors(インターフェイスアダプター)

このレイヤーではドメインやユースケースに便利な形式から、永続フレームワーク(DB)に便利な形式にデータを変換します。

円の内側のコードは、DBについて何も知らず、DBがSQLであれば全てのSQLはこのレイヤーに限定する必要があります。

ここではPortの具象クラスであるGatewayとResourceを格納しています。そのため、GatewayではPortのインターフェイスを継承しています。

GatewayではconvertTaskメソッドでSQLから返ってきたEntityをDomainに変換する処理をしています。

ResourceのserializeTaskメソッドでTaskドメインからAPIのレスポンスとして返したい形に変換しています。

 

・Gateway

 

・Resource

Frameworks & Drivers(フレームワークとドライバ)

最も外側の円は、フレームワークやツールで構成されています。例えばDBやWebフレームワークなどがこのレイヤーに格納されています。RouterやDriverもこのレイヤーに格納しています。

Tasksテーブルのカラムであるcreated_atとupdated_atはdefaultValueを「CURRENT_TIMESTAMP」、「CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP」にして自動的に登録されるようにしています。

statusもdefaultValueを1にすることでcreateではデフォルトで登録できるようにしています。

・Driver

 

Routerではexpressのrouterを使ってルーティング設定をしています。

またRouterで各層の依存関係を定義(DI)することで、それぞれレイヤーで利用可能な状態にします。

・Router

 

Serverではexpressを使ってサーバー処理を書いています。routerのパスや待ち受けるポート番号などを設定しています。

また、CORSで「http://localhost:3000」のみアクセスできるようにしています。

・Server

 

・IDBConnection

 

・MysqlConnection