男爵が書く

DDD、オブジェクト指向、技術書の感想など

XP祭り2021でカンバンとJIRAの話をしました

かれこれ4ヶ月ほど前のことになりますが、XP祭り2021にオンラインで参加していました。アジャイル開発の本を読んだり現場での実践は数年前からやっていたのですが、このジャンルのカンファレンスに参加するのは初めてでした。

confengine.com

自分が最初にアジャイルに触れたのは、同僚の勧めで『アジャイルサムライ』を読んだのがきっかけだったと記憶しています。それまでの現場ではなんとなーく見積もりをして計画を立て、要求が膨らんだり作業が思うように進まなくて炎上するということを繰り返していました。そんな中、「スコープ・予算・時間・品質のトレードオフ・スライダー」や「ベロシティを計測して根拠のある計画を立てる」といった考え方に感銘を受けたのを覚えています。

その後はトム・デマルコの『デッドライン』や『ピープルウェア』、『熊とワルツを』、スティーブ・マコネルの『ソフトウェア見積もり』など、どちらかというと古典的なプロジェクトマネジメントの本を好んで読んでいました。この頃になると、プロジェクトマネジメントにも見積もりにもソフトウェア開発の歴史の中で蓄積されてきた技術が確かに存在していることがわかってきました。と同時に、なぜ世の開発現場はそのような技術や知見を無視した運営がなされているのかという、ある種怒りにも近い感情が湧き上がってきました。せめて自分の周りの状況だけでも変えるためには、自ら学び、チームを巻き込んで実践していく他ありません。

続きを読む

PHPerKaigi2021でユースケースシナリオについて話したことを半年後にふりかえる

早半年前になりますが、PHPerKaigi 2021に登壇者として参加していました。今更ながら記録のために書いておきます。

phperkaigi.jp

 PHPerKaigiには第一回の2018年に一般参加していて、カンファレンス初体験にしてどっぷりとその楽しさの虜になってしまったという経緯があります。それ以降の2年間はスケジュールが合わず不参加だったのですが、今回ようやく再び参加することができました。オンライン開催だったので札幌在住の自分には参加しやすかったというのもあります。

www.infiniteloop.co.jp

CfPはこの2年ほど自分が現場で活用してきたユースケースという道具について、ざっくりと20分で話すという内容にしました。ユースケースの本来の意味の探求と、ユースケースシナリオを書く際に押さえたいポイント、そこから実際のクラス設計に落とし込むまでの流れを解説してみました。ICONIXについてもしばらく実践してはみたのですが、今の所本格的に取り入れるに至っていないので割愛しました。 

speakerdeck.com

続きを読む

ユースケースシナリオを中心に据えたドメインモデルの育て方

この記事はドメイン駆動設計 Advent Calendar 2020 - Qiitaの19日目です。 昨日はたなかこういち (@Tanaka9230) | Twitterさんの『DDDに関する論の主戦軸を整理してみた(2020年版) - Qiita』でした。

今日はドメイン駆動設計における主要な関心事の一つである「いかにドメインモデルを作って、育てていくか」という話を書きたいと思います。

エリック・エヴァンスの『ドメイン駆動設計』やヴォーン・ヴァーノンの『実践ドメイン駆動設計』には、ソフトウェアが扱う問題領域に存在する知識やルールをソフトウェアの要素(クラスなど)で直接表現することの価値とその実践方法が書かれています。ドメインモデルは問題領域に対する理解を表現したものであり、まずこれがなければドメイン駆動設計のプラクティスは実践できません。では開発チームがドメインモデルに到達するためのプラクティスにはどのようなものがあるのでしょうか?

その答えは世の中にいくつもあると思いますが、今回紹介するのは「ユースケース」を中心に据えた方法論です。ユースケースと聞いて多くの人が思い浮かべるのはユースケース図かもしれませんが、ここで重要となるのはユースケースシナリオ(ユースケース記述)と呼ばれる文章表現のほうです。ドメイン駆動設計はユビキタス言語を認識して言葉を大事にする設計手法です。ユースケースシナリオは自然言語で書かれるので、ユビキタス言語を直接用いてシステムの仕様を表現することができます。

自然言語で書かれているので、エンジニア以外でもほとんど予備知識無しに読み解くことが可能です。ドメイン駆動設計が開発者の中で完結した手法ではなく、顧客やプロダクトオーナー、ビジネスメンバーを巻き込んで行う必要がある以上、設計ドキュメントが誰にでも理解できる形式になっていることが非常に大きな意味を持ちます。ユースケースシナリオを中心に据えた手法は、ソフトウェアに関わる全ての人を設計の深いところまで連れて行くことができるのです。この性質はビジネス領域とテクノロジー領域を行き来して進化していくドメイン駆動設計をスムーズに回していく助けとなるはずです。

それでは、筆者が自分の現場でどのように実践しているのかを順を追って説明していきます。

続きを読む

ScalaMatsuri 2020でPofEAAについて話しました

去る10月の17日、ScalaMatsuri 2020で登壇しました。今年はオンライン開催だったため、自宅とコワーキングスペースからZoomとDiscordでの参加。オンラインでの大型カンファレンスは参加するのも登壇するのも初めてだったのですが、オンラインならでは工夫が随所に凝らされていて存分に楽しめました。

scalamatsuri.org

CfPが採択されたのが今年の3月末で、そこから登壇資料の作成やスタッフの方との接続テストを行ってから当日を迎えました。会場までの移動時間がかからないので、開会の挨拶にも余裕を持って参加できました。

お昼は近場の行きつけのカフェでランチ。スマホかとじゅんさんのセッションを視聴しながら食事を摂れるのも、オンライン開催ならではですね。

自分は15時からの40分枠で、『PofEAAで考えるSaaSバックエンドの作り方』と題して発表させていただきました。ScalaMatsuriは海外からの参加者も多い国際カンファレンスなので、拙い英語でなんとか資料を作りました。最初から英語で考えて、後から日本語訳をつけるほうが案外スムーズに進みました。Google翻訳で「英語→日本語」の変換をチェックしながらの作業でした。

speakerdeck.com

PofEAAネタはPHPカンファレンス福岡2018でDoctrine2の話をして以来です。今回はORMだけでなく、アプリケーション全体に様々なパターンを適用するお話です。ほとんどがAlpで実践した内容となっております。オフラインのカンファレンスのようなダイレクトな反応は味わえなかったのですが、Discordに質問用と雑談用のルームが用意されており、話しながらリアクションを確認できるのは良かったです。

お楽しみの懇親会では、日頃ScalikeJDBCでお世話になっている@seratch_jaさんや、最近何かとご縁のある@todokrさんなど、Scala界隈で活躍する方々のお話が聴けて貴重な時間でした(ほぼ聴き専と化していました)。Discordの部屋を大量に作って任意のところにいって会話するというやり方は、懇親会会場で「輪」に入っていくあの感覚に近いものがあったと思います。

今回は準備期間中に大きな方針転換を迫られ、スタッフやスポンサーの方々も色々と大変だったと思います。それでも開催に漕ぎ着けてくれたことに感謝しつつ、来年もまたこの祭りに参加できることを楽しみにしています。

Object-Oriented Conferenceで契約による設計について話してきました

去る先週の日曜日、Object-Oriented Conferenceに参加してきました。 オブジェクト指向好きとしてはド直球なテーマで、去年名前を聞いてからずっと楽しみにしていました。

ooc.dev

今の時代にあえてオブジェクト指向をメインテーマにするというのは、とても意義のあることだと思います。ドメイン駆動設計やマイクロサービスアーキテクチャがブームになる中で、そこに至るまでに先人達が歩んできた道筋を理解することなく、新しい概念や道具を使いこなすことは困難です。「何故」ドメイン駆動設計なのか、「何故」マイクロサービスなのか。オブジェクト指向を探求していけば、自然とその動機が見えてきます。

「時代の流れだから◯◯を使うべき」と言う人もいるでしょう。しかし、大抵の開発現場に必要なのは全く新しい何かではなく、これまで使いこなせていなかった道具に立ち返ることだと常々思います。それができて初めて、次のステップに進めるはずです。そうでなければ、新しいものにすぐに飛びついては失敗するというループを永遠に繰り返すことになります。

昨今、PHP界隈でもオブジェクト指向の話題が目立つようになってきました。関数型の要素が話題になりがちなScalaも、本格的なオブジェクト指向の機能を備えています。今オブジェクト指向を学ぶことは、ただ古い時代に思いを馳せることではなく、明日から使える古くて新しい道具を発掘することになるのです。

続きを読む

Scalaでイミュータブルなエンティティを実装する

この記事はScala Advent Calendar 2019 - Qiitaの13日目の記事です。

Scala界隈にはドメイン駆動設計を実践されている、または導入を検討されている方が多いかと思います。筆者が携わっているScalebaseプロジェクトでも、バックエンドAPIの実装にScalaを採用しつつドメイン駆動設計を念頭に開発を進めています。

www.scalebase.com

今回はその試行錯誤の経験の中から、Case Classを用いたイミュータブルなエンティティの特性について、感じたことを共有したいと思います。

ミュータブルなエンティティ

エリック・エヴァンスのドメイン駆動設計』では、システムが同一性を考慮しなければならないエンティティと、同一性を考慮しなくてよい値オブジェクトを区別して設計することが強調されています。エンティティは概念的に「変化するもの(ミュータブル)」であり、値オブジェクトは一度生まれたら「変化しないもの(イミュータブル)」です。

同一性を考慮する必要があるというのは、例えば現実世界の人間のように、身長、体重、年齢、健康状態、あるいは名前が変わったとしても、「同一の人間である」と捉えるべき存在であるということです。しかし、システム上は必ずしも人間がエンティティであるとは限りません。例えば映画館の来場者の数や属性を記録するだけのシステムでは、人間を表す値オブジェクトを設計するのが妥当な判断になるかもしれません。何をエンティティとして扱うべきかは、作っているアプリケーションによって異なるのです。

別の例としては、「銀行口座」もシステム上でエンティティとして扱われることが多い概念でしょう。今あなたは「口座管理システム」の設計を任されているとします。このシステムでは各口座の同一性を認識し、どの口座に、どの通貨が、いくら預けられているかを記録しておく必要があります。

f:id:dnskimox:20191213020903p:plain

一般的には同一性を確認するために一意な識別子(ID)を設け、データベースのユニーク制約などを用いて、システム内に同一のIDを持つエンティティがただ一つであることを保証することになるでしょう。上のUMLは「名前」と「金額」を表す値のグループを、それぞれ値オブジェクトとして定義することを示しています。また、口座IDも専用のクラスを設けて値オブジェクトにしています。まずはこれをScalaのクラスで表現してみましょう。

class Account(
  val id: AccountId,                               // 口座のID
  private var ownerName: Name,                     // 名義人名
  private var balances: Map[String, Money] = Map() // 通貨毎の口座の残高
) {
  assert(balances.forall(_._2.value >= 0), "残高が0未満になることはない")

  def getOwnerName: Name              = ownerName
  def getBalances: Map[String, Money] = balances
  def changeOwnerName(newName: Name): Unit = {
    ownerName = newName
  }
  def addMoney(newMoney: Money): Unit = {
    val existingMoney = balances.getOrElse(newMoney.code, Money(newMoney.code, 0))
    balances = balances + (newMoney.code -> existingMoney.plus(newMoney))
  }
}

case class AccountId(value: Int)                     // 口座IDを表すVO
case class Name(firstName: String, lastName: String) // 名前を表すVO
case class Money(code: String, value: Double) { // 金額を表すVO
  def plus(other: Money): Money = {
    require(other.code == code, "同じ通貨のみ加算できる")
    copy(value = value + other.value)
  }
}

IDが変化してしまうと同一性を認識できなくなるので、そこだけは定数とし、他のプロパティは全て変数としています。この形は「概念的に」ミュータブルであるエンティティを、コード上でもミュータブルなものとして表現しています。インスタンスを作り、リポジトリに永続化するコードは以下のようなイメージです。

val account1 = new Account(AccountId(1), Name("太郎", "山田"), List())
account1.addMoney(Money("JPY", 100.0))
accountRepository.store(account1)

ミュータブルな実装であれば、復元したエンティティに対して変更を加え、再度保存するというようなコードが素直に書けます。

val account1 = accountRepository.findById(AccountId(1))
account1.changeOwnerName(Name("次郎", "山田"))
account1.addMoney(Money("USD", 1.0))
account1.addMoney(Money("EUR", 2.0))
accountRepository.store(account1)

さらにリポジトリIdentity Mapパターンを組み込めば、同一のIDのエンティティがスコープの中にただ一つしか存在しないように保証することも可能です。もし同一のIDのエンティティが複数同時に存在するようなコードを書いてしまった場合、どれを保存すれば最新の状態が記録されるか判別が困難になるため、これは重要なポイントです。

www.martinfowler.com

続きを読む

PofEAAで考える値オブジェクトの永続化あれこれ

この記事はドメイン駆動設計#1 Advent Calendar 2019 - Qiitaの3日目の記事です。

エリック・エヴァンス氏の『ドメイン駆動設計』に端を発したDDDの設計哲学では、システムが同一性を認識しなければならないエンティティと、同一性を認識しなくて良い値オブジェクトを区別して設計することが重要であるとされています。例としてよく使われるのが「貨幣オブジェクト」です。

// 貨幣
class Money {
  String code; // 通貨コード
  Double value; // いくらかを表す数値
}

疑似コードなので雰囲気で読んでください。このMoneyクラスを使って「100円」というインスタンスを2つ作ったとします。システムはこれらのインスタンスが同一の貨幣を指しているのか、それとも偶然値が一致している(等価である)だけなのかをどのように判別すれば良いでしょうか? ここで「判別しなくて良い」と言えるシステムなら、貨幣オブジェクトを値オブジェクトとして扱うことが出来ます。極端な例として、日本中の貨幣にIDを振って管理する「貨幣流通管理システム」のようなものであれば、そうはいかないでしょう。

貨幣のようなものは、道端にポンと置かれていることはあまりありません。道端に落ちている貨幣は、誰のものかわからなくなります。システム内でも同様で、永続化対象となる値オブジェクトは何らかのエンティティの中に入っているはずです。IDを持ったエンティティの中に入っていなければ、永続化した値オブジェクトを再度取り出す手がかりが無くなってしまうためです。

// 財布
class Wallet {
  String id; // 財布のID
  Money money; // 財布の中身
}

では、このエンティティ内の値オブジェクトはどのようにデーターベースに永続化されるのでしょうか? オブジェクトをデーターベース、特にリレーショナル・データベースに永続化する際には、オブジェクトの形とテーブル構造を必ずしも一致させられないという問題があります(インピーダンスミスマッチ)。Walletのような複合的なオブジェクトを保存するには、何らかの実装方針を選択しなければなりません。

これついてはマーティン・ファウラー氏が『エンタープライズアプリケーションアーキテクチャパターン』(PofEAA)の中で答えてくれています。『ドメイン駆動設計』の出版より少し前の本ですが、値オブジェクトというアイデア自体はオブジェクト指向界隈にそれ以前からあったものなのです。PofEAAは実装上の課題一つに対していくつかの解決策を挙げ、それぞれどのような長所・短所があるのかを解説するというスタイルで書かれています。値オブジェクトの永続化に関しても、3つのデザインパターンが紹介されています。

小さなオブジェクトを一つだけ

まず上記に挙げたようにWalletオブジェクトの中に一つだけMoneyオブジェクトが入っているようなケースでは、Embedded Valueパターンが有力な候補になるでしょう。

www.martinfowler.com

Embedded Valueパターンは、親となるエンティティを永続化するテーブルに、値オブジェクトの各プロパティを分解して入れてしまうという方法です。例えば、walletsテーブルは以下のような定義になります。

CREATE TABLE wallets (
  id VARCHAR(32) PRIMARY KEY COMMENT '財布のID',
  money_code CHAR(3) COMMENT '通貨コード',
  money_value FLOAT COMMENT 'いくらかを表す数値'
);

永続化する際にはWalletオブジェクトの各プロパティと、Moneyオブジェクトの各プロパティをそれぞれ対応するカラムに入れます。そして復元する際には、まずMoneyオブジェクトのプロパティを取り出して組み立て、次にWalletオブジェクトのプロパティを取り出して、先に作っておいたMoneyオブジェクトの参照を持たせれば、元通りのWalletオブジェクトが手に入ります。この辺りの具体的な実装方法は、DDD界隈ではリポジトリの話題として扱われます。PofEAAの中でも様々なパターンが紹介されているので、気になる方はぜひ読んでみてください。

Embedded Valueのメリットは、エンティティの中にある小さな値オブジェクトのために個別のテーブルを作らなくて良くなる点にあります。また、エンティティを取得する際にテーブルのJOINが不要になるため、パフォーマンスにもいい影響を与えるでしょう。

続きを読む