SlideShare a Scribd company logo
Scala with DDD
かとじゅん(@j5ik2o)
Scalaで実践的な設計の話
自己紹介
•

DDD/Scala/Finagle

•

http://git.io/trinity

•

Haskell/MH4
Scalaで実践的な設計の話
 じゃなくてMH4の話…。
設計には様々な正解があります。
DDDの実践例のひとつだと
思ってください。
ところで
ウェブアプリケーション
を作るときに、
何を重視して設計するか?
テーブル?
UI?
DDDでは(ドメイン)モデル
なんで?
詳しくはこちら?
というのは、
冗談です
モデルオブジェクト(=オ
ブジェクト指向)を使っ
て、問題を解決したいか
らです。
複雑な問題は
モデルを使って
解決する
I/F

Domain
Model
問題の領域=
ドメイン
ボクらのハンタードメイン
Hunter

Item

Monster

Sword

Armor
ハンター世界の
一つのシナリオを
考えてみよう
落とし物を拾うシーン
HUNTER_LOST_MATTER
テーブルに
外部キーの関連を追加する
HUNTER

1

0..*

HUNTER_LOST_MATTER 1

1 LOST_MATTER
これは間違いではない。
しかし実装の用語であって
ドメインの知識を
表していない
ハンターが落とし物を拾い、
アイテムとして所持する。
Hunter

1

0..*

LostMatter

trait	
  Item	
  
trait	
  LostMatter	
  extends	
  Item	
  
trait	
  Hunter	
  {	
  
	
  	
  val	
  items:	
  Seq[Item]	
  
	
  	
  def	
  take(lostMatter:	
  LostMatter):	
  Try[Hunter]	
  
}
ドメインの語彙=
ユビキタス言語
シナリオ

実装

モデル
他にもシナリオがある
•

モデル間の関係がどうなるのか考える
•

乗り攻撃のシーン

•

しっぽを切り落とすシーン
DDDによるレイヤー化
UI

Controller
Form

Dto(ViewModel)

Validator
JavaScript

HMTL/CSS

アプリケーション層
Factory

Entity

ドメインの概念

ValueObject

ドメイン層

Repository

Service

Module

Aggregate

インフラストラクチャ層
Configuration

RPC

ORM

DataAccess
モデルと実装
モデルの種類
エンティティ
値オブジェクト
サービス

モジュール
エンティティ

•

見分けることができる

•

不変の識別子を持つ
trait	
  Entity[ID	
  <:	
  Identity[_]]	
  {	
  
!

	
  	
  /**	
  エンティティの識別子。	
  */	
  
	
  	
  val	
  identity:	
  ID	
  
!

	
  	
  override	
  final	
  def	
  hashCode:	
  Int	
  =	
  
	
  	
  	
  	
  31	
  *	
  identity.##	
  
!

	
  	
  override	
  final	
  def	
  equals(obj:	
  Any):	
  Boolean	
  =	
  	
  
	
  	
  	
  	
  obj	
  match	
  {	
  
	
  	
  	
  	
  	
  	
  case	
  that:	
  Entity[_]	
  =>	
  
	
  	
  	
  	
  	
  	
  	
  	
  identity	
  ==	
  that.identity	
  
	
  	
  	
  	
  case	
  _	
  =>	
  false	
  
	
  	
  }	
  
!

}
trait	
  Identity[+A]	
  extends	
  Serializable	
  {	
  
!

	
  	
  def	
  value:	
  A	
  
!

}	
  
!

object	
  EmptyIdentity	
  
	
  	
  extends	
  Identity[Nothing]	
  {	
  
!

	
  	
  def	
  value	
  =	
  throw	
  EmptyIdentityException()	
  
!

	
  	
  override	
  def	
  equals(obj:	
  Any):	
  Boolean	
  =	
  
	
  	
  	
  	
  EmptyIdentity	
  eq	
  obj	
  
!

	
  	
  override	
  def	
  hashCode():	
  Int	
  =	
  31	
  *	
  1	
  
!

	
  	
  override	
  def	
  toString	
  =	
  "EmptyIdentity"	
  
}
case	
  class	
  User(id:	
  Int,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  firstName:	
  String,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  lastName:	
  String)	
  
!

val	
  l	
  =	
  List(	
  
	
  	
  User(1,	
  "Yutaka",	
  “Yamashiro"),	
  
	
  	
  User(2,	
  "Junchi",	
  “Kato")	
  
)	
  
l.exists(_	
  ==	
  User(1,	
  "Yutaka",	
  "Yamashiro"))	
  //	
  true
//	
  値表現としての山城さんが改名されてしまったら見分けられない	
  
val	
  l	
  =	
  List(	
  
	
  	
  User(1,	
  "Yutaka",	
  “Yamashiro").	
  
	
  	
  	
  	
  copy(lastName	
  =	
  “Hogeshiro"),	
  
	
  	
  User(2,	
  "Junchi",	
  "Kato"))	
  
l.exists(_	
  ==	
  User(1,	
  "Yutaka",	
  "Yamashiro"))	
  //	
  false	
  
!

//	
  この操作は危険。オブジェクトを取り違える可能性。	
  
User(1,	
  "Yutaka",	
  "Yamashiro").copy(id	
  =	
  2)
class	
  User(val	
  id:	
  Int,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  val	
  firstName:	
  String,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  val	
  lastName:	
  String)	
  {	
  
	
  	
  	
  override	
  def	
  equals(obj:	
  Any):	
  Boolean	
  =	
  obj	
  match	
  {	
  
	
  	
  	
  	
  	
  case	
  that:	
  User	
  =>	
  id	
  ==	
  that.id	
  
	
  	
  	
  	
  	
  case	
  _	
  =>	
  false	
  
	
  	
  	
  }	
  
	
  	
  	
  override	
  def	
  hashCode	
  =	
  31	
  *	
  id.##	
  
	
  	
  	
  //	
  識別子は更新できない	
  
	
  	
  	
  def	
  copy(firstName:	
  String	
  =	
  this.firstName,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  lastName:	
  String	
  =	
  this.lastName)	
  =	
  	
  
	
  	
  	
  	
  	
  new	
  User(firstName,	
  lastName)	
  
}	
  
object	
  User	
  {	
  
	
  	
  def	
  apply(…):	
  User	
  =	
  …	
  	
  
}
//	
  	
  見つけられる	
  
val	
  l	
  =	
  List(	
  
	
  	
  User(1,	
  "Yutaka",	
  "Hogeshiro"),	
  
	
  	
  User(2,	
  "Junchi",	
  "Kato"))	
  
l.exists(_	
  ==	
  User(1,	
  "Yutaka",	
  "Yamashiro"))	
  //	
  true
case	
  class	
  HunterId(value:	
  UUID)	
  
	
  	
  extends	
  Identity[UUID]	
  
!

class	
  Hunter(	
  
	
  	
  val	
  identity:	
  HunterId,	
  
	
  	
  val	
  name:	
  String,	
  
	
  	
  val	
  rank:	
  Int,	
  
	
  	
  //	
  ...	
  
)	
  extends	
  Entity[HunterId]	
  {	
  
	
  	
  //	
  ...	
  
}
値オブジェクト
•

識別はしない。

•

値の説明が目的。

•

原則的に不変オブジェクト。

•

Identityは値オブジェクト。
sealed	
  trait	
  Item	
  {	
  
	
  	
  val	
  name:	
  String	
  
	
  	
  def	
  beUsedBy(hunter:	
  Hunter):	
  Try[Hunter]	
  
}	
  
case	
  class	
  Analepticum()	
  extends	
  Item	
  {	
  
	
  	
  val	
  name	
  =	
  "analepticum"	
  
	
  	
  def	
  beUsedBy(hunter:	
  Hunter):	
  Try[Hunter]	
  =	
  {	
  
	
  	
  	
  	
  //	
  hunterを回復させる	
  
	
  	
  }	
  
}	
  
case	
  class	
  Antidote()	
  extends	
  Item	
  {	
  
	
  	
  val	
  name	
  =	
  "antidote"	
  
	
  	
  def	
  beUsedBy(hunter:	
  Hunter):	
  Try[Hunter]	
  =	
  {	
  
	
  	
  	
  	
  //	
  hunterを解毒させる	
  
	
  	
  }	
  
}
class	
  Hunter(	
  
	
  	
  val	
  identity:	
  HunterId,	
  
	
  	
  val	
  name:	
  String,	
  
	
  	
  val	
  rank:	
  Int,	
  
	
  	
  val	
  items:	
  Set[Item]	
  
)	
  extends	
  Entity[HunterId]	
  {	
  
!

	
  	
  def	
  use(item:	
  Item):	
  Try[Hunter]	
  =	
  {	
  
	
  	
  	
  	
  	
  require(items.exists(_	
  ==	
  item))	
  
	
  	
  	
  	
  	
  item.beUsedBy(hunter)	
  
	
  	
  }	
  
!

}
ドメインモデルはユビキ
タス言語と対応づくこと
(クラス名, 属性, 振舞い)
指定席と自由席
•

座席予約システムの場合
•

座席と参加者はエンティティ。各
チケットに座席番号が紐づくから

•

イベント自体が自由席でチケット
を持っていればどこでもよいなら
エンティティである必要はない。
個数だけ把握できればいいので、
値オブジェクトとなる。
ライフサイクルの管理
ライフサイクル管理
ファクトリ

リポジトリ
集約
リポジトリ
•

エンティティをリポジトリに
保存したり、識別子からエン
ティティを取得できる。

•

ドメインの知識は表現しない
で、I/Oだけを担当する。

•

内部で何をしていても、外部
からはコレクションのように
見える。
よくある勘違い
•

DDDにおいては、User#saveはドメイン
モデルの責務じゃない。ユビキタス言語
に対応する言葉がないから。これはリポ
ジトリの責務。

•

ARのようなモデルはドメインモデルとせ
ずに、インフラストラクチャ層のモデル
と定義した方が現実的。
Repository

DAO

Repository

on Memory on Memcached

on DB

DAO

on API
どんなI/Fがあるか
def	
  resolve(identity:	
  ID)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  Future[E]	
  
!

def	
  store(entity:	
  E)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  Future[(R,	
  E)]	
  
!

def	
  delete(identity:	
  ID)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  Future[(R,	
  E)]	
  
!

def	
  resolveChunk(index:	
  Int,	
  maxEntities:	
  Int)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Future[EntitiesChunk[ID,	
  E]]	
  
!

//	
  toList,	
  toSetは	
  メモリ版の実装のみ。	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
def	
  toList:	
  Future[List[E]]	
  
def	
  toSet:	
  Future[Set[E]]
Cache Management
Repository
Decorator

on Memcached

on DB
!

	
  	
  protected	
  val	
  storage:	
  AsyncRepository[ID,	
  E]	
  
!

	
  	
  protected	
  val	
  cache:	
  AsyncRepository[ID,	
  E]	
  
!

	
  	
  def	
  resolve(identity:	
  ID)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (implicit	
  ctx:	
  EntityIOContext):

	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Future[E]	
  =	
  {	
  
	
  	
  	
  	
  implicit	
  val	
  executor	
  =	
  getExecutionContext(ctx)	
  
	
  	
  	
  	
  cache.resolve(identity).recoverWith	
  {	
  
	
  	
  	
  	
  	
  	
  case	
  ex:	
  EntityNotFoundException	
  =>	
  
	
  	
  	
  	
  	
  	
  	
  	
  for	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  entity	
  <-­‐	
  storage.resolve(identity)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (_,	
  result)	
  <-­‐	
  cache.store(entity)	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  yield	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  result	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
	
  	
  }
def	
  filterByPredicate(predicate:	
  E	
  =>	
  Boolean,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  index:	
  Option[Int]	
  =	
  None,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  maxEntities:	
  Option[Int]	
  =	
  None)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Future[EntitiesChunk[ID,	
  E]]	
  
!

def	
  filterByCriteria(criteria:	
  Criteria,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  index:	
  Option[Int]	
  =	
  None,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  maxEntities:	
  Option[Int]	
  =	
  None)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Future[EntitiesChunk[ID,	
  E]]	
  
!

trait	
  CriteriaValue[A]	
  {	
  
	
  	
  val	
  name:	
  String	
  
	
  	
  val	
  operator:	
  OperatorType.Value	
  
	
  	
  val	
  value:	
  A	
  
	
  	
  def	
  asString:	
  String	
  
}	
  
!

trait	
  Criteria	
  {	
  
	
  	
  protected	
  val	
  criteriaValues:	
  List[CriteriaValue[_]]	
  
	
  	
  def	
  asString:	
  String	
  
}
val	
  repository:	
  HunterRepository	
  =	
  
HunterRepository(RepositoryType.Memory)	
  
//	
  or	
  RepositoryType.Memcached	
  
!

val	
  hunter	
  =	
  new	
  Hunter(EmptyIdentity,	
  ...)	
  
!

val	
  updateTime	
  =	
  for	
  {	
  
	
  	
  (newRepos,	
  newEntity)	
  <-­‐	
  repository.store(hunter)	
  
//	
  def	
  store(entity:	
  E):	
  Try[(R,	
  E)]	
  
	
  	
  hunter	
  <-­‐	
  newRepos.resolve(newEntity.identity)	
  
//	
  def	
  resolve(identity:	
  ID):	
  Try[E]	
  
}	
  yield	
  {	
  
	
  	
  hunter.updateTime	
  
}
val	
  repository:	
  HunterRepository	
  =	
  	
  
	
  	
  HunterRepository(RepositoryType.JDBC)	
  
!

val	
  hunter	
  =	
  new	
  Hunter(EmptyIdentity,	
  ...)	
  
!

//	
  def	
  withTransaction[T](f:	
  (DBSession)	
  =>	
  T):	
  T	
  
val	
  updateTime	
  =	
  UnitOfWork.withTransaction	
  {	
  
	
  	
  tx	
  =>	
  
	
  	
  	
  	
  implicit	
  ctx	
  =	
  EntityIOContext(tx)	
  
	
  	
  	
  	
  for	
  {	
  
	
  	
  	
  	
  	
  	
  (newRepos,	
  newEntity)	
  <-­‐	
  repository.store(hunter)	
  
//	
  def	
  store(entity:	
  E)	
  
//	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  Try[(R,	
  E)]	
  
	
  	
  	
  	
  	
  	
  hunter	
  <-­‐	
  newRepos.resolve(newEntity.identity)	
  
//	
  def	
  resolve(identity:	
  ID)	
  
//	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  Try[E]	
  
	
  	
  	
  	
  }	
  yield	
  {	
  
	
  	
  	
  	
  	
  	
  hunter.updateTime	
  
	
  	
  	
  	
  }	
  
}
ScalikeJDBCで
UnitOfWorkを実装
val	
  repository:	
  HunterRepository	
  =	
  	
  
	
  	
  HunterRepository(RepositoryType.JDBC)	
  
!

val	
  hunter	
  =	
  new	
  Hunter(EmptyIdentity,	
  ...)	
  
!

//	
  def	
  withTransaction[T](f:	
  (DBSession)	
  =>	
  Future[T]):	
  Future[T]	
  
val	
  updateTime	
  =	
  UnitOfWork.withTransaction	
  {	
  
	
  	
  tx	
  =>	
  
	
  	
  	
  	
  implicit	
  ctx	
  =	
  EntityIOContext(tx)	
  
	
  	
  	
  	
  for	
  {	
  
	
  	
  	
  	
  	
  	
  (newRepos,	
  newEntity)	
  <-­‐	
  repository.store(hunter)	
  
//	
  def	
  store(entity:	
  E)	
  
//	
  	
  	
  	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  Future[(R,	
  E)]	
  
	
  	
  	
  	
  	
  	
  hunter	
  <-­‐	
  newRepos.resolve(newEntity.identity)	
  
//	
  def	
  resolve(identity:	
  ID)	
  
//	
  	
  	
  	
  	
  (implicit	
  ctx:	
  EntityIOContext):	
  Future[E]	
  
	
  	
  	
  	
  }	
  yield	
  {	
  
	
  	
  	
  	
  	
  	
  hunter.updateTime	
  
	
  	
  	
  	
  }	
  
}
 	
  def	
  withTransaction[A](op:	
  (DBSession)	
  =>	
  Future[A])	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (implicit	
  executor:	
  ExecutionContext):	
  Future[A]	
  =	
  {	
  
	
  	
  	
  	
  Future(ConnectionPool.borrow()).flatMap	
  {	
  
	
  	
  	
  	
  	
  	
  connection	
  =>	
  
	
  	
  	
  	
  	
  	
  	
  	
  val	
  db	
  =	
  DB(connection)	
  
	
  	
  	
  	
  	
  	
  	
  	
  Future(db.newTx).flatMap	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  tx	
  =>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Future(tx.begin()).flatMap	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _	
  =>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  op(db.withinTxSession(tx))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }.andThen	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  case	
  Success(_)	
  =>	
  tx.commit()	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  case	
  Failure(_)	
  =>	
  tx.rollback()	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }.andThen	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  case	
  _	
  =>	
  connection.close()	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
	
  	
  }
REST APIで
ドメインの利用例
class	
  HunterController(hunterRepository:	
  HunterRepository)	
  	
  
	
  	
  extends	
  ControllerSupport	
  {	
  
	
  	
  def	
  createHunter	
  =	
  SimpleAction	
  {	
  
	
  	
  	
  	
  request	
  =>	
  
	
  	
  	
  	
  	
  	
  val	
  params	
  =	
  parseJson(request)	
  
	
  	
  	
  	
  	
  	
  val	
  formValidation	
  =	
  CreateForm.validate(params)	
  
	
  	
  	
  	
  	
  	
  formValidation.fold(validationErrorHandler,	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  case	
  form	
  =>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  UnitOfWork.withSession	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  implicit	
  session	
  =>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  hunterRepository.	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  store(form.asEntity).flatMap	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  case	
  (_,	
  entity)	
  =>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  responseBuilder.	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  withJValue(entity.asJValue).toFuture	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  })	
  
	
  	
  }	
  
}
class	
  HunterController(hunterRepository:	
  HunterRepository)	
  	
  
	
  	
  extends	
  ControllerSupport	
  {	
  
	
  	
  def	
  transferItems(from:	
  HunterId,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  to:	
  HunterId,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  itmes:	
  Seq[Item])	
  =	
  SimpleAction	
  {	
  
	
  	
  	
  	
  request	
  =>	
  
	
  	
  	
  	
  	
  	
  UnitOfWork.withSession	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  implicit	
  session	
  =>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  for	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  toHunter	
  <-­‐	
  hunterRepository.resolve(to)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  fromHunter	
  <-­‐	
  hunterRepository.resolve(from)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _	
  <-­‐	
  fromHunter.transerItems(items,	
  toHunter)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  yield	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  createResponse()	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  }	
  
	
  	
  }	
  
}
やってみて思ったこと
DDDでは
フルスタックF/Wが
使いにくい
Play2 with DDD
Controller

Batch or ???

Entity
VO

Domain
Repository

Service

ScalikeJDBC

Anorm

Play2
まとめ
•

コストがかかる。複雑でない問題には使わない。

•

CoCを前提にするF/Wとは相性が悪い。F/Wとけん
かしない方法を選ぶべき。

•

OOPよりデータと手続きの方が高速。とはいえ、オ
ブジェクトを使いますよね。高速化必須な場合は局所
的に手続き型にする。
ありがとございました。

More Related Content

Scala with DDD