yujiro's blog

「インターネット上で正しい答えを得る最善の方法は、質問することではない。間違った答えを投稿することだ」by ウォード・カニンガム

DTO を用意してドメインオブジェクトの流出を防ぐ

先月発売された「ドメイン駆動設計入門」を読んでいる。

www.shoeisha.co.jp

ドメイン駆動設計においては、ドメインオブジェクトを利用するのはアプリケーションサービスに留めるべきで、それより外の世界にドメインオブジェクトを流出させるべきではない、とされる。

アプリケーションサービス外からドメインオブジェクトに定義されている振る舞いを呼び出されてしまう危険性があるからだ。

例えばコントローラやUI の世界からドメインオブジェクトのメソッドを呼び出されてしまい、意図しないバグを発生させる可能性がある。

開発チーム内でポリシーをルール化する方法もあるが、コードによるルール化が最も強力で、なにより コードによる制約はプログラムをぐっとシンプルにする。

コードによるルール化の方法だが、 DTO(Data Transfer Object)を定義して、そいつをUI層に渡してやる。

f:id:bambookun:20200308140810p:plain

ドメインオブジェクト

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 で中には値しか入っていない。

DTO

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 になっているので後は些末な話だと思う。