アイコンよりどりみどりの PlantUML 構成図を書く

## モチベーション

- 色んなアイコン使いたい。
    - AWS アイコン (2018 以降 ver.)
    - 言語アイコン
    - 一般的なサーバーアイコン
        - etc.
- AWS アイコンのみの構成図サンプルは見つかるが、他はないの？
- 細かい定義・仕様はいいから、コードで語れ。

という方向け。

## 要件

- とある商品管理システム。
- フロントエンドは、 AWS S3 + CloudFront でホスティングした SPA。
- バックエンドは、Python + Flask on EC2。
- どこかのレコメンドシステムにも接続しているらしい。

### サンプル構成図

![plantuml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170621/c9efe453-31a9-ffc1-b769-5a6b07d2df29.png)

### サンプルコード

<details><summary>コード</summary><div>

```vb
@startuml
title 商品管理システム

' @see https://github.com/awslabs/aws-icons-for-plantuml/tree/v5.0
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/AWSCommon.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/Compute/EC2.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/Compute/Lambda.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/Database/Aurora.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/General/Client.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/GroupIcons/Cloudalt.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/NetworkingAndContentDelivery/CloudFront.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/NetworkingAndContentDelivery/ELBApplicationLoadBalancer.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/NetworkingAndContentDelivery/Route53.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/NetworkingAndContentDelivery/VPC.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/Storage/S3Bucket.puml
!includeurl https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v5.0/dist/Storage/SimpleStorageServiceS3.puml


' @see https://github.com/tupadr3/plantuml-icon-font-sprites/tree/v2.1.0
!includeurl https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.1.0/common.puml
!includeurl https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.1.0/devicons/java.puml
!includeurl https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.1.0/devicons/javascript.puml
!includeurl https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.1.0/devicons/nginx.puml
!includeurl https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.1.0/devicons/python.puml
!includeurl https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.1.0/font-awesome-5/file_excel.puml
!includeurl https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.1.0/font-awesome-5/server.puml
!includeurl https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.1.0/font-awesome-5/slack.puml
!includeurl https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.1.0/material/dns.puml

' @see https://github.com/Roemer/plantuml-office/tree/1.0.0
!includeurl https://raw.githubusercontent.com/Roemer/plantuml-office/1.0.0/office2014/Clouds/cloud.puml
!includeurl https://raw.githubusercontent.com/Roemer/plantuml-office/1.0.0/office2014/Concepts/firewall.puml
!includeurl https://raw.githubusercontent.com/Roemer/plantuml-office/1.0.0/office2014/Servers/cluster_server.puml
!includeurl https://raw.githubusercontent.com/Roemer/plantuml-office/1.0.0/office2014/Servers/datacenter.puml

' ''''''''''''''''''
' Overwrite default
' ''''''''''''''''''
show stereotype

!definelong AWSEntity(e_alias, e_label, e_techn, e_color, e_sprite, e_stereo)
rectangle "==e_label\n<color:e_color><$e_sprite></color>\n" <<e_stereo>> as e_alias
!enddefinelong

!definelong AWSEntity(e_alias, e_label, e_techn, e_descr, e_color, e_sprite, e_stereo)
rectangle "==e_label\n<color:e_color><$e_sprite></color>\n e_descr" <<e_stereo>> as e_alias
!enddefinelong

' 他UMLのキーワードを使用可能にするための設定
allow_mixing

' '''''''''''''''
' Declare object
' '''''''''''''''
actor "ユーザー" as user
Client(user_browser, "ユーザー端末", "") {
    DEV_JAVASCRIPT(user_js, "商品管理システム")
}

Cloudalt(aws, "AWS", "") {
    Route53(aws_r53, "", "")

    ' Frontend
    CloudFront(aws_cloud_front, "", "")

    Lambda(aws_lambda_auth, "Lambda@Edge Basic認証", "")

    SimpleStorageServiceS3(aws_s3_frontend, "", "") {
        S3Bucket(aws_s3b_frontend, "", "") {
            DEV_JAVASCRIPT(user_js_src, "React")
        }
    }

    VPC(aws_vpc, "", "") {
        ELBApplicationLoadBalancer(aws_alb, "", "")

        ' Backend For Frontend
        EC2(aws_ec2, "", "") {
            DEV_NGINX(aws_ec2_nginx, "")
            DEV_PYTHON(aws_ec2_web_api, "Flask") {
                component "一覧・詳細・新規作成・編集・削除" as aws_web_api_crud
                component "商品リストDL" as aws_web_api_dl
            }

            FA5_FILE_EXCEL(aws_ec2_output_data, "中間データ")
        }

        Aurora(aws_web_api_db, "", "")
    }
}

OFF_DATACENTER(other, "外部システム") {
    OFF_FIREWALL(other_fw, "")

    MATERIAL_DNS(other_dns, "DNS")

    FA5_SERVER(other_api_server, "") {
        DEV_JAVA(other_api_server_api, "レコメンド API")
    }
    OFF_CLUSTER_SERVER(other_es_master, "Elasticsearch cluster")
}

FA5_SLACK(slack, "")

' ''''''''''
' Data flow
' ''''''''''
user -- user_browser

' render app
user_browser <--> aws_r53
user_browser <-- aws_cloud_front: 画面(html + js)取得
aws_cloud_front <--> aws_lambda_auth
aws_cloud_front <-- user_js_src

' api call
user_js <--> aws_alb
aws_alb <--> aws_ec2_nginx
aws_ec2_nginx <--> aws_ec2_web_api

' events
aws_web_api_crud <--> aws_web_api_db
aws_web_api_dl <--> aws_web_api_db
aws_web_api_dl <--> aws_ec2_output_data

' 外部システム連携
aws_web_api_dl <--> other_fw
other_fw <--> other_dns
other_fw <--> other_api_server_api
other_api_server_api <--> other_es_master

' 異常系
aws_ec2_web_api -left[#red]-> slack: エラー通知

@enduml
```
</div></details>

※以下の Online PlantUML Editor で、ツールのインストールなしで描画できる
https://plantuml-editor.kkeisuke.com/

## 雑解説

- 必要な分は見せたということだ・・・で終わりでもいいのだが、いつかの自分のために覚書。

### !includeurl

- いわゆる import 文
- [サンプル](https://github.com/awslabs/aws-icons-for-plantuml/tree/v5.0#hello-world) では、`!define` で繰り返し部分を共通化している。
- しかし、個人的には邪魔・URL直でよいと思う。以下理由。
    - `!define` の変数管理が面倒くさいから。
        - 新たに include したい場合、GitHub から ([ファイルファインダー](https://help.github.com/ja/github/getting-started-with-github/keyboard-shortcuts#source-code-browsing) など使って) 探してくる。
        - で、見つけたら「Raw」を開き、URLをそのまま貼り付ければよい。
        - もしサンプルの通り `AWSPuml` という変数に置き換えていた場合、どこまでが変数値で、どこまでが追加する値か、把握・書き換えなければならない。
        - include リポジトリが 1 つならいいが、複数のリポジトリからアイコン参照するようになると、いちいち面倒くさい。
        - IDE サポートも多くは望めないので、変数の衝突回避も人間が注意する必要がある。面倒くさい。
    - `!includeurl` の後ろを valid な URL にできるから。
        - なので、そのまま開き、中身を確認できる。
        - モノによって、引数の位置や機能が違うとかあるので、いざという時すぐ確認できる方がよい。

### @see

- 使うリポジトリを記載することで、新たに include したい場合に探しに行きやすくしている。
- また、明示的にバージョン指定しておくことで、「知らない間に Breaking change があって壊れた」という事態を回避できる。

### Overwrite default

- もろもろ見やすさの試行錯誤と妥協の結果である。
- 以下を考えた気がする（正直もう忘れた）
    - AWS のアイコンとサービス名の対応を覚えるのは諦めたので、デフォルトでサービス名を表示させたい。
    - object を宣言した時に与えた名前を表示させたい。

### Declare object

- 各種 object の宣言、これまた面倒くさいのが以下。
    - 引数の定義（数・機能）が違うこと。
    - `!includeurl` の値を見ただけでは、どんな object が使えるようになったかわからないこと（いちいち中身を確認する必要がある）
- 例えば、 `EC2`
    - https://github.com/awslabs/aws-icons-for-plantuml/blob/v5.0/dist/Compute/EC2.puml#L10-L11
    - 以上の通り、引数は 2 or 3 個。
- 例えば、 `DEV_JAVASCRIPT`
    - https://github.com/tupadr3/plantuml-icon-font-sprites/blob/v2.1.0/devicons/javascript.puml#L54-L57
    - 以上の通り、引数は 1 ~ 4 個。
- 間違えた時のエラーメッセージもわかりづらいので、「あのアイコンはうまくいったのに、コピペしたこのアイコンはうまくいかない。なぜ？」みたいな時間泥棒に遭いがち。

### Data flow

- よい矢印模索中・・・
    - Read/Write 両方なら `<-->` とか、データの流れる方向に `-->` とか、`作用の主体 --> 客体` とか。でもこれでは矛盾が・・・とか。
    - もうちょっとハッキリ指針を決めたいところ。今後考える。
- 位置の強制
    - `-left->` のように、位置を強制できる。
    - 図が巨大になってくると、「どうしてもこっちに置いた方がわかりやすい」みたいな場合があるので、必要悪か。
    - Excel 構成図に比べて、「細かい位置・レイアウトを気にせず書けること」が PlantUML 構成図のメリットの 1 つなので、やりすぎは本末転倒。

### エディタ

- VSCode + [PlantUML](https://marketplace.visualstudio.com/items?itemName=jebbs.plantuml) がオススメ。
    - Syntax highlight やコード補完ができる。

## まとめ

- なんだか文句の多い紹介だった気もするが、実際のところ非常に役に立っている。
    - サッと書いたホワイドボードの手書き構成図を、記録用の清書にするなど。
- ただのテキストなので、特定サービスのアカウント作ったり、そのサービスの継続性の心配も必要ない。
- 他、PlantUML のメリットは検索すればいくらでも出てくるので割愛する。
- PlantUML はいいぞ。