LoginSignup
1
2

More than 1 year has passed since last update.

Clean ArchitectureをTypeScriptで実装し、RDBとDocumentDBを差し替え可能にしてみる

Last updated at Posted at 2021-05-16

この投稿は http://kazukimuta.com/blog/2021/05/16/try_clean_architecture のクロス投稿記事です

1. 背景

Clean Architectureについて学びたく、TypeScriptでClean ArchitectureなWEBサーバーを実装してみました。
なお、本記事はこちらのスペースマーケット社の記事を大いに参考にしています。ありがとうございます。

2. つくったもの

TypeScript x ExpressのWEBサーバー。クライアントは実装していません。
DBは(システム開発の現場ではそうそうないでしょうか)RDB(MySQL)とDocumentDB(MongoDB)を差し替え可能なようにしてみました。

ソースコードこちら

3. 構成

クラス構成は以下のような形です。
※ ところどころ端折っています。クラス図としての誤りなどあれば、ご指摘ください。。

CleanArchitecture.png

Clean Architectureの図とは対応づけは以下のような形です。

  • Framework&Drivers
    • server : Expressのエントリポイント
    • router : Expressのrouter
    • MongoConnection, MySQLConnection : DBコネクタの実態
  • Interface Adaptor
    • RDSTaskRepository, NoSQLTaskRepository : DBコネクタを操作する実態
    • TaskController : いわゆるコントローラ(MVCのC)。ただしExpressのrouter実態とは切り離している
    • IRDBConnection, INoSQLDBConnection : 各種DCConnectionのインターフェース
  • Application Business Rules
    • GetTask,, : データモデルに対してCRUD操作を実施するサービスクラス。ほかにも色々なクラスがあるが記載割愛
    • ITaskRepository : TaskRepositoryのインターフェース。上記サービスクラスは、このインターフェース越しにCRUD操作する
  • Enterprise Business Rules
    • Task : データモデル.今回はタスク管理サービスをイメージしたもの

CleanArchitectureImg.jpeg

4. 所管

4.1. Dependency Injection(DI)の効用

GetTaskなどのサービスクラスが直接DBオブジェクトを操作するのでなく、ITaskRepository経由でDBを操作するため、RDBであれDocumentDBであれ、ITaskRepositoryインターフェースをもつ操作オブジェクトを実装すれば差し替え可能です。

また、DBConnectionについてもインターフェースを挟んでいることで、例えばテスト実行時にはモックに差し替えるといった切り替えをすることができます。

インターフェースに依存させることで、テスト容易性や移植性の向上に大いに寄与するイメージがわきました。

4.2. DIのとき、どこでインスタンスを作るべきかになやむ

DIは「依存性の注入」と略されるだけあって、クラス外部からそのクラスが依存するオブジェクトを渡していくパターンです。
従って、クラスの外側で、中に渡すためのインスタンス生成をする必要があります。

DBコネクタ周りのインスタンス生成では、router.tsのなかで以下のようにインスタンス生成をしていますが、このクラスが担うべきタスクでは無いようにも思います。

# src/infrasctucture/router.ts

function getTaskRepository(): ITaskRepository {
  dotenv.config();
  switch (process.env.DB_TYPE) {
    case "mongo":
      const mongodbConnection = new MongodbConnection();
      mongodbConnection.connect().then(() => {
        console.log("ok");
      });
      return new NoSQLTaskRepository(mongodbConnection);

    case "mysql":
    default:
      const mysqlConnection = new MysqlConnection();
      return new TaskRepository(mysqlConnection);
  }
}

const tasksController = new TasksController(getTaskRepository());

let router = express.Router();

router.get("/tasks", async (req: express.Request, res: express.Response) => {
  let results = await tasksController.findAllTasks(req, res);
  res.send(results);
});


また、インスタンス生成する場所が曖昧であるがゆえに、インスタンス生成の実装場所が散らばる懸念もあります。

これらの課題を解消する手段がDIコンテナで、TypeScriptの場合TSyringeInversifyJSawilixなどがあるようです。
このあたり、追って利用してみたいと思います。

4.3. Application Business RulesとEnterprise Business Rulesの違い

正直、両者の違いがイマイチ理解できておらず。勉強が必要です。。

5. まとめ

Dependency Injectionパターンがよい!
Clean Architectureはもう少し勉強が必要

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2