[name=ken3ypa]
- GitHub がMermaid記法をサポート
- 決済まわりを触っていて、シーケンス図が頻出
=> これはすらすらかけるようになりたい
- markdown記法にインスパイアされた、JavaScriptベースの図表作成ライブラリ。mermaid記法を用いて、開発にまつわる諸々の図作成できる
- 公式:http://mermaid-js.github.io/mermaid/#/
- ライブエディタ: https://mermaid-js.github.io/mermaid-live-editor/edit
- フローチャート / シーケンス図 / クラス図 / ステート図 / ER図 / ユーザージャーニー / ガントチャート / 円グラフ…etc
- 似たようなライブラリとしてはPlantUMLがある。記法については差異はあるものの、出来ることはほぼ同じ(のように見える。深掘りはしていない)
- 記法の種類が多いので全部を学ぼうとすると途中で力尽きがち。作りたい図に絞って学ぶとよい
- 一つの図に絞れば記法はそう多くないので、2時間ぐらいやればガッツリ書けるようになる
ここではシーケンス図作成のために用意されている14の記法について見ていく
- シーケンス図における主体を記載できる。
- 省略も可能だが、participantを明示することで並び順を指定することが可能
sequenceDiagram
ken->>maki: 巻いてますか?
maki-->>ken: 巻いてます
ken-->>maki: ですよね…
sequenceDiagram
participant maki
participant ken
ken->>maki: 巻いてますか?
maki-->>ken: 巻いてます
ken-->>maki: ですよね…
- Participantsの亜種。図中で表示されるアイコンが棒人間になる
sequenceDiagram
actor ken
actor maki
ken->>maki: 今日も巻きますか?
maki->>ken: 今日は巻きませんね…
ken->>maki: えっ
participant, actor にAliasを指定できる。SQLっぽい
participant <alias_name> as <name>
actor <alias_name> as <name>
sequenceDiagram
participant k as ken
participant m as maki
k->>m: mってなんですか?
m->>k: 私の別名です
k->>m: そうですか…
sequenceDiagram
actor k as ken
actor m as maki
k->>m: mってなんですか?
m->>k: 私の別名です
k->>m: そうですか…
8種類の矢印と :
以降に指定する文言を元に、A to B へのメッセージを記載できる。
a->b: message
Type | 説明 |
---|---|
-> | 矢印なしの直線 |
--> | ドット付き直線 |
->> | 矢印あり直線 |
-->> | ドット・矢印あり直線 |
-x | 終端×・矢印あり直線 |
--x | 終端×・矢印・ドットあり直線 |
-) | かっこいい矢印 |
--) | ドット付きかっこいい矢印 |
sequenceDiagram
actor k as ken
actor m as maki
k->m: 矢印なしの直線
k-->m: ドット付き直線
k->>m: 矢印あり直線
k-->>m: ドット・矢印あり直線
k-xm: 終端×・矢印あり直線
k--xm: 終端×・矢印・ドットあり直線
k-)m: かっこいい矢印
k--)m: ドット付きかっこいい矢印
有効化・無効化を設定できる
sequenceDiagram
ken->>+maki: Transaction
ken->>+maki: ActivateB
maki->>-ken: DeActivateB
ken->>+maki: ActivateC
maki->>-ken: DeActivateC
maki->>-ken: Fin Transaction
Note <right of / left of / over> message でコメントを残せる
sequenceDiagram
ken->>maki: こんにちは
Note left of ken: これは日本での一般的な挨拶
sequenceDiagram
ken->>maki: 巻いてますか?
Note over ken, maki: ※ 初対面でこんな挨拶はリスクが伴う
sequenceDiagram
ken->>maki: ここで装備していくかい?
loop 「はい」って言うまで続く
maki->>ken: いいえ
end
sequenceDiagram
ken->>maki: ここで装備していくかい?
alt はい
maki->>ken: はい
else いいえ
maki->>ken: いいえ
end
opt ほい
maki->ken: ほい!
end
並行で起こっている事象について表すことができる
par [Action 1]
... statements ...
and [Action 2]
... statements ...
and [Action N]
... statements ...
end
sequenceDiagram
actor User
User->>Subject: submit
par Subject to Observer
Subject->>Observer1: call update
and Subject to Observer2
Subject->>Observer2: call update
end
Observer1-->>User: Notify
Observer2-->>User: Notify
rect <rgb/rgba> ~ end
で囲うことで、スコープ内のフローへ背景色を追加できる
rect rgb(0, 255, 0)
... content ...
end
rect rgba(0, 0, 255, .1)
... content ...
end
sequenceDiagram
participant Alice
participant John
rect rgb(191, 223, 255)
note right of Alice: Alice calls John.
Alice->>+John: Hello John, how are you?
rect rgb(200, 150, 255)
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
end
John-->>-Alice: I feel great!
end
Alice ->>+ John: Did you want to go to the game tonight?
John -->>- Alice: Yeah! See you there.
mermaid中にコメントを残したい場合は %% で記載可能。 つけたコメントはパーサから無視されるため、表示には影響しない。
sequenceDiagram
Alice->>John: Hello John, how are you?
%% this is a comment
John-->>Alice: Great!
エンティティコードを指定して文字や記号の実体参照ができる
sequenceDiagram
A->>B: #9731;
B->>A: #127759;
A->>B: #128511;
autonumber
を記載するか、optionを設定することでフローに番号を振ることができる
sequenceDiagram
autonumber
ken->>maki: どもども
ken->>maki: 元気ですか?
ken->>maki: あれ?
ken->>maki: おーい
ken->>maki: 聞こえますか?
link [participant name] link_name @ URL
で、participant をマウスオーバーした際にリンクを表示させることができる(が、esaやHackMD、GitHubなどmermaid をサポートしている各種ツールでもこの機能は未対応)
sequenceDiagram
participant Alice
participant John
link Alice: google @ https://dashboard.contoso.com/alice
link Bob: google @ https://dashboard.contoso.com/bob
Alice->>John: Hello John, how are you?
https://developer.amazon.com/ja/docs/amazon-pay-checkout/overview.html
sequenceDiagram
autonumber
%% 登場人物
actor b as #128102; 購入者
participant mf as #128722; EC運営者フロントエンド
participant mb as #128331; EC運営者バックエンド(API)
participant a as #128509; AmazonPayがホストしているページ
participant ab as #128331; AmazonPayBackend(API)
%% 決済処理の流れについて
mb->>mb: 「AmazonPayボタン」のペイロードとシグニチャを生成
mb->>mf: 「AmazonPayボタン」をレンダリング
mf->>b: ECサイトは「AmazonPayボタン」を含む<br> /product/cart/checkout ページをレンダリング
b->>mf: 「AmazonPayボタン」をクリック
mf->>a: 「CheckoutSessionConfig」を入力値として<br> AmazonPayプレオーダーページを起動
b->>a: Amazon Payでログイン
b->>a: Amazonに登録している住所・支払方法を操作し<br> 「続ける」をクリック
a->>mf: checkoutReviewReturnUrl(amazonCheckoutSessionIdを付与)にリダイレクト
mf->>mb: getCheckoutSession()のエンドポイントを叩く
mb->>ab: req: getCheckoutSession(amazonCheckoutSessionId)
ab->>mb: res: ユーザー入力の住所・支払方法を更新したレスポンスを返す
mb->>mf: 下記4つを含む注文確認画面を表示<br> 1: 選択した住所・決済方法と「変更」ボタン<br> 2: 合計金額と購入商品情報<br> 3: (もし指定が必要な場合)配送方法<br> 4: 「注文」ボタン
rect rgba(10,10,100,0.2)
alt 購入者が注文確認画面で更新する場合
b->>mf: 「住所 or 決済方法変更」ボタンをクリック
mf->>a: amazonCheckoutSessionIdを保持して<br>AmazonPayプレオーダーページを起動
b->>a: Amazonに登録している住所・支払方法を操作し<br>「続ける」をクリック
a->>mf: checkoutReviewReturnUrl(amazonCheckoutSessionIdを付与)にリダイレクト
mf->>mb: getCheckoutSession()のエンドポイントを叩く
mb->>ab: req: getCheckoutSession(amazonCheckoutSessionId)
ab->>mb: res: ユーザー入力の住所・支払方法を更新したレスポンスを返す
mb->>mf: 更新された情報をもとに注文確認画面を再度表示
end
end
b->>mf: 「注文」ボタンをクリック
mf->>mb: バックエンドに情報を送る
mb->>ab: req: updateCheckoutSession(amazonCheckoutSessionId, paymentIntent,<br>checkoutResultReturnUrl, amount, metadataなど)
ab->>mb: res: amazonPayRedirectUrl を含んだレスポンスを返す
mb->>a: amazonPayRedirectUrlへリダイレクトする
b->>a: リダイレクト先で下記のいずれかが表示される<br>1: 多要素認証/認証失敗フロー<br>2: checkoutResultReturnUrlへのリダイレクト
Note over b, a: ※ 多要素認証が必要か否かはAmazonPay側が判断
rect rgba(10,10,100,0.2)
alt 多要素認証が必要な場合
b->>a: 多要素認証を完了させる
end
end
rect rgba(100,10,10,0.2)
alt 認証が失敗した場合
b->>a: 認証失敗フローに進む
Note right of a: 多要素認証ページが再度表示されるケースもある
end
end
a->>mf: checkoutResultReturnUrl(amazonCheckoutSessionIdを付与)にリダイレクト
mb->>ab: req: completeCheckoutSession(amazonCheckoutSessionIdを付与)
ab->>mb: completeCheckoutSessionのレスポンス
alt CheckoutSessionStatusが「Completed」の場合
rect rgba(10,100,10,0.2)
mb->>mb: CcompleteCheckoutSessionのレスポンスに含まれるChargeID・ChargePermissionId を保存
mb->>mf: 購入完了ページにリダイレクト
mf->>b: 購入完了ページを表示する
end
else CheckoutSessionStatusが「Cancelled」の場合
rect rgba(100,10,10,0.2)
mb->>mf: 決済失敗ページにリダイレクト
mf->>b: 決済失敗ページを表示する
end
end
[name=makicamel]
-
ストリームとは
データを、比較的小さい単位が連続したものと捉え、上流から下流へ「流れるもの」とみなし、そのデータの入出力・送受信(途中段階を含む)を最小限の滞留とさせ低遅延処理となるように扱う形態を指す。またその操作のための抽象データ型を指す。 ストリーム (プログラミング) - Wikipedia
-
ふつうにファイルを読んだ場合、ファイルをすべてメモリ上に展開する
- ファイルが大きくなるとメモリの枯渇、パフォーマンスの悪化の可能性が出る
- ストリームはファイルの一部を読み込み・書き込みする
ー ハンズオン Node.js p.122
ref Net::HTTPResponse#read_body (Ruby 3.1 リファレンスマニュアル)
「TODO リストを返すサーバ」は以下のように実装できる
const todos = [
{ id: 1, title: 'ネーム', completed: false },
{ id: 2, title: '下書き', completed: true }
]
// req は読み込みストリーム、res は書き込みストリーム
const server = http.createServer((req, res) => {
// リクエストの URL や HTTP メソッドに応じて適切なレスポンスを返す
if (req.url === '/api/todos') {
if (req.method === 'GET') {
// GET メソッドの場合、全 TODO を JSON 形式で返す
res.setHeader('Content-Type', 'application/json')
return res.end(JSON.stringify(todos))
}
// GET 以外の HTTP メソッドはサポートしないため 405(Method Not Allowed)
res.statusCode = 405
} else {
// /api/todos 以外の URL はないので 404 (Not Found)
res.statusCode = 404
}
res.end()
}).listen(3000)
createServer
はコールバック関数を受け取る- コールバック関数は 2 つ引数を受け取る
- 第一引数はリクエストを表す。読み込みストリーム
- 第二引数はレスポンスを表す。書き込みストリーム
上記のサーバにアクセスするリクエストは以下のように実装できる
http.request('http://localhost:3000/api/todos', res => {
let responseData = ''
console.log('statusCode', res.statusCode)
res.on('data', chunk => responseData += chunk)
res.on('end', () => console.log('responseData', JSON.parse(responseData)))
}).end()
request
メソッドは書き込みストリームであるhttp.ClientRequest
オブジェクトを返す- リクエストは
request()
メソッドコール時ではなくhttp.ClientRequest
オブジェクトのend()
メソッドコール時に送信される
http モジュールは低レベルなので直接使うよりもふだんはフレームワーク経由で利用する
特によく使われるのは Express で、http モジュールを使うよりもシンプルに可読性高く書ける
// http モジュールで対応メソッドを増やす場合、if 文の数が増える
const server = http.createServer((req, res) => {
if (req.url === '/api/todos') {
if (req.method === 'GET') {
// ..
}
if (req.method === 'POST') {
// ...
}
// Express で対応メソッドを増やす場合、 Router の記述を増やす
// routes.todos.js
router.route('/')
.get((req, res) => {
// ...
})
.post((req, res) => {
// ...
})
module.exports = router
// app.js
// /api/todos 以下のパスに対するリクエストのハンドリングを ./routes/todos モジュールに移譲
app.use('/api/todos', require('./routes/todos'))
// ...
// http モジュールでパスパラメータの id を解釈する場合、正規表現を使う
const server = http.createServer((req, res) => {
const match = req.url.match(/^\/api\/todos\/(\d+)\/?$/)
if (match) {
// 数字部分を取り出す
const todoId = Number(match[1])
// ...
}
// Express でパスパラメータの id を解釈する場合、プレースホルダを利用できる
app.get('/api/todos/:id(\\d+)', (req, res) => {
const todoId = Number(req.params.id)
// ...
})
// http モジュールでクエリパラメータを取得する場合、URL API で URL をパースする
// URL は第一引数に req.url、第二引数にベースの URL を指定する
new URL('/api/todos?completed=true', 'http://localhost:3000')
_.searchParams.get('completed')
// => true
const server = http.createServer((req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`)
if (url.pathname === '/api/todos') {
const completedFilter = url.searchParams.get('completed')
// ...
}
// Express では特別な処理をしなくても req.quesry から取得できる
app.get('/api/todos', (req, res) => {
const completedFilter = req.query.completed
// ...
})