Slide 1

Slide 1 text

TypeScriptでモジュラーモノリスやっ てみた クラスメソッド株式会社 リテールアプリ共創部 マッハチーム morimorikochan

Slide 2

Slide 2 text

名前 morimorikochan 所属 クラスメソッド株式会社    リテールアプリ共創部    マッハチーム🚀 趣味 Switchのゲーム X @marooon88 ⾃⼰紹介

Slide 3

Slide 3 text

主な事業 ● AWSなんでも(各クラウドなんでも) ● 受託開発、⾃社開発、データ分析関連 ● 基本全部直案件 クラウドみたいな先端技術に強いIT企業 ● ZennやDevelopersIOを運営してます 3 クラスメソッドとは?

Slide 4

Slide 4 text

4 マッハチームとは

Slide 5

Slide 5 text

● 単⼀のデプロイ可能なシステム(モノリス)に、ドメイ ンで分割されたモジュールが複数存在している状態 ● モノリスとマイクロサービスの中間 🐿モジュラーモノリスとは? 出典: Modular Monoliths • Simon Brown • GOTO 2018

Slide 6

Slide 6 text

典型的なレイヤードアーキテクチャはこんな感じ 技術的関⼼によって 分割されてる Controller 層 🐿モジュラーモノリスとは? Order Controller Service 層 Order Service Repository 層 Order Repository User Controller User Service User Repository Company Controller Company Service Company Repository 参考: Mark Richards Neal Ford 著 島⽥ 浩⼆ 訳 ソフトウェアアーキテクチャの基礎

Slide 7

Slide 7 text

モジュラーモノリスはこんな感じ ドメインによって分割されてる Order ドメイン 🐿モジュラーモノリスとは? Order Controller Order Service Order Repository User ドメイン User Controller User Service User Repository Company ドメ イン Company Controller Company Service Company Repository 参考: Mark Richards Neal Ford 著 島⽥ 浩⼆ 訳 ソフトウェアアーキテクチャの基礎

Slide 8

Slide 8 text

🤨なんで新規PJで採⽤した? 技術的関⼼で分離されたレイヤードアーキテクチャを使っていた らつらみが... src/ handler/ ...いろんなファイル use-case/ ...いろんなファイル repository/ ...いろんなファイル domain/ ...いろんなファイル

Slide 9

Slide 9 text

つらみ1. ファイル多すぎ問題 システムで扱うドメインの範囲が 広がっていくと、技術で分割され たディレクトリ配下のファイルが 肥⼤化する ● 例えば特定のControllerに関 連するRepositoryをたどりに くい ○ 複数のディレクトリをま たぐので 🤨なんで新規PJで採⽤した? repository/ company-repository user-repository notification-repository coupon-repository coupon-issue-repository campaign-repository campaign-entry-repository order-repository reservation-repository reservation-cancel-repository これに加えてテストコードも...

Slide 10

Slide 10 text

つらみ2. レビューしにくい プルリクエストのdiffが各ディレクトリに散らばってレビューがしにくい 1つのプルリクエストに含まれるのは特定の技術層のコードではなく特定の ドメインのコード(アジャイル開発だと尚更そう) 🤨なんで新規PJで採⽤した? Controller 層 Order Controller User Controller Company Controller Company ドメ イン Company Controller Company こっちじゃなくて こっち

Slide 11

Slide 11 text

特定の機能だけ独⾃のディレクトリ構成にしたいが、レイヤーの原則を守る ために悩んでしまう‧無理な構成になる (1つのドメインがどんどん複雑になりやすく、こういった機会が多い案件 つらみ3. ⼀部だけ特例的な構成にしにくい 🤨なんで新規PJで採⽤した? Service 層 Order Service Repository 層 Order Repository User Service User Repository Company Service Company Repository 出典: Mark Richards Neal Ford 著 島⽥ 浩⼆ 訳 ソフトウェアアーキテクチャの基礎 Order Quotation Calculator ここでいい んかな❓

Slide 12

Slide 12 text

👀なんでモジュラーモノリス ● つらみを解決できるから ○ レビューしやすい点 ○ モジュール内で特例的な 実装を取りやすい点 ● モノリスと違ってDynamoDB と相性が良い ● もしマイクロサービスにする と、開発チームは1つのみなの でチーム単位とサービス単位 が合わない Order ドメイン Order Controller Order Service Order Repository User ドメイン User Controller User Service User Repository Company ドメ イン Company Controller Company Service Company Repository 出典: Mark Richards Neal Ford 著 島⽥ 浩⼆ 訳 ソフトウェアアーキテク

Slide 13

Slide 13 text

📦具体的にどんなの? ● 階層構造のモジュラーモノリス ○ 特定のモジュールが肥⼤化した場合に分割しやすいように ○ Djangoを参考に ○ 1つのモジュール内はController→Service→Repositoryで構成 ■ +DI(Inversify) ● serverless-expressで記述されていて、AWS Lambda上で動作 (Lambdalith) ● Nest.jsは使ってない ○ Lambdaのコールドスタート時に時間がかかる ● データベースはDynamoDB ○ RDBと⽐較すると割とテーブルごとに独⽴している ○ モジュールを跨いだテーブル同⼠の依存が少なく、モジュラーモノ リスの弱点をカバー

Slide 14

Slide 14 text

order/ // モジュール handler/ api-user.ts // エンドユーザーのルーティング api-admin.ts // 管理者のルーティング batch.ts // バッチ実⾏エントリーポイント service/ create-order.ts export-orders.ts order-repository.ts order.ts 📦具体的にどんなの? こんな感じ→

Slide 15

Slide 15 text

order/ // モジュール handler/ api-user.ts // エンドユーザーのルーティング api-admin.ts // 管理者のルーティング batch.ts // バッチ実⾏エントリーポイント service/ create-order.ts export-orders.ts order-repository.ts order.ts order-quotation-calculator/ // ⼦モジュール service/ calculate-quotation.ts quotation.ts 📦具体的にどんなの? こんな感じ→

Slide 16

Slide 16 text

💪どうだった? ● 実は運⽤して1ヶ⽉も経ってない ● 構築して思ったこと ○ つらみは解消できそう ○ 実装時に⼀定の設計スキルが必要になる ■ 思考停⽌で実装できるものではない。 ■ モジュールに切り出すか常に⾒極める必要がある ■ その点では技術ベースのレイヤードアーキテクチャは楽 ○ モジュール内はある程度散らかっていても保守性への影響は少ない ○ モジュールの境界をちゃんと決めないとモジュール間の依存関係が 複雑になって保守性が⼤きく下がりそう... ■ Shopifyのブログでも強く⾔及されている

Slide 17

Slide 17 text

🤬課題: モジュールの境界をどう定義し制約するか TypeScriptは“export”されると どこからでも呼び出しができる →放置すると他のモジュールか らでも無秩序にexportできる (例: OrderRepositoryから UserServiceを呼び出す) ⾃動で‧できるだけ早く検知し たい Order ドメイン Order Controller Order Service Order Repository User ドメイン User Controller User Service User Repository Company ドメ イン Company Controller Company Service Company Repository

Slide 18

Slide 18 text

🤬課題: モジュールの境界をどう定義し制約するか Linterを使う ● eslintを使っていれば no-restricted-pathsが使え ない🥺 ● biomeだと useImportRestrictionsが 使える! Order ドメイン Order Controller Order Service Order Repository User ドメイン User Controller User Service User Repository Company ドメ イン Company Controller Company Service Company Repository

Slide 19

Slide 19 text

🤬課題: モジュールの境界をどう定義し制約するか // 親ディレクトリ のファイル はなんでも importできる import { exportedValue as e1 } from "./../exportedValue"; // 同じディレクトリのファイルはなんでも importできる import { exportedValue as e2 } from "./exportedValue"; // 子ディレクトリは index.tsだけ import { exportedValue as e3 } from "./fuga"; import { exportedValue as e4 } from "./fuga/exportedValue"; //ERROR

Slide 20

Slide 20 text

● レイヤードアーキテクチャ(技術ベース)のつらみは解消できそ う ● 課題もある ○ 実装時に⼀定の設計スキルが必要になる ○ モジュールの境界をどう定義し制約するか ■ biomeだとuseImportRestrictions が今の所良さそう ● 感想 ○ 何年後かに答え合わせでまた登壇できるといいな 📚結論‧感想

Slide 21

Slide 21 text

Rustについて興味ある⼈いませんか...? サーバーサイドTypeScriptについて興味ある⼈いませんか...? 🫠宣伝

Slide 22

Slide 22 text

ありがとうございました 🫠おわり