Skip to content

Instantly share code, notes, and snippets.

@bigwheel
Created March 7, 2019 00:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bigwheel/0e3338964bc0f391de82c7abeb4db6de to your computer and use it in GitHub Desktop.
Save bigwheel/0e3338964bc0f391de82c7abeb4db6de to your computer and use it in GitHub Desktop.

以下あくまで私見として捉えてください。僕もDDDを勉強中で解釈が間違っていたりとか異なる意見があると思いますので。 あと書きなぐっているので読みづらくてすみません、あとで余裕があれば改めてブログにでもまとめます。

それで、僕の理解だとリポジトリって対象の集約ごとに提供するメソッドが多少ばらつくんですよね。 例えば書き込みをサポートしないリポジトリとか、IDを複数渡して一度に取ってこれるリポジトリ、そうじゃないリポジトリとか。 やりがちな失敗が、最初にスタンダードなread/writeができるリポジトリを定義してすべてのリポジトリでそれを継承しようとしてしまうことです。 これだとReadしかできないリポジトリが発生したとき、それだけリポジトリ基底クラスを継承しないようにするかReadWriteRepositoryの既定クラスとしてReadRepositoryを置くなどしないといけなくなります。 最悪の選択はReadしか不要なのにWriteのメソッドも実装してしまうことで、これは余計なコードが増えるだけではなく他の開発者が「このリポジトリってwriteしていいんだ」と勘違いする理由になります。

より本質的にアンチパターンであるとした場合の理由を考えると リスコフの置換原則「派生型はその基本型と置換可能でなければならない」に反しているからだと思います。 リポジトリの基底クラスって定義しても基底クラスの型へ代入して使うことはまったくないですよね? だからdef get(id: ID): Aggregateのようなメソッドを定義したとしても、別々のリポジトリ間でメソッド名が同じである、以上のメリットがないんじゃないかなと。 (これはコップ本12.2 シンインターフェイスとリッチインターフェイスのようなアプローチをする場合はメリットが出ますが、パフォーマンス上の問題などで結局しないことも多いと思います)

あとこれはアンチパターンの論拠とはならないんですが、Scalaの型システムへよく習熟してない人が無理に型パラメータを活用しようとして とんでもないコードになったり本来の意図を表現できない既定リポジトリクラスになることはよくありました(当方2の2でそうなっています)。

@j5ik2o
Copy link

j5ik2o commented Mar 7, 2019

対象の集約ごとに提供するメソッドが多少ばらつく

そのとおりだと思います。すべてのリポジトリが同じメソッド集合を実装する必要はないと思います。

ただ、いくつかの汎用的な問い合わせや保存機能はあるので、選択的にミックスインして使えるとよいとは思います。
https://github.com/j5ik2o/scala-ddd-base/blob/master/core/src/main/scala/com/github/j5ik2o/dddbase/
https://github.com/j5ik2o/scala-ddd-base/tree/master/jdbc/slick/src/main/scala/com/github/j5ik2o/dddbase/slick
https://github.com/j5ik2o/scala-ddd-base/blob/master/example/src/main/scala/com/github/j5ik2o/dddbase/example/repository/slick/AbstractUserAccountRepositoryBySlick.scala
選択できれば、派生先の実装で無理に実装せざるを得ないという状況にはならないはずです。

@sp1rytus
Copy link

sp1rytus commented Mar 7, 2019

だからdef get(id: ID): Aggregateのようなメソッドを定義したとしても、
別々のリポジトリ間でメソッド名が同じである、以上のメリットがないんじゃないかなと。

このあたりも、その通りだと思います。コード上はidを一意として認識していたとしてもDB上ではPRIMARY KEYとして張ってなければ永続化の観点で意味ないでしょうし。実際サービス上で使う想定を行なったときに。このget関数なんてほぼ利用がなく、むしろSELECT * FROM XXX WHERE id IN (xxx, xxx, xx) のようなマルチ選択のような利用の方が圧倒的に多いです。
ですので。Repositoryはかとじゅんさんが言うようにmixin でもいいかもしれませんが。業務上でエンジニア全員にmixinのパターンを覚えさせるのも大変です。そういった意味で最低限存在する関数だけを固定しておくのはありかもしれませんね。

蛇足すると、Employee EmployeeProfile みたいなモデルを作るといずれのオブジェクトもIDはEmployee#Id として扱いたい場合があります。こういった場合も。add/update を要求する場合。 EmployeeProfile Repositoryにとっては 新規IDの発行ではなく、既存Employee#Idを用いての INSERT OR UPDATE の処理となるので。update関数のみでカバーできることになりadd関数は不要ですね。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment