落穂拾い | これで理解できるドメイン駆動設計!

Prev: ユーザーがショッピングカートの中身を確認する

この章では本文では触れてこなかったり、コラムという形で触れてきたことについて雑多に言及していきます。

RepositoryはApplicationServiceにDIしなくて良いのか?

方々で書いてきましたが、テストする際にRepositoryをMockにしなければならない理由がない限りはDIする必要はないでしょう。

レイヤとドメイン駆動設計の関係#インフラストラクチャレイヤで詳しく書いています。

ValueObjectで間違った値を入れられないようにした方が良いのでは?

その必要性があるならしたら良いと思います。ただ、繰り返し書いていますが、想定しているメリットが実際にどれだけ享受できるのか考え、実装コスト、パフォーマンス、可読性などなどを加味した上で、本当にやった方が良いと思うならです。

また、そういう間違った値が入らないようにするというのはValueObjectではなくDomainPrimetiveパターンです。

ValueObjectの定義についてはドメイン駆動設計の戦術的設計とはの末尾のコラム「ドメインモデルとの対応と依存以外のEntityとValueObjectに関する話は忘れて良い」を参照してください。

DomainPrimitiveパターンについてはセキュア・バイ・デザイン: 安全なソフトウェア設計 Compass Booksシリーズを参照ください。

***をやった方が良いのでは?

前節ということは同じです。その必要性があるならしたら良いでしょう。

レイヤとドメイン駆動設計の関係のコラム「アーキテクチャと保険」でも書きましたが、リスクとリスク対処で謳われている効果だけみてアーキテクチャを決めてはいけません。

アーキテクト見習い、 一人前のアーキテクト、アーキテクト見習い未満

なぜこうも、メリットデメリットや実際に享受できるか、コストを考えてアーキテクチャを決めよと私がいうのか。それには理由があります。

私が20代半ばのころ、アーキテクチャ設計を任されたことがあります(そんな若手にやらせるなと今なら思いますが)。私はエンタープライズアプリケーションアーキテクチャパターンを読んで、レイヤは疎結合にした方が良いんだなとか、こういうレイヤ構造にしようとかそういうことを考えて設計しました。もちろん、超のつく大失敗です。レイヤを疎結合にするためにDTOに詰め直してとかまるっきり無駄なことでした。

この経験で一つ悟りをえました。それは「マーチンファウラー(上記著書の著者)は自分のプロジェクトにいない」ということです。本に書かれていることは、著者が自分の経験に基づいて良いと思われる一般解を書いているだけであり、自分の参加しているプロジェクトで「うーん、このプロジェクトならこういうアーキテクチャがいいんじゃないかな?」なんて言って提案してきたものではないのです。

現実に直面しているプロジェクトについて分析して、どういうアーキテクチャが良いのか判断できるのは自分たちだけです。本を参考にこそせよ、鵜呑みにはしてはいけません。

アーキテクト見習いであったが故に、私は本を鵜呑みにするという間違いを犯しました。その間違いを反省し、自分の頭で現実とどう折り合いをつけていくか考え始めたことで、私は一人前のアーキテクトとしての一歩を踏み出せたのだろうと思っています。

ディレクトリ構成をどうするか?

ディレクトリ構成については色々な構成が考えられます。appディレクトリがコードのルートだとして、レイヤ構造をそのまま表すスタンダードなものなら

  • app/domains
  • app/infrastructures
  • app/usecases

が考えられます。ただ、レイヤ構造に拘らず「AggregateとRepositoryが1対1なんでしょ?」と考えるなら

  • app/domains
  • app/usecases

にして、RepositoryはAggregateと同じディレクトリ(つまり、app/domainsのサブディレクトリ)に格納するという案もあります。ただ、ドメインレイヤのクラスとインフラストラクチャレイヤのクラスの役割を理解できていないエンジニアがいるなら、混乱するのでやめた方が良いかもしれません。

あるいは、ドメインレイヤもインフラストラクチャレイヤもユースケース(アプリケーション)レイヤもフィーチャーツリーのModule(Package)構造に従うというのなら

  • app/ddd/shopping/order/domains
  • app/ddd/shopping/order/infrastructures
  • app/ddd/shopping/order/usecases
  • app/ddd/shopping/domains
  • app/ddd/shopping/infrastructures
  • app/ddd/shopping/usecases

のように、ルートディレクトリとして適当なディレクトリ(上記ならddd)を切って、その直下からフィーチャーツリーに則ったModule(Package)構造にして、末尾でdomains、infrastractures、usercasesを分けるという選択肢もあります。

また、domains、infrastructures、usecasesを単数系にするとか別の名前(infrastructuresをinfra)にするとか色々考えられます。

いずれにせよ、これが正解であるという答えはないので、開発チームや開発しているシステムの状況を考えて決めるのが良いでしょう。

ドメインレイヤの依存関係

ユーザーがショッピングカートの中身を確認するでも書きましたが、基本的に無節操なModule(Package)またぎの依存関係はメンテナンス性を著しく下げます。私が提案する方法を取らなくとも良いですが、なんらかの秩序だった依存関係に制限した方が良いでしょう。

Railsはドメイン駆動設計に向かない?

ここまで、Railsを例に説明をしてきたのですが、「よく言われる通り、Railsはドメイン駆動設計に向かないなぁ」と思ったでしょうか。動的型付け言語ゆえの型の読み取りにくさは問題になるかもしれませんが、ことさらRailsはドメイン駆動設計に向かないと言われる理由はないように思えないでしょうか。

というか、これを書いている私自身、なんでそう言われるのかわからないのです。思うに、ドメイン駆動設計の核心とは関係ないところ、例えばクリーンアーキテクチャを厳守しようとするとか、無用にDIするとか、ユーザーがショッピングカートに商品を追加するのようなケースでも無用にレイヤ通りにクラスを作ろうとするとか、そういう非合理的な思い込みで自縄自縛に陥ったり、あるいはJavaなどの別言語でうまくいったやり方をRailsの上でもやろうとしたり、はたまた自分の中で確たるドメイン駆動設計論(つまり持論)が出来上がっていて、その通りにできないからといったことが原因ではないかと思います。

なんとなーくですが、Railsはドメイン駆動設計に向かないと考えている人たちは「ドメイン駆動設計がありその上に言語やフレームワークが乗る」と考えている気がします。だから、ドメイン駆動設計の理論通りにやるのが正解で、言語やフレームワークがそれに反するなら言語やフレームワーク側を捻じ曲げるべきという考え方になっているように思います。

これは暴言ですが、真っ当なアーキテクトなら逆で「選定した言語やフレームワークの上で設計手法を実践する」と考えるだろうと思います。言語やフレームワークを曲げるより、設計手法を言語やフレームワークにフィットするように変容する方が容易で現実的だからです。

ApplicationServiceから別のApplicationServiceを呼び出すのはありか?

要件によっては、そういうニーズが生じるのはあり得ると思っています。しかし、依存関係が複雑になりかねないため避けた方が良いかなと個人的には思っています。

どうしても処理を共有したいなら、DomainServiceに委譲するか、SubUsecaseのようなサフィックスをつけたクラスを作って委譲するのが良いのではないかと思います。なお、SubUsecaseというのは、分析手法としてのユースケースの中に「サブユースケース」という考え方があり、それを借用している形です。詳細はユースケース実践ガイド―効果的なユースケースの書き方を参照してください。

Next: 旅立ち