DTO を用意してドメインオブジェクトの流出を防ぐ
先月発売された「ドメイン駆動設計入門」を読んでいる。
ドメイン駆動設計においては、ドメインオブジェクトを利用するのはアプリケーションサービスに留めるべきで、それより外の世界にドメインオブジェクトを流出させるべきではない、とされる。
アプリケーションサービス外からドメインオブジェクトに定義されている振る舞いを呼び出されてしまう危険性があるからだ。
例えばコントローラやUI の世界からドメインオブジェクトのメソッドを呼び出されてしまい、意図しないバグを発生させる可能性がある。
開発チーム内でポリシーをルール化する方法もあるが、コードによるルール化が最も強力で、なにより コードによる制約はプログラムをぐっとシンプルにする。
コードによるルール化の方法だが、 DTO(Data Transfer Object)を定義して、そいつをUI層に渡してやる。
ドメインオブジェクト
class User { private let userName: UserName private let birthDay: Date private let sex: Sex private let marriageCount: Int struct UserName { private let firstName: String private let lastName: String } struct Sex { ... } public func changeLastName(name: String) { self.lastName = name self.marriageCount += 1 } ... }
(名前は名字の変更のみができて、結婚歴が1インクリメントされる、みたいなちょっとアレなシステム要件w)
DTO は単純なPlane Object で中には値しか入っていない。
class UserData { public let firstName: String public let lastName: String public let birthDay: String public let sex: String public let marriageCount: Int public init(user: User) { self.firstName = user.userName.firstName ... } }
DTO のイニシャライザにUserドメインオブジェクトを直接渡しているが、「ドメイン駆動設計入門」によればそれは問題ない、とのこと。
UserDataはコンストラクタの引数として受け取るUserと密な関係にあります。Userのデータを公開するためのオブジェクトであるUserDataがUserに依存することはあまり問題になりません。
成瀬 允宣. ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本 (Japanese Edition) (Kindle の位置No.2232-2233). Kindle 版.
(この記事のサンプルコードは僕のオリジナルです。)
ちなみに、僕はクラス extension でconvertToDto
みたいなメソッド生やしてそいつがDTO を生成するようなやり方を採用しています。
参考 : bamboo-yujiro.hatenablog.com
DTO をもって今度はVO(ViewData) に変換するなり、DTO のままUIを描画するなりはまたチームの採用しているアーキテクチャの方針に従うべきだが、一番重要なドメインオブジェクトを公開してしまうことによるリスクが0 になっているので後は些末な話だと思う。