Más contenido relacionado La actualidad más candente (20) Similar a Scala with DDD (20) Scala with DDD32. 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
}
!
}
33. 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"
}
34. 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
35. //
値表現としての山城さんが改名されてしまったら見分けられない
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)
36. 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
=
…
}
37. //
見つけられる
val
l
=
List(
User(1,
"Yutaka",
"Hogeshiro"),
User(2,
"Junchi",
"Kato"))
l.exists(_
==
User(1,
"Yutaka",
"Yamashiro"))
//
true
38. case
class
HunterId(value:
UUID)
extends
Identity[UUID]
!
class
Hunter(
val
identity:
HunterId,
val
name:
String,
val
rank:
Int,
//
...
)
extends
Entity[HunterId]
{
//
...
}
40. 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を解毒させる
}
}
41. 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)
}
!
}
49. どんな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]]
51. !
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
}
}
}
52. 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
}
53. 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
}
54. 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
}
}
56. 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
}
}
57.
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()
}
}
}
59. 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
}
}
})
}
}
60. 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()
}
}
}
}