Screaming Loud

日々是精進

ドメイン駆動を実践してみた

以前(http://yuutookun.hatenablog.com/entry/2015/04/04/190539)、ドメイン駆動に関して調べてみてから、ちょこちょこ勉強しつつ、ちょうど自分の新規プロジェクトが動き始めたので、反映させてみた感想をまとめてみました。

概要

・採用したDDD手法
・言語
・パッケージ構成
・どうやって作っていったか
・良かったところ
・悪かったところ

採用したDDD手法

昔ながらのレイヤードであったり、オニオンだったり、ヘキサゴナルだったりありますが、
今回はクリーンアーキテクチャを採用しました。

言語

Scalaで実装しています

パッケージ構成

以下を参考に作成しました。
https://speakerdeck.com/yoskhdia/ddd-plus-clean-architecture-plus-ucdom-fullban
http://qiita.com/kondei/items/41c28674c1bfd4156186
https://speakerdeck.com/usrnameu1/genbafalsekurinakitekutiya-scala-play-puroziekutoshe-ji-falseohua

|- domain # データモデル
     L enum # ドメインに定数
 |- usecase # コアロジック(旧Model), 別パッケージからは呼び出さない
 |- util or service  # 状態を持たないutil系
 L  adaptor #  各usecaseとcontrollerと橋渡し (daoとのやり取りなど)
      |- repository # DBとのアクセス
     |- input # リクエストを受け取る型の定義
     |- output # レスポンスを受け取る型の定義
          L  transformer # repository層とuse case層の型変換
   |- controller # リクエスト受け取り部分
     |- input # リクエストを受け取る型の定義
     |- output # レスポンスを受け取る型の定義
          L  transformer # controller層とuse case層の型変換

進め方

ドメイン駆動開発(以下DDD)は、ドメインエキスパート(そのビジネス領域に関してのエキスパート)との対話によって、設計を行う手法です。
まずは、何がエンティティで何が値オブジェクトかを確定させます。
エンティティに応じた操作をusecaseで記述します。
DBとのアクセスはrepositoryを通じて行います。
http ⇔ controller ⇔ use case ⇔ repository ⇔ DB
entity

良かった点

DBで使うオブジェクトとフロントで使うオブジェクトがキレイに切り離されるので、影響範囲が特定しやすいです。
Entityをベースに考えるので、アウトプットを変えたいと言う時に、アウトプットのオブジェクトを作って、そこにEntityの情報を突っ込むだけで追加できるというのが変更に強いです。

悪かった点

やはり、モデルの変換が多いため、実装がどうしても多くなってしまいます。

所感

まだそこまで大きな範囲には適用していないのですが、DDDの威力が垣間見れました。
特にScalaは、DDDと相性がいいなーと思った次第です。

チームをスケールさせる

社会的にはチームで働くということが多いです。
一般的には一人で働くより複数人で働くほうがスケールするからです。
しかし、チームとは単純な足し算ではなく、マネジメントコストがかかったり、コミュニケーションコストがかかったりするため、悪くなるとマイナスな部分すら出てきます。

自分なりにチームをスケールさせる指針をまとめました。

失敗を受け入れる

リーダーとはメンバーの失敗を受け入れて、チームの成長の責任を負うものです。
メンバーの挑戦を阻害してはいけない。
メンバーの挑戦による失敗はチームでリカバリーをする。

育てるという考えを捨てる

チームを「育てる」という観点で見てしまうと、メンバーがリーダー以上にスケールしない。
チームの成長とは相乗効果で学びあうものだと思っている。
リーダーは持っている知識を惜しみなく出す、メンバーはその知識に対して常に批判的な目を忘れない。
そのサイクルが回っていれば、チームとしての成長は加速するはず。

振り返りの時間を作る

チーム内での違和感や不満をすぐに出せる雰囲気を作るというのは難しい。
なぜなら、言われる人に時間がなくて言うタイミングすら無いときもあるからだ。
そもそも振り返りの時間自体を取るべきである。

雑談をする

人間とは感情の生き物なので、仕事以外の雑談も挟む必要があると思う。
そういう雑談からコミュニケーションのハードルは下がるので。

ここに書いてあることは、結構当たり前ではありますが、逆に出来ていないとチームがギクシャクしてしまいかねないです。

csshxでマルチスクリーンの場合に裏側に隠れてしまう問題

みなさんはcsshxつかってますか??

csshxの素晴らしさに関しては、以下で共有されてます。
人間とウェブの未来 - csshXが素晴らしすぎる件
csshXを使ってリモートホストの一括操作を行う | Developers.IO
複数のサーバーに ssh ログインして一括操作できる csshx を使ってみた感想とメモ |

ただすでにメンテが止まってしまっています。
github.com

なんと、LastUpdateが2012年!

ということで、iTermとか代替品は色々あるのですが、なんだかんだ使いやすいのでcsshxを使ってしまうのですが、
Marveriksにアップデートした辺りから裏っかわに隠れてしまったりして、無理やり持ってきたり、一度出したりしてたんですが、
調べてみたら、issueに上がってました。。。
github.com

解決法

ということで、解消法は以下です!
ここのチェックを外すだけでうごきましたぁぁ。
f:id:yuutookun:20160325111958p:plain

今まで何で調べなかったんだろう。。

追記:自分がよく使うcsshx

csshx --login yuto gate001+5

scalaのクエリビルダー「quill」を使ってみた

takezoeさんがquillを紹介していたので、
Scalaの新しいデータベースアクセスライブラリ「quill」を試してみた - たけぞう瀕死ブログ
実際にどんな感じで使えるのかを試してみました。

http://getquill.io/getquill.io

結論、Slick2系から置き換えるのはなかなかありかなと思っています。

直接的なクエリサンプルは、上記のtakezoeさんのブログやドキュメントに書いてあるので、もうちょっと踏み込んだ内容を紹介したいと思います。

Database Access

今回は自分がよく使うMySQLにアクセスするように試しました。

動かすのに必要な最低限のlibraryは以下です。

libraryDependencies ++= {
  Seq(
    "mysql"       %  "mysql-connector-java" % "5.1.36",
    "io.getquill" %% "quill-jdbc" % "0.4.2-SNAPSHOT",
    "com.zaxxer" % "HikariCP" % "2.4.3",
    "com.typesafe" % "config" % "1.3.0"
  )
}

HikariCPとTypesafeConfigがないと、動きません。
また、このバージョンの場合、Java8でないと動きません。

build.sbtを上記のように作って上げれば、ドキュメントに書いてあるようなクエリをコンパイルすることができます。
quill_example/UserDao.scala at master · moc-yuto/quill_example · GitHub

テーブル名

Slickと違ってテーブル名を文字列で指定する部分はなく、queryのcase classがそのままテーブル名に使われます。

以下の例だと、case classのUsersがそのままテーブル名として使われます。

case class Users(id: Int, name: String, age: Int)

val find = quote { (id: Int) =>
  query[Users].filter(u => u.id == id)
}

トランザクション

やはり、実践投入するならトランザクション処理は必須となりますが、本家ドキュメントには書かれていません。
しかし、テストコードにはしっかり書かれています。

書き方としてはとても単純で、以下のようにtransaction関数でくくってあげればいいだけです。

db.transaction{
  val a = db.run(UserDao.findName)(name).headOption
  if (a.isEmpty) db.run(UserDao.insert)(name, age)
  else db.run(UserDao.update)(a.get.id, age)
  throw new Exception
}

このように書くと以下の様なクエリが流れました。

2016-03-06T15:29:28.783495Z   13 Query  SET autocommit=1
2016-03-06T15:29:28.783727Z   13 Query  set session transaction read write
2016-03-06T15:29:28.783987Z   13 Query  SELECT @@session.tx_isolation
2016-03-06T15:29:28.828915Z    4 Query  SELECT x1.id, x1.name, x1.age FROM users x1 WHERE x1.name = 'aaa'
2016-03-06T15:29:28.875733Z    4 Query  select @@session.tx_read_only
2016-03-06T15:29:28.877008Z    4 Query  select @@session.tx_read_only
2016-03-06T15:29:28.877849Z    4 Query  UPDATE users SET age = 1323 WHERE id = 2
2016-03-06T15:29:28.890415Z    4 Query  rollback
2016-03-06T15:29:28.891274Z    4 Query  SET autocommit=1

実際のトランザクションのコードは以下です。
quill_example/Users.scala at master · moc-yuto/quill_example · GitHub

感想

Slickよりも冗長な部分が減ったな―と感じます。
それでいてSlick風な書き方なので、書きやすいと思います。
また、Slickでは生成できないSQLを作れるDSLが多数あるので、細かいハンドリングがしやすいと感じました。


以下は動かしたサンプルです。
github.com