DDDを志して3年経ったら「DDDの皮を被ったクリーンアーキテクチャ」になった話
【DDDを志して3年経ったら「DDDの皮を被ったクリーンアーキテクチャ」になった話】というタイトルでDevelopers Summit 2024夏に登壇しました。
▼登壇資料
本記事では「登壇内容」、「登壇した経緯」、「登壇内容への自分自身の振り返り」の順でまとめていきます。すでに登壇を聞いていただいたり資料を見ていただいた方は、後半の章から読んで頂けますと幸いです。
登壇内容
どんな発表?
よくある取り組みや設計ルールに対して
- 「無視していること」
「遵守していること」
「バランスを取っていること」
をそれぞれ事業・組織・プロダクト特性などの観点から説明します。
- つまり
- アーキテクチャ論でよく聞く「要はバランス」の具体例を徹底的に解説します!
私がアーキテクチャを学んだ経緯
CTOが「要はバランス」と言いたくなるアーキテクチャ論を、スタートアップの立ち上げを通して学んできた経緯を話しつつ、最終的に辿り着いた設計思想に話をつなげていきます。
ざっくり経歴
- 2016年〜2019年:新卒入社して3年間
- フロントエンド大好きな普通のWebエンジニア
- 2019年:CTO転職&DDDとの出会い
- 設計が分からなさすぎて一瞬で可読性ゼロのコードを量産&DDDを学びトライアンドエラー
- 2020年:ピボットを経て起死回生
- 事業が上手くいかなくて会社が潰れかけるが、オンライン家庭教師サービスで起死回生
- 2024年 設計水準を一定保ちながら事業成長を続けている
- 4月入社のEMから「DDDの皮を被ったクリーンアーキテクチャですねコレ」と評される
2016年〜2019年:新卒入社して3年間
- フロントエンド好きのフルスタックエンジニア
- Webフロントエンドが気に入って「開眼!JavaScript」などを読む
- 一方で業務はバックエンド・インフラが多め
- 転機:社内新規事業への挑戦
- 社内新規事業提案が採択されたので、プロトタイプ開発とスーツ着て営業を1年ほどやった
- 売上も全然立たなかったし、感想としてはCEOと開発両方やるの大変すぎやろと思った
2019年:転職&DDDとの出会い
- スタートアップCTOに転職
- もともと教育サービスに関わりたかったので、CTOとしてスカウトを受けて素直に転職した
- 技術力なさ過ぎ問題
- フルスタックだと思っていたが、自分1人でサービス立ち上げとなると実力不足すぎた
- 知識を増やそう増やそうとしているうちに、「ダメコード」が量産された
- DDDとの出会い
- 社運が掛かった有料課金機能の実装時に、自分の実装が信用できなさすぎて、設計手法の学習とテストコードの拡充を始める。形だけの実装パターンの踏襲(戦術DDD)を始める
学んで速攻で実践&記事化
CTOになって半年。ORM依存の設計でバグを起こしまくったので独学で改善した知見を詰め込んだ。結果として300Like超をいただきモチベーションになる。
2020年:ピボットを経て起死回生
- 創業事業が伸びずクローズ。新たにオンライン家庭教師事業を立ち上げ
- 具体的には”来月CTOの給料出せないかも”くらい追い詰められた(2020年頭)
- コロナ禍を迎えており、オンライン家庭教師事業を立案することで資金調達して乗り切る
- 当時の私の感情
- 事業が終わった原因は何?ダメコードによるバグが足を引っ張ったかもしれないし、社運が掛かった事業で呑気にDDDにチャレンジしていたからかもしれないし、韓国から競合が参入してきたせいかもしれないし、最初のコンセプトから終わっていた可能性だってある
- 分からんからこそ「銀の弾丸なんて無い。事業の状況、プロダクトの特性、起きている問題を直視して、そのときベストだといえる判断をしていくしかない」と思えた
立ち上げた事業紹介
- オンライン家庭教師マナリンク
- オンライン家庭教師が探せるメディア(2024年現在、先生600人)
- 授業・売上・宿題等の管理ができるSaaS
- 『メディア的な機能とSaaS的な機能の両方が大事』
- Web(Next.js×Laravel)、アプリ(React Native)
カオスなオンライン家庭教師事業立ち上げの中で
- 当時のマナリンク開発の客観的な状況
- 業務委託エンジニアが離れてしまったのでエンジニアは自分含め3〜4名
- 幸いにもリリース後数ヶ月で売上が立つようになったが、比例して改善が無限に発生。平行してスマホアプリの開発、旧サービスからの一部機能の移行、開発環境整備など。
- 割り切り
- 大好きなフロントエンド領域は、もうダメコード量産でOK!
- 今後事業を支えていくバックエンド(DB、インフラ含む)に注力する!
- インフラナンモワカラン中でコンテナベースに移行(今後数年支えるから完遂。今も生きている)
- バックエンドをDDD原理主義から適度にカスタマイズ。事業特性と重なるラインを模索
2020年当時のTwitterや技術記事のDDD界隈の印象※個人の感想です
- 「DDDはこうあるべき」VS「そんなんこだわっている暇あったら機能作れ」バトル
- 「DDDに従っていないとまともなプロダクトは作れない的な勘違いや強迫観念」
- 「フロントエンドでドメイン層?何言っているの?」「スタートアップでDDDは愚策」といったコメ
- 「一周回って戦術DDDでもやらないよりはマシ」「戦術DDDはアンチパターン」派の衝突
総じて、”私はこの事業でこの組織でこういう風に”「DDDをやったら上手くいきました」の””内が割愛されて広まり、誤解されている印象を受けた。
発信内容を素直に受け取ればタメになることを言っているのに、前提を誤解されるケースが多そう。
原理主義的なネットの発信に辟易した結果...
という煽りタイトルの記事が爆誕した
「削除しやすい設計」の例
- ”とにかくわかりやすく”単一責任原則を守れる工夫
- ControllerやUseCaseは単一のpublic methodを持つ
- 「DDDっぽいよね!」とか「クリーンアーキテクチャっぽいよね!」を重視しない
- あくまで「削除しやすいこと = 機能が独立して実装されていること」を重視
- 極端な話、「この処理どこに置いて良いか分からなかったので全部Entityに入れちゃいました」よりも「原理主義的なDomainServiceとは異なりますがとりあえずDomainServiceという命名で切っておきました」のほうが大事と判断
事業・組織・プロダクトを加味した実際の設計思想を解説
以下考えてみましょう
- なぜ弊社ではDDD・クリーンアーキテクチャを遵守しなかったのか?
- なぜ「削除しやすい設計」を心がけたら一旦良いか!に至ったのか?
観点
- どんなプロダクトを開発しているか?
- ドメインの特徴は何か?
- どんな規模・価値観・スキルの組織か?
- 技術選定は影響しているか?
どんなプロダクトを開発しているか?
- オンライン家庭教師マナリンク
- 『メディア的な先生検索機能とSaaS的な授業管理機能の両方が大事』
- 設計思想への影響
- メディア側は色んな先生検索・表示機能がありReadのロジックが複雑
- SaaS側は授業記録や売上管理がメインで、Writeのロジックが複雑
- 立ち上げ初期からマイクロサービスとかやりたくなかったのでモノリスで通したい
- 機能群ごとにディレクトリを切り、それぞれDDDやシンプルな設計を使い分ける
- CQRSパターンの徹底・EntityをRead Modelにしない
①機能群ごとにディレクトリ
├── src/app/Domain
│ ├── Calendar # 授業予定管理機能なのでDDDに寄せる
│ ├── ChatRoom # ただのチャットなのでシンプルに
│ ├── Learning # 宿題などの学習記録管理なのでDDDに寄せる
│ ├── Notification # 通知機能なのでシンプルに
│ ├── OnlineTeaching # オンライン授業機能なのでDDDに寄せる
│ ├── TeacherSearch # 先生検索機能なのでシンプルに
...
- DDDの境界づけられたコンテキストやフロントエンドのPackage by Featureに近い
- それぞれの機能群で互いに参照したいケースは、ひねらずに愚直に実装
- ※実践DDDなどを読んでいると、コンテキストを跨ぐときの設計手法もいろいろあるようだが、ビジネス的に境界をまたぐことが少ないこと、全エンジニアが全機能を開発するフェーズであることなどから、シンプルに参照してもスパゲティになりにくいと判断
②CQRSパターン・EntityをRead Modelにしない
- 事業特性
- オンライン指導の透明化をミッションにしているから、想定外のReadが後から増えがち
- SQLけっこう速くないとSEO・CVRに影響するから富豪的にSQLをたたけない
- 課題
- EntityにViewのための無駄なGetterが増えてただのDTOになる
- CQRSパターンの徹底
- EntityをGet時に使わない。更新のための既存データのGetには使う
-
app/Domain/Hoge/UseCase/Query
以下に特化型のClassが大量に生える
Queryの例
先生が見るカレンダーに表示されるデータ用のクエリ。授業予定もプライベートな予定も両方表示できる数少ない画面なので専用のQueryを用意。Query実装時は必ず唯一のpublicメソッドhandle
を持つことで多目的に使われないようにする。
class MySQLGetCalendarQuery implements GetCalendarQueryInterface
{
public function __construct(
private HogeQueryInterface $hogeQuery, private FugaQueryInterface $fugaQuery
) {}
/**
* 中略
*/
public function handle(int $teacherId, Carbon $startAt, Carbon $endAt): array
{
$lessonSchedules = $this->hogeQuery->handle($teacherId, $startAt, $endAt);
$hogeSchedules = $this->fugaQuery>handle($teacherId, $startAt, $endAt);
return [...$lessonSchedules, ...$hogeSchedules];
}
}
ドメインの特徴は何か?
- マナリンクは「ドメイン作りながら設計」
- オンライン家庭教師はコロナ禍以降メジャーになった新しい事業領域
- 仕様を決める際に参考になる法律や事例が無いし、ユーザー(先生・ご家庭)も”答え”を知らない
- さながら「ドメイン駆動設計」ではなく「ドメイン作りながら設計」
- 設計思想への影響
- ちゃんとビジネス側の”確度”を汲み取って設計レベルに反映する
- 「事業価値のある機能ならいずれリファクタの機会がある」というポジティブな諦め
「ドメイン作りながら設計」だと何が起きるか?
- 儲かるかわからないけどきっと価値があるから作る
- 受託やSaaSとかだと受注ベース・ニーズベースで開発するから”予算”が見えやすい
- マナリンクでは”オンライン家庭教師はこうあるべき”というリーダーシップで開発する。だから社内での議論を通して設計レベルを見出す
- 意義のある機能はどうせ改善が入る
- 初期リリースで”当たり”を引くことは少ない。利用率・内容・売上貢献などを見て即改善
- 「最初はこんな設計で良いだろ。後からどうせリファクタの機会あるよ」が本当に起きる
- "機能群ごとにディレクトリ"作戦がめっちゃ刺さる
どんな規模・価値観・スキルの組織か?
- 全エンジニア3〜5名(2024年現在、5名)
- 意思決定は爆速
- 学習意欲は高いが、手段の目的化はしないメンバー
- 「先日学んだデザインパターンをとにかく試したい」といった手段の目的化はしない(信頼)
- だからこそ、too muchに見える設計を提案されても、CTOはそれを尊重する
- 『ハードルを下げるのは技術選定の役目、ハードルを越えるのは各メンバーの役目』
Q. とはいえ、どうやってチームの設計レベルを底上げしているのか?
開発チームにどうやって設計を学ばせているか①
- 設計は”身体性”のある学び
- ”暗記系”や”一度理解したらOK”の学びでは無く、”体で覚える”ように学ぶ
- 自転車の乗り方🚲を言語化できないように、本質的には言語化&伝達できない
- 例:実装パターンだけ先に覚えてしまい、違う、そこじゃないってタイミングで使われる
- ”身体性”のある学びを進める方法
- トライアンドエラーの数を増やす(たくさん乗る)
- 失敗を失敗と自己理解させる(転けたら痛い)
- テックリード自ら行動で語る(大人が自転車に乗る&転けたりする)
開発チームにどうやって設計を学ばせているか②
- トライアンドエラーの数を増やす
- 1リリースに対して1つの設計しか検討しないより、5つ検討する方が5倍学べる
- 開発メンバーからの壁打ち、相談、書籍を読んでの質問などに徹底的に対応※出社制
- 失敗を失敗と自己理解させる
- 『機能ごとにディレクトリ』指針により、微妙な設計でもレビューを通せることがある
- できるだけ『開発者自身に2次リリース、3次リリースを主導』させ、失敗を認知させる
- テックリード自ら行動で語る
- 「要はバランス」って言う前にCTOが誰よりも設計関連の書籍を読む&実践
- 設計失敗談や、ボツになった案を社内勉強会でメンバーに語る
技術選定は影響しているか?
- 立ち上げ初期からフロントエンドはVue or React、バックエンドはLaravel
- 設計が苦手なメンバーはしばらくフロントエンド中心にアサインするなど工夫可
- PHPの言語特性
- PHP7.4や8.0で型周り、OOP周りで相当な進化を遂げたが、一方で限界もある
- LaravelのDIコンテナが超優秀
- ほどほどにDDDやクリーンアーキテクチャを取り入れつつ、作り込むも手抜きするも自由
- 良くも悪くも言語自体は簡単なので、より本質的な問題解決や設計に集中できる
言語自体が難しかったり罠が多かったら、設計頑張る余裕がなくなりそうなので一長一短ですね
設計思想に至った背景まとめ
- プロダクト特性から、臨機応変に設計レベルを決めれることが合理的
- ドメインが未確定かつ磨くべき領域のため、リファクタありきで設計可能
- 小規模組織なので、世間であまり見かけない柔軟な意思決定を求めることが可能
- 開発メンバーが学習意欲旺盛かつ手段の目的化はしない
- PHPやTypeScriptといった程々に枯れた技術選定で設計に集中しやすい
- CTOが技術大好き&プロダクト開発大好き&事業クローズ経験者という背景
以上が登壇内容です。続いて、登壇に至るまでの流れ(きっかけや練習など)について書いていきます。
登壇した経緯や、登壇当日の感想
登壇した経緯
今回デブサミはスポンサー等ではなく、公募での登壇でした。弊社の広報の方が、「デブサミの公募受付やっています。今日までです」とある日突然教えてくれまして、じゃあ応募してみるかと以下のような文章を添えて応募しました。
2020年頃、スタートアップの立ち上げにあたって、カオスなコードにしないようにしつつ整頓された開発基盤を目指しDDDを導入しました。
それから3年以上、事業の特性や状況、チームなどを加味してやること・やらないことを選んで改善してきた結果、今年入社したEMから「これはDDDの皮を被ったクリーンアーキテクチャですね」と言われました。
本LTでは、DDDの原理原則からどのように種々の状況を加味してカスタマイズし、現在に至っているか解説します。原理原則と自分の状況の狭間で苦しんでいる方々に聞いていただきたいです。
どうして登壇する気になったのか?
そもそも私は技術記事を累計100以上書いているし、技術イベント登壇も年に数回はこなしているのですが、大型カンファレンスへの登壇はおろか、参加もしたことがほとんどありませんでした。
理由はシンプルで、「技術イベントは、登壇することで意味がある。登壇せずに参加だけだと、楽しいなという気持ちで終わってしまう」という危機感が個人的に強くて、かつ大きなカンファレンスの登壇機会なんてなかなか頂けない(普通に落ちる)ので、参加していなかったという感じです。
今回も、正直公募が通らなければデブサミへの参加すらしなかったかもしれませんが、幸いにも選考を通していただいたので、登壇することになりました。
登壇の練習や準備
当然ながら30分の登壇予定、しかも100人以上の方を前にして、となると、割と人生で見てもほとんどない経験になるので、練習はかなりしないといけない...と思いきや、意外にも通しでの発表練習は1回しかしませんでした。
日頃から5分〜10分程度の登壇機会はたくさん頂いているし、あとそれに慣れているおかげで、タイマーさえ見ながらであれば、割と発表しながらペースを調整したり、聴講者の反応(頷き方、ウケ具合など)を見ながら内容を適宜調整できることも自分の実績として持っていたので、一度オフィスでエンジニアの同僚を前に発表してみると、タイマーを確認しながらではあるもののほぼ30分ぴったりでいけたので、本番は多少早口になることを加味するとむしろ少し余裕ができるくらいでちょうど良さそうである、という感じでした。
もちろんもっと内容をシビアに見られるような発表であれば異なるとは思いますが、技術カンファレンスの登壇であれば、自分の登壇経験を横展開できるという自信に繋がったのは貴重な経験でした。
また、資料の作成はそれなりに時間を掛けたものの、当該資料はslidevのオリジナルお手製テーマを使うことで、簡単に余白やデザインが統一されたスライドになるようにしていて、余計な思考をせずにサクサク作れました。
登壇の感想
登壇して話しているときは↓の感じでした。最前列に知人のエンジニアが構えていただいて撮影していただきました。
細かいですが、弊社エンジニアは関係者席に案内されたのですが、関係者席が最後方だったのでロクな写真が撮れなかったです。危うく何も画像が残らないところでした。危ない危ない。
肝心の登壇ですが、時間も30分以内にぴったり収まったのがまずは良かったです。何なら最後一緒に登壇したKondoさんに話を雑に振ったりする余裕もありました。
また、個人的には冒頭の自己紹介近辺でちょっと冗談を交えても会場のウケが悪く、なんかスベり気味になっていたのですが、Kondoさんが自己紹介直後で「弊社への入社理由は設計に対する音楽性の一致🎤です」と言ったあたりで僕が「別に僕DDDで音楽を奏でた記憶ないんですけどね〜」といったコメントを首かしげながらアドリブで入れてみたら、ちょっと会場がウケてくれて、なんとなくこの発表は若干笑いながら聞いてもいいやつだ、みたいな空気感を作れたので、そこはファインプレーだったかなと思っています(正確にはアドリブじゃなくて、前日の夜にお風呂に浸かりながら「当日もし空気が硬かったとき用に序盤の終わりくらいに何個か捻った発言を差し込めるといいかもしれん」と思って考えておいたフレーズの1つだったので、お風呂は神です)。
登壇した内容に対する自分自身の振り返り
本登壇では、「アーキテクチャ論って、よく【要はバランス】に落ち着いているけど、ちゃんと丁寧に背景から説明すればバランスの具体的な話もできるはず」という命題へのチャレンジでした。
資料中にも似たようなことを書いていますが、アーキテクチャに関する発信って、会社とか組織の状況が前提に立っているがそこを割愛してしまって具体の手法だけが出回るがゆえに、誤解や強迫観念が生まれやすいというのが私の持論です。そこで、自社の歴史とか組織の状況を踏まえつつ、あえて原理主義から外したところも書いていくことで、登壇を聞いた人が今後周囲に振り回されず、自分の状況に目を向けて意思決定できるようにという考えがありました。
これは別の機会にまとめようかなとも思うのですが、そもそもアーキテクチャに限らず、「技術記事を読む」とか「登壇を聴く」を通して自分の学びにするって想像以上に難しいことなのです。私自身がこれまで大量に発信してきたので特にそう感じるのですが、文字という限られた情報量だけで、顔も知らない背景も知らない誰かに技術的な知見を届けるというのは難しすぎるし、その文字情報を見ただけで「わかった気」になってしまうのも、技術発信を聴くという行為の課題だなと思っています。
私が個人的に気をつけていることとして、「記事の執筆者や登壇者が、この内容を文字に起こす過程で欠落した情報量は何か?に思いを馳せる」というのがあります。わかりやすい例で言えば具体的な会社内での施策の情報や、ドメインロジックの仔細、開発チームの技術レベル、日々の情報収集の仕方やソース、技術選定当時の世論など、欠落した情報は無限にあるはずで、そこを多少なりとも想像することで、発信内容をそのまま受け取ること無く、自分の状況に即して学びに置換できるものとして心がけています。その1つのコツみたいなものが今回の登壇を通して伝えられていたら良かったかなとも思っています。
登壇後、Ask the Speakerというコーナーにて聴講者の方から質問を受けましたが、好意的な反応や、回答していて面白い質問が多く、登壇内容が一定受け入れられたことが感じられてよかったです。
宣伝
最後にどさくさに紛れて宣伝です。
DDDとかクリーンアーキテクチャとか言う前に、アーキテクチャの基礎を学びたい方向けにLaravelベースの教材を書いています。わざとFat Controllerにしているサンプルコードをリファクタリングしていく形式の教材です。ご自身や周囲にこれからアーキテクチャやっていこうという方がいましたら、ぜひおすすめしてください!
オンライン家庭教師マナリンクを運営するスタートアップNoSchoolのテックブログです。 manalink.jp/ 創業以来年次200%前後で売上成長しつつ、技術面・組織面での課題に日々向き合っています。 カジュアル面談はこちら! forms.gle/fGAk3vDqKv4Dg2MN7
Discussion