アソビューのDDD×CQRS環境に飛び込んで直面した壁と工夫したこと

はじめに

アソビュー - Qiita Advent Calendar 2025 - Qiitaの15日目(裏面)です。 アソビューでバックエンドエンジニアをしております、河野です。

私はエンジニアとしては3年目で、今年の6月にアソビューに入社してから半年が経過し、今回初めてテックブログを書くことになりました。

本日は私がアソビューに入社してからの間に実務を通して1番苦労したと感じる、バックエンドのアーキテクチャ理解について紹介します。

前職でも Java/Spring Boot による開発経験はありましたが、アーキテクチャ設計を強く意識する文化はなく、ドメインモデル(らしきもの)はあるが手続き型の実装が多い環境でした。 そのため、アソビューで採用されているDDD(ドメイン駆動設計)とCQRS(コマンド・クエリ責務分離)を念頭においた設計方針をなかなか理解することができず、入社当初は苦手意識がありました。

本記事では、入社後に直面した課題と、その理解がどのように進んでいったか下記のポイントを中心に記録としてまとめます。

  • 入社直後に苦労したアーキテクチャ理解(DDD/CQRS)
  • 実装経験の中で特に難しかった点
  • レビューや学習を通じて理解が深まった過程
  • 半年経過した現在の視点と変化

技術的な解説というよりも、若手エンジニアが新しいアーキテクチャに適応していく過程で苦労したこと工夫したことを整理した内容となります。


背景

アソビューのバックエンドでは、Java / Spring Boot を使用して、DDD の考え方とCQRSを取り入れたレイヤードアーキテクチャを採用しています。

  • Domain層
  • Application(Service)層
  • Infrastructure(DataSource)層
  • UI(Controller / gRPC)層

これらの責務が明確に分離され、各層に適切な役割をもたせることが求められています。

一方、入社以前の私は、

  • DDD:書籍を読んで概念を知っている程度
  • CQRS:名称も知らない状態
  • レイヤードアーキテクチャ:実務で扱った経験がある

という状況でした。


余談

エンジニア1年目に読んだ「現場で役立つシステム設計の原則」(増田亨氏著)が印象に残っていたのですが、後に増田氏がアソビューのDDDを監修していたことを知り、実務で本格的なDDDに触れる環境に来たのだと実感しました。


最初の開発タスクでつまずいたポイント

入社後、最初に担当したのはランキングメタ情報の更新機能でした。

社内担当者が使用する機能で実装するのは参照APIと更新API、そしてAPIを呼び出す画面の追加くらいなので、そこまで重たいタスクではないかなと思っていましたが業務知識もアーキテクチャ理解もまだ不十分な段階での取り組みとなり、多くの課題に直面しました。

実装を進める中で特に困ったポイントは以下の通りです。


コードリーディングに時間がかかる

アソビューのバックエンドはマイクロサービスアーキテクチャを採用しており、機能単位で複数のサービスが連携しながら処理を構成しています。

そのため、1つの機能を実装する場合であっても、複数のアプリケーションがどのように関わっているのかを追う必要があり、APIはどのアプリケーションに実装するのか、実装したAPIを呼び出すためのルーティングはどこに実装するのかを把握するまでにもそれなりに時間がかかってしまいました。

全体像のイメージがついていざ実装をしようとしてもバックエンドアプリケーションは業務領域ごとに module が分かれており、各 module には多数のドメインモデルが存在します。

module間、アプリケーション間で同名のドメインモデルがあることも珍しくなく、これらのモデルが表現する業務概念や責務の範囲がコードを読んでも十分に理解できておらず、自分が実装するAPIはどのmoduleに属すのか、ランキングメタ情報のモデルには何が必要か曖昧な状態で開発を進めることになりました。

このように、複数のサービスと多数のドメインモデルが関係する環境下では、コードの読み進めに時間がかかり、実装以前に全体構造を理解すること自体が大きな課題となりました。


CQRSの思想が理解できない

参照はQueryService、更新はCommandServiceを定義することでロジックを明確に分離しているのですが、分離することの意義が理解できませんでした。

「1つのAPIのためにここまで分割する必要があるのか」という疑問を抱えながら進めていました。

また、Query側ではドメインモデルを使用せずDTOを返却する設計になっており、この点も理解に時間を要しました。

ドメインモデルが既に存在しているにもかかわらず、それを使わずに別途DTOを用意する理由が見えず、「重複したように見えるデータ定義」に戸惑いました。


アーキテクチャテストによる制約

既存処理を理解してやっとの思いで実装しても、まだ困難は続きました。pre-commitで動作するArchUnit を用いたアーキテクチャの依存関係を検証するテスト(以下、アーキテクチャテスト)に何度も失敗しコミットすることができなかったのです。

下記は私が違反したアーキテクチャテストの例です。

  • 別moduleのドメインモデルを使用してしまう
  • UI層で受け取るリクエスト用のDTOをそのままApplication層に渡してしまう(Application層からRepository層までドメインモデルを利用しなければならない)
  • module内でDIするクラスの上限を超えてしまう(無闇にDIするクラスを増やさない)

アーキテクチャテストの存在を知らず、実装して動作確認した後にpre-commitで失敗し再度修正が必要になった時には「なんでこんなに厳しい制約があるんだ!」とイライラしていました。


レビューで指摘されたこと

実装を一通り終え、Pull Requestを提出した後、複数の重要な指摘を受けました。

どれもアーキテクチャ理解の不足に起因するもので、後述する「理解を深める工夫」へつながる重要なきっかけとなりました。


ドメインルールを Application層やInfrastructure層に実装してしまう

ランキングメタ情報には「深夜時間帯は更新できない」という制限があります。

これは深夜に動くランキング作成バッチによって参照されるデータのため、該当時間帯に更新されるとデータ不整合が起きかねないのでそれを防ぐためのドメインルールです。

当初私はこのルールがランキングメタ情報というドメインに関するルールという意識が薄く、手続型の実装に慣れていたこともあって深く考えずにこの判定を Serviceクラス(Application層)に記述していましたが、

「これはドメイン層に閉じ込めるべきロジックです」

とレビューで指摘されました。

またドメインに関する判定ロジックをInfrastructure層に実装していました。

DataSourceクラス(Infrastructure層)にドメインモデルの状態に応じた判定ロジックと、判定結果を受けてinsertまたはupdateするという肥大化したメソッドを実装したところ

「ドメインモデルの状態を判定する処理はドメインモデルに実装し、判定を元にinsert,updateを判断するのはServiceで実装すべきでDataSourceクラスではシンプルかつ汎用的な役割に留めるべき」

とレビューで指摘されました。

2つの指摘から自分がどのレイヤーに何の処理を実装すべきかの理解ができていないことに気づきました。レイヤー毎の責務の分離というものを概念としては知っていたが実践できるほど定着していなかったというのを認識する良い機会になりました。


UI層の関心事をドメインモデルに実装してしまう

先ほどの「深夜時間帯は更新できない」ドメインルールをドメインモデルに実装し深夜時間帯であれば更新処理を中断し例外をthrowしエラーレスポンスを返却するように修正して再度レビューに出しました。

すると今度は

「ドメイン層では更新可能な時間というルールがあり可能かどうか判定を行うだけで判定結果を受けてUIにどう表現するかまで考慮すべきでない」

と指摘されました。

ルールに違反した場合に例外を投げてエラーにするのか、または200OKでメッセージを返却するのかはドメインモデルを使用する側の関心事であり、ドメインとは切り離されたインターフェースの話である。
「仮にドメインモデルにUIに関する実装(メッセージなど)を含めてしまうと、UIの文言を変更したいだけの修正であってもドメインモデルに変更を加える必要が生じます。これはUIとドメインの責務の分離ができておらず、依存関係が高すぎるためです」と説明いただき、納得しました。

ドメインモデルに閉じ込めるのはあくまでドメインに関するロジックでありなんでもかんでも入れ込めば良いわけではないことを学びました。


更新 API に不要な ValueObject を定義してしまう

更新処理の実装で “ランキングメタ情報” のドメインモデルを定義する時に、「ドメインを構成する概念は全てフィールドとして保持すべき」という考えが強く、更新ユースケースで不要なPersonaType(どのペルソナのランキングかを表す) をValueObjectとして追加していました。

レビューでは次の点が指摘されました。

  • YAGNI原則(今必要ないものは導入しない)
  • ドメインモデルと DB スキーマを 1:1 に対応させる必要はない

この指摘を受け、ドメインを構成する全ての要素を含めるのは誤りではないものの「ランキングメタ情報を更新する」という今実現したいことに不要な要素は実装しないという考え方があることを学びました。


理解を深めるために行ったこと

上記の課題やレビュー指摘を踏まえ、アーキテクチャの理解を深めるために行った取り組みを下記に整理します。


AIを使用してコードを精読する

AIはとても便利で「このmoduleではどんな処理をしてる?」「このAPIで呼び出されてる外部アプリは?」など雑な質問でも綺麗に整理してくれるのでなんとなく処理の概観が分かってしまいます。

しかしそれではなんとなくの理解で終わってしまいます。

そこで私はAIを使って「素早く」読むのではなく「じっくり」理解することにしました。

特に

「なぜこの設計になっているのか」 「機能開発時に気をつけるべき設計ポイントなにか」

などの質問をするとdesign docなどのドキュメントでは抽象的に表現される部分を具体を交えて学ぶことができて理解につながったと思っています。


アーキテクチャテストの事前読解

アーキテクチャテストに繰り返し阻まれた経験から、このテストを単なる「エラーの原因」として扱うのではなく、プロジェクトにおいて最低限守るべき設計ルールを明示した仕組みとして捉え直すようになりました。

それ以降は、実装を始める前にテストコードを読み、どのような依存関係や層構造が許容され、何が禁止されているのかを把握することを心がけました。

アーキテクチャテストを事前に理解することで、次のような点が明確になります。

  • Domain、Application、Infrastructureそれぞれの層が前提としている責務の範囲

  • 層を跨ぐ依存がどの方向で許可されており、どこから先は許容されないのか

これにより、テストに引っかかってから修正するのではなく、事前にルールを把握したうえで実装を進められるようになり、無駄な試行錯誤が大幅に減少しました。

またアーキテクチャテストは不変ではなくプロダクトの成長に合わせて都度更新がされていきます。テストでなぜそのようなルールが定められ、新しく追加されたのかというテスト設計者の意図を理解することがより良い設計を知る糸口だと考えています。


レビューを積極的に依頼し、理解を補完

チームメンバーに対してPRでは「細かいところまで指摘をお願いします」と依頼し、自分の理解が及んでいない部分を可視化していきました。

レビューを通じて、自分では気づけなかった考え方や原則(集約設計、層の役割、ドメインモデルの在り方など)を吸収できた点は大きな成果でした。


半年経過して何が変わったか

ランキングメタ情報のAPIを通じてアーキテクチャに向き合った経験や、レビューによるフィードバックを繰り返し受ける中で、入社当初から変化があったと感じるポイントは下記です。

ビジネスロジックの閉じ込めの価値を実感

ビジネスロジックをドメイン層に閉じ込める設計の価値を実感するようになりました。

ビジネスルールがドメイン層に適切に集約されている場合、開発タスクがドメイン層の修正だけで完結し、影響範囲も限定的であることを体験的に理解しました。

逆に、ロジックが複数の層に分散していると、仕様変更時に複数の箇所へ修正が波及し、意図しない副作用のリスクも高まることを改めて認識しました。

最後に

アソビューへ入社してからの半年間は、DDDとCQRSを中心とするアーキテクチャに触れ続ける中で、多くのつまずきと学習の機会がありました。

依然として難しいと感じる部分はありますが、その分、学べることや成長できる余地が非常に多い環境だと実感しています。

アソビューでは一緒に働くメンバーを募集しています!
DDDやCQRSに関心のある方・実践していきたい方、大歓迎です!
カジュアル面談もやっておりますので、お気軽にエントリーください!

www.asoview.co.jp