注目の記事

Atom と PlantUML で快適シーケンス図駆動開発ライフ

2016.11.29

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

サーバーサイド開発担当のエンジニアが「設計と実装を進めようとしている」という背景で話を進めます。

PlantUMLは強い

「認識合わせ」という名目でホワイトボードに図を書いて会話することがよくあります。共通言語で会話してあいまいなところを少なくしたら、マネージャーも安心感がありますし、プログラマも自分がやるべきことに集中できますね。

…3日経ちました。あのとき描かれていたホワイトボードの図のとおりに、実装することになりました。認識の齟齬をなくしてくれた貴重な図です。写真に撮りました。どこに保存してたっけ。やっぱり変更したくなったらどうしましょう。またホワイトボードに書き起こす?DRYじゃないですねえ。

そこで、UML図 が登場します。表現したい図を電子データで作成、保存できて、あとで見るときも役に立ちますね。が、しかし、UML図はそれはそれでやや手間がかかるところもあります。作図を助けてくれるツールやサービスはたくさんありますが、

  • 描画ツールを使いたくない
  • 差分管理ができないのが辛い
  • メンテするのがいやだ。どこにあるかわかりづらくなる

というのが私の感想です。

矢印の太さや、長さ、コンポーネントの位置は関心事ではないのでできれば道具にまかせたい。そこで今回はPlantUMLが定める記法でUMLのテキストを書いて、それを図に変換します。加えて、Atomのパッケージplantuml-viewerを使ってリアルタイムプレビューできるようにします。

テキストで書けると差分管理できるようになる点が良いです。履歴が残ります。GitHubの機能が使えます。つまり、変更に対して レビューをしてもらえ、コメントをもらえます。 描画ツールでやろうとするとレビュー方法に工夫が必要ですし、もらったコメントの管理が大変です。GitHubの仕組みに乗せてしまうことでそのあたりの課題が一気に解決できます。

シーケンス図は強い

UMLには種々の図がありますが、こと設計〜開発段階においては シーケンス図 が強力です。どのようなコンポーネントがあって、それらがどうやりとりするかを視覚的に捉えることができるためです。

シーケンス図というとオブジェクトがあって、そのオブジェクトのメソッドを実行すると他のオブジェクトが生成される…といったような図をイメージするかもしれませんが、「コミュニケーションをとりつつ進める設計段階」ではもっと大きな粒度、データベースや、外部APIといった粒度をオブジェクトにします。クラスやメソッドレベルのやりとりはコードで十分表現可能 である一方、コンポーネント間のやりとりは外部仕様として日本語ベースになっている ことが多く、後者はたとえ厳密だとしても可読性が低いです。シーケンス図はそれを補完するのに役立ちます。次の図は「ニュースの一覧を外部から取得してクライアントに返すAPI」のシーケンス図です。

00

みてわかるように、それほど綿密に書き込みをしているわけではありません。各コンポーネントがどういうやりとりをしていて、そのときどんな情報を受け渡しているか、という程度です。これで十分です。 設計書はこのシーケンス図に加えてコンポーネントの境界についてインターフェースを明記すると良いでしょう。例えば、JSONの構造やプロトコルなどです。

やること

せっかくなので、この記事ではPlantUMLを使い始めてからシーケンス図を作って、レビューをもらい開発を開始するまでの流れを例として示そうと思います。以下の順で話を進めます。

  1. PlantUMLを導入する
  2. Atomパッケージ plantuml-viewer を使いながら編集する
  3. シーケンス図を生成する
  4. GitHubでレビューしてもらう

1. PlantUMLを導入する

JARで提供されています。ダウンロードページから落としてください。このJARはテキストに記載したUMLのDSLを画像へ変換する際に使います。

2. Atomパッケージ plantuml-viewer を使いながら編集する

まずパッケージをインストールします。

10

デフォルトでは Option + Control + p でプレビューウィンドウが立ち上がります。.puなどのPlantUMLに由来する拡張子に反応してくれるようです。あとは、編集ウィンドウで作業するとすぐにプレビューへ反映されます。

20

3. シーケンス図を生成する

編集しおわったら、画像ファイルを作成します。

$ cd /path/to/plantUML/file
$ java  -Dfile.encoding=UTF-8 -jar ~/installed/plantuml/path/plantuml.jar ./*pu -o out

これで、outディレクトリに画像ファイルが生成されます。生成した画像ファイルは、Markdownの仕様書に埋め込むように使います。

4. GitHubでレビューしてもらう

シーケンス図をレビューしてもらいます。生成したシーケンス画像を埋め込んで、簡単な設計書をつくりました。

30

ソースコードと同じようにドキュメントのプルリクを出します。プルリクにコメントが付きます。

50

本領を発揮するのはここです。画像だけみてもどこが修正されたからわかりづらいですが、もとはテキストで記載されているので、どこが修正されたか一目瞭然です。これは非常に強力な特長だと感じました。

51

無事に指摘対応がおわり、マージされました。マージされたシーケンス図をベースに、開発を進めます。快適!

おまけ

UML図は、こんなシーンでも役に立ちます。

PM:「ニュースAPIからとってきたデータってキャッシュする?」

しますよ。シーケンス図に書いてました。TTLは1分に設定してます、というように、プログラミング時に役立つだけでなく、仕様の再確認をする際の証憑にもなります。あとから再確認が発生しやすいのはどうしてもコンポーネント間の連携部分やインターフェース部分に集中するためドキュメントもこのあたりを充実させておくと安心です。

開発者:「複雑なクラス関係になった。プルリク送るときに説明したほうがいいかな」

シーケンス図ではなくクラス図ですが、簡単なクラス図をプルリクエストにそえることで、クラス関係の見通しがよくなります。

70

クラス図も、「クラス内にどのようなメソッドがあるか」であったり、「どんなフィールドがあるか?それはpublicかprivateか?」といったようなことは書いていません。やはりコードを見ればわかる からです。ここで表現したいのはクラス関係と継承であり、図としてはクラスと矢印だけで十分です。

図を書くとなると、見やすい図をつくる、きれいな絵を描く、という方向に流されがちです。そうではなく、「何を表現して何を伝えたいのか」を定め、必要十分な情報を書けば良いです。もし足りない要素があるとすれば、レビュー指摘をもらえるはずなのでGitHubでプルリクを出してチームメンバーに見てもらいましょう。

この記事で使ったPlantUML

@startuml{news-after-login.png}
'画像変換するときにここで指定したファイル名になる。

title ニュース一覧取得API(ログイン後)

hide footbox
'シーケンスの縦線の下部にシーケンスボックスを出すかどうか指定する。よほど長いものでない限りいらないだろう

actor Webアプリ as user
'actorにすると人っぽい見かけになる

participant API as api
'participantにすると四角になる

database Redis as redis
'databaseにするとストレージっぽい見かけになる

database DynamoDB as dynamo
'as の左部が表示名、右部が変数名

participant NewsAPI as news

user -> api : ニュース一覧取得リクエスト
' -> 同期メッセージという意味になる。

activate api
' オブジェクト生成的な意味合いになる。

api -> redis : AccessToken
note right : db:9, ttl:60s
redis --> api : userId
api -> dynamo : userId
note right : table:metadata
'矢印の右側にコメントボックスを表示してそこにコメントを表示する、という意味

dynamo --> api : ユーザープロフィール

group 公開ニュースの取得
'区切りがわかりにくくなってしまう場合はgroup - endで囲うとよい

api -> redis  : 公開ニュース取得
note right : db:11,ttl:60s
redis --> api : 公開ニュースのJSON

alt キャッシュなし
'分岐を表現したいときは分岐ケースをalt - endで囲う

api -> news : 公開ニュース取得
news --> api : 公開ニュース
api -> redis : 公開ニュースキャッシュ
end
end

group ユーザニュースの取得
api -> redis  : ユーザニュース取得
note right : db:11,ttl:60s
redis --> api : ユーザニュースのJSON
alt キャッシュなし
api -> news : ユーザニュース取得
news --> api : ユーザニュース
api -> redis : ユーザニュースキャッシュ
end
end
api -> api : プロフィールから配信ニュース抽出、\n公開開始日時でソート
api --> user : ニュース一覧

@enduml

他の記法やシーケンス図以外のUML図についてはこちらのサイトを参考にしてください。

参考