LoginSignup
1
0

More than 5 years have passed since last update.

ドメインオブジェクトからプレゼンテーション層で使うDTOへの値の詰替え

Last updated at Posted at 2019-02-05

クソ記事投稿週間な今日この頃です。
業務でScalaを使っているのでコードはScalaです。ご了承ください。

検討対象のコード

ドメイン層で以下のようなクラスが定義されているとしたときに、
セッターはもちろんのこと、ゲッターを禁止にするために、属性にprivateがついている前提であったとします。

/**
  * 顧客クラス
  *
  * @param name 名前
  * @param age 年齢
  * @param address 住所
  */
case class Customer(private val name: Name, private val age: Age, private val address: Address) {
  // TODO 様々なふるまいが定義されいている
}

case class Name(private val first: String, private val last: String)
case class Age(private val value: Int)
case class PostalCode(private val value: String)
case class City(private val value: String)
case class Address(private val postalCode: PostalCode, private val city: City)

アプリケーション層のアプリケーションサービスでプレゼンテーション層に値を返すために、以下のようなクラスが定義されています。

/**
  * レスポンス用のクラス
  * DTO(Data Transfer Object)
  * ドメインの知識を流用させたくないので、ただの値の入れ物になっている
  */
case class Response(firstName: String,
                    lastName: String,
                    age: Int,
                    address: String)

このような場合に、下記のドメインモデルのオブジェクトCustomerResponseに変換するためのdomainModelToResponseModelメソッドをどう実装するのがシンプルかを少し考えました。

// アプリケーションサービス
object SomeAppService {
  def execute(): Response = {

    // TODO 何かしらの処理がここに入る

    val alice = Customer(
        Name("チョコミント", "クソ野郎"),
        Age(99),
        Address(PostalCode("888-8888"), City("HappyTown")))

    // ドメインモデルをResponse用のDTOに詰めかえる
    // このメソッドの実装について考える
    domainModelToResponseModel(alice)
  }

考えた結果

ドメインオブジェクトの属性のprivate修飾子を外してしまうと、ゲッター使い放題を許可してしまうことになるので、

アプリケーション層→プレゼンテーションへの値の詰替え限定でケースクラスに実装されているunapplyメソッドを利用するのがいいのかなと思いました。

値の詰替えをするdomainModelToResponseModelメソッドは以下のようになりました。

  /**
    * ドメインオブジェクトからレスポンス用のオブジェクトに変換する
    * アプリケーションサービスに定義
    *
    * @param customer ドメインオブジェクトのCustomer
    * @return レスポンス用のオブジェクト(DTO)
    */
  private def domainModelToResponseModel(customer: Customer): Response = {
    // ケースクラスのunapplyメソッドを使って値を取り出す
    val Customer(
    Name(firstName, lastName), 
    Age(age), 
    Address(PostalCode(postalCode), City(city))) = customer

    val address = s"$postalCode $city"
    Response(firstName, lastName, age, address)
  }

ついでにファーストクラスコレクションもこんな感じに取り出せます。

/**
  * Customerのファーストクラスコレクション
  */
case class Customers(private val customers: Set[Customer]) {
  def nonEmpty: Boolean = customers.nonEmpty
  def empty: Boolean = !nonEmpty

  // TODO 様々なふるまいが定義されている
}

val Customers(values) = customers

values.map { customer =>
  val Customer(
  Name(firstName, lastName),
  Age(age),
  Address(PostalCode(postalCode), City(city))) = customer

  val address = s"$postalCode $city"
  Response(firstName, lastName, age, address)
}

他の言語だと値の詰替えってどのように実施するのだろうかということが疑問に残りました。単純に考えると、ゲッターぐらいしか思い浮かばなかったです。。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0