非同期Batch処理がしたいときのREST API設計

 こんばんは、日本はまだGWですが、こちらはMayDay以外一切の休みなく働いております。10連休羨ましい!!

 お久しぶりです、最近Scalaを全く使わないサービスへの異動になり、ひたすらJavaでScalaっぽいコードを書いては怒られているMunchkinです。

 今回はREST API(Restish APIの方が正しいかな)で非同期Batch処理を起動したいっていうお話です。正直自分でもまだ正解がわかっていないので、ガンガンマサカリを投げてください。

先に結論

この先の考察の結果として、僕は以下のような結論に至りました。

仮にすでに以下のようなリソース定義が存在するとします。

/api/v1/documents/{docType}/{documentId}

この時、特定のdocType全体に署名を追記する非同期batch処理を行いたい場合、batch処理自体をリソースと捉え、新たにパスを切り、各メソッドに操作を割り当てます。

/api/v1/documents/bathces/signingAppender/{jobId}
  • POST: ジョブの起動
  • GET: ジョブのステータス確認
  • PUT: 使用しない
  • DELETE: ジョブの停止

こういう設計にすることで

  • 非同期処理用のAPIを同期APIを切り離すことでAPIに統一性が出る
  • REST原則に則ったAPI提供ができる

と言ったメリットが出ます。

徹底的に改ざんされた、読まなくても差し支えない経緯

(以下の文章は、情報漏えいないよう、大幅に事実を変更された余り本題に関係ない経緯です。本題はコチラへ)

 日本は21世紀初の改元と10連休に湧いているらしい。結局、今年の春も桜を見る事なく過ぎていった。

 眼に映るのは、ヤシなんだかバナナなんだかわからない、やたら南国チックな街路樹と、昨晩のスコールで出来た水たまりを懸命に駐輪場から掻き出そうと汗を流す警備員のおじさんの背中だけである。

 僕はおじさんが普段座っている駐輪場の椅子で朝の一服を終えると、重たい腰をあげてオフィスのある13階へ向かった。

 すでに始業時間は過ぎているが、同僚はまだ数えるほどしか出社しておらず、また誰も作業を始めているようには見えない。そこで、僕も通勤途中に露店で買った、やたら甘いコーヒーとパクチが山盛りに入ったサンドウィッチで朝食にする事にした。

 僕がサンドウィッチからやたら甘い謎の肉塊を取り除いていると、チームリーダーのマオが話しかけにきた。

マオ「ねえ、Munchkin。君が一昨日上げてくれたREST APIの設計書なんだけど、ペンディングになってるチェーン各店舗のクーポンをまとめて作成して作成されたクーポンのmeta dataと画像のURLを返すやつ、そろそろ出来た?」
僕「あぁ、あのフランチャイズ店舗が好きに割引率や内容を決められるように、バラバラのクーポン作って配布したいってやつか。設計自体は終わったんだけど、ちょっと悩んでることあってね。」
マオ「へぇ、一応聞かせてもらっても良い?」
僕「前にF社さんからの要望で作った20件までまとめて作れるBatch APIがそのまま使えるかなと思ったんだけど、今回のT社さん、確か1000店舗以上だろ? QRコードの埋め込みもあるからいくら並列で流しても10分はかかるし、フルでそこにリソース回したら他が使えなくなるから、できれば非同期でBatch処理にしたいんだよね。」
マオ「それでいいと思うけど、なんか問題があるの?」
僕「いや、凄いくだらないんだけど、Pathをどうしようかなって。/api/v1/gourmet/{storeId}/coupons@POST使いたいなぁと思ったんだけど、これもうF社様の時の同期BatchAPIで使ってるし。そもそも僕非同期のREST APIなんて作った事ないからさ。」
マオ「なるほどね。(笑)まぁ、設計ができてるなら問題ないけど早めに決めてね。」

 さて、この非同期で行われるbatch処理を実行するAPIを、どのような仕様でどのようなURIに割り当てるべきかというのが今僕の頭を悩ませる大きな問題なのである。

本題

 上記の経緯を読み飛ばした人のために解説させていただきますと、画像埋め込みありのクーポンを千枚単位でAPI経由で発行したく、APIの要件は以下の3点です。

  • APIを通じて千枚単位のそれなりに重い処理を行う
  • REST原則にできるだけ従う形で非同期Batch APIを提供する
  • 非同期のため、ステータスを取得するAPIが必要

そして、問題としては

  • 既存Pathの中で最も近いリソースPath(/api/v1/gourmet/{storeId}/coupons)はすでに使用済み
  • 非同期処理はその他の同期でレスポンスが返されるAPIとは別の場所に置きたい

があります。

REST原則

 聡明なるQiita読者の皆様には確認する必要もないかもしれませんが、一応REST原則を確認しておきます。Wikiからの引用ですが、妥当性は確認してあります。

・ステートレスなクライアント/サーバプロトコル
・すべての情報(リソース)に適用できる「よく定義された操作」のセット
・リソースを一意に識別する「汎用的な構文」
・アプリケーションの情報と状態遷移の両方を扱うことができる「ハイパーメディアの使用」
’’’Wikipedia 「Representational State Transfer」より抜粋・引用’’’

 このREST原則のうち、”ステートレスなクライアント/サーバプロトコル”と”アプリケーションの情報と状態遷移の両方を扱うことができる「ハイパーメディアの使用」”は、そもそも僕たちが提供するAPI基盤の担当領域になるため、今回は一旦置いておきます。

 したがって、

  • "すべての情報(リソース)に適用できる「よく定義された操作」のセット"
  • "リソースを一意に識別する「汎用的な構文」"

をどのように実現するかが今回の肝になります。

リソースはなにか

 REST APIの設計に置いて、"リソースを一意に識別する「汎用的な構文」"、つまりURIから一意に特定できるリソースをどう定義するかは非常に重要な問題です。

 今回の場合、普通に考えればリソースは作成される対象のクーポンであり、リソースへのPathは/api/v1/gourmet/{storeId}/couponsが好ましいと考えられます。

 ただ、この形式でリソース定義すると以下の問題が避けて通れなくなります。

  • このPathはすでに処理が割り当てられている
  • 既存Pathへの変更は後方互換性を破壊するため行えない
  • 非同期であることがPathから読み取れない(API利用者にとっての利便性が下がる)

 上の2つについては僕たちの設計独自の問題ですが、3つ目の問題はかなりジェネラルで多くの開発者が突き当たる問題ではないかと思います。  ここまで来ると、クーポンをリソースとしたAPIは不適当なのではないかと思えてきます。

 そこで、今度はこの処理そのものをリソースであると考えてみます。  僕はREST APIの設計をする時にフォルダとファイルの関係で考えることが多いのですが、今回はBatch処理そのものをexeやshファイルだと考えて設計し直してみます。

 そうすると、以下のようなURIが考えられます。

/api/v1/gourmet/batches/coupons/generator

 これでURIを見るだけで、バージョン1のGourmetサービスのAPIで、クーポン作成のバッチ処理を行なうということがpathから一見でわかるようになりました。  ただし、このままだとリソース、つまり処理が一意に特定できません。したがって、Batchの処理にIDを通して処理を特定できるようにします。

/api/v1/gourmet/batches/coupons/generator/{batchId}

 良さそうに見えます。リソースとURIが決まったので、次はすべての情報(リソース)に適用できる「よく定義された操作」のセットについて考えていきます。

Batch処理に対するRESTfulな操作セット

 REST APIは同一のURIに対して異なるHTTPメソッドを投げることで指定されたリソースに対して操作を行います。  以下に挙げるのは一般的な操作とパラメータです。

Method Action Request Body Query Param
GET リソースの情報を取得する N/A フィルタやソートなどの条件、ページングなど
(e.g. ?max=100&order=asc)
POST リソースを作成したり処理を実行したりする 作成するコンテンツやメタデータなど 実行条件やエラー時の処理など
(e.g. ?skipIfExists=true&exclusive=true)
PUT リソースを置換・更新する 変更するコンテンツやメタデータなど フィルタや更新対象の指定、実行条件など
(e.g. ?fields=[location,mapUrl]&filter=[attendance=ATTEND])
DELETE リソースを削除する N/A 削除対象の絞り込み、実行条件など
(e.g. ?filter=[lastRef<=1507042549298]&exclusive=true)

 ちなみに、POST以外の操作には冪等性、つまり何度叩いても、リソースそのものに第三者から変更が加えられない限り、同じ結果が返ってくることを保証しなくてはいけません。

 先程決めたURI、/api/v1/gourmet/batches/coupons/generator/{batchId}に対してそれぞれの処理を割り当てると、次のような感じでしょうか?

  • POST:  Batch処理の起動
  • GET:   処理中Batchのステータス取得
  • PUT:   今回は使わない
  • DELETE: 処理中Batchの停止

 この操作セットなら、/batches/全てに適用可能な操作セットと言えるのではないかと思います。

 Deleteはステータスも含めた削除で良いかなと思わなくも無いのですが、停止したBatchは何回停止させても停止状態も停止した時間も変わらないので、べき等性は保証できています。

 API利用者からしたら誰かによって停止されたというステータスをGETで取得できたほうが利便性が高いように思われるので、DELETEは処理中Batchの停止を行なうという仕様にします。

出来たAPI仕様

 さて、上記の結論をまとめると、以下のようなAPI仕様になります。

FORMAT: 1A

# Async Batch API 


# Async Batch API of Coupon Service[/batches/coupons]

## Coupon Batch Generator [/generator/{batchId}]
This API is to create thousands of coupon in a single call.
You can create multiple coupons ASYNCHRONOUSLY.
You are able to get the status by polling if necessary.
This API is supposed to be used for the case of creating more than 100 tickets. 

You need to specify the {batchId} to specify which job you want to operate.
No format restriction, but recommended to use UUID to avoid the conflicts.

+ Parameters

    + batchId: 387396de-1a1c-4376-9403-3e2a4e2d484d (string) - An unique identifier of the batch.

### Launch Batch Coupon Generator [POST]
Launch Coupon generates batch.
Batch will be executed asynchronously.
You will get the response immediately, but it normally not contains any result.
You need to call the GET API to check the status and result.


+ Request Launch Batch Content (application/json)

    + Body

        { "targets": [{
              [couponId]:{
                "storeId":string, 
                "desctiption": string,
                "validFrom": Date,
                "expiredOn": Date,
                "discounts": [{
                    "itemId":string, 
                    "discountType":enum, 
                    "amount": number, 
                    "conditions":[{"conditionType":enum, "value": number}]
                }]
              }
           }]
        }

+ Response 200 (application/json)

    + Body

            { 
                "batchId": string,
                "status": enum,
                "acceptedAt": Date,
                "processingItems": number,
                "results": {
                  "success":[{
                    "couponId": string,
                    "imageUrl": URL,
                    "isPublished": boolean
                  }],
                  "fail":[{
                    "couponId": string,
                    "errorCode": number,
                    "cause": string
                  }]
                }
            }

+ Response 409 (application/json)

    + Body

        {"message": "Batch ID already exists and couldn't be launched."}

### Check the Batch Generate status [GET]
You can get the status of the batch you have launched.
Status contains
- ID of batch
- started date time 
- status of batch. Either of {WAITING, RUNNING, FINISHED, STOPPING, ABORTED}
- amount of accepted coupons
- result of batch
Created or failed items are added to the response content.
You can process the result of created items even though the batch is still running.


+ Response 200 (application/json)

    + Body

           { 
                "batchId": string,
                "status": enum,
                "acceptedAt": Date,
                "processingItems": number,
                "results": {
                  "success":[{
                    "couponId": string,
                    "imageUrl": URL,
                    "isPublished": boolean
                  }],
                  "fail":[{
                    "couponId": string,
                    "errorCode": number,
                    "cause": string
                  }]
                }
            }

+ Response 404 (application/json)

    + Body

        {"message": "Batch doesn't exist."}


### Kill the batch creation job [DELETE]
You can stop the batch job.
By kicking this job, you can kill the running job.
In order to stop the job safely, we will kill the job when the current processing item finishes its creation.
To make sure that the job has been stopped, you might need to call the GET API several times to check the GET API.
Even though the batch is killed, created items won't be deleted.
You need to delete them manually by checking the coupon Id of success list in the response.


+ Response 200 (application/json)

    + Body

           { 
                "batchId": string,
                "status": "STOPPING",
                "acceptedAt": Date,
                "processingItems": 0,
                "results": {
                  "success":[{
                    "couponId": string,
                    "imageUrl": URL,
                    "isPublished": boolean
                  }],
                  "fail":[{
                    "couponId": string,
                    "errorCode": number,
                    "cause": string
                  }]
                }
            }

良さそうに見えます。今回は一旦これで行こうかと思います。

まとめ

 以上、つらつらとBatch処理をREST APIで提供する方法を書いてきました。

 結論としては、Batch処理をAPIで提供する場合、処理そのものをリソースと捉え、処理に対する操作をメソッドに割り当てるのが良さそうだなということでした。

 実際、仮に/api/v1/gourmet/{storeId}/couponsが使えたとしても、同期処理と非同期処理が似たようなPathに混在するのはAPI利用者にとって気持ちの良いものではないと思うので、やはりこちらの方が良い設計なのではないかと思っています。

 ちなみに今回はこれでGoサインを出したのですが、正直これでほんとに正しいのか全く自信がなく、もし正しくはこうするべき、もしくはこの設計には問題があるなどコメントがあれば、どしどしいただけると幸いです。

 現在もう一本、認可と権限管理に関する記事も書いてるので、また近々お見えすると思います。  今週中に終わらせたいなぁ…それでは今回はこれで!

最近、自分の英語力が技術力や責任に追いつかなくなってきた。

こんばんは、まんちかんです。
先日公開したSingletonに関する記事がQiitaで週間ストック数ランキングに載って少し浮かれてます。

qiita.com

qiita.com

とはいえ、まだまだ13ストック、もっと良い記事をどんどんアウトプットしていきたい所存であります。

 さて、あまり記事を更新しないでおくと書く習慣が無くなりそうなので、また技術関連以外の記事をば。若干ネガティブな記事です。。

最近英語力の低さに足を引っ張られてる

 最近、自分の英語力が、技術力や与えられた責任に対して追いつかなくなってきてるなと感じる事がとても多いです。
 僕は業務上、アーキテクチャや設計に関して外国人のTech Leadの同僚とディスカッションしながら進めていくことが多いのですが、僕自身の英語力の低さがそのディスカッションの生産性を明らかに下げているのです。

 僕も技術者の端くれなので、週末には新しい技術を学び、新しい概念を覚え、その使い方やメリットデメリットをコードに起こしたり、記事やノートにまとめたりしてるわけです。
 しかし、いざその知識を最大限に活用して生産性を上げていかなくてはいけないという英語でのディスカッションの場で、自分の考えていることや学んだ知識を、日本語で説明できるほどに論理的に説明できないのです。

 最初は「なんだかうまく伝わらないな、相手の頭の良さを考えれば当然納得してもらえるはずなのに・・」と首を傾げていました。
 しかし、そう言った機会が増えるにつれ、だんだんと、自分の英語で説明してる事が全く論理的じゃない、むしろ覚えたことを断片的に説明してるせいで、全体でみると完全に支離滅裂になっていることに気づいたのです。

 当然ながら、自分の頭の中には論理的で、技術的にも信頼に足るはずの意見がそこに存在するのです。(そこは日本人のTech Leadたちと話す中で、ある程度は客観的に正しいと指示される理由が十分にあるということは確認しました。)
 しかし、口から流れ出る英語には一切の論理性が欠落し、断片的に提供される専門知識らしき何かが場を混乱させているのは明らかなのです。

 また、それだけではありません。同僚の中には同じく非英語ネイティブの中国人の同僚もいるのですが、彼の理論の展開の早さに、僕の英語力が追いつけないのです。
 彼の言ってることに対して、日本語での意見や反論は浮かびます。しかし、その反論を的確に英語で表現しようと四苦八苦する間に彼は次の議論を展開させる。もはや僕は彼にとって足手まといでしか無くなってしまっているのです。
 同じ非英語ネイティブゆえに、言い訳もできません。彼と同じ役割を期待されるには、僕の能力はあまりに低い。

今まで、真面目に英語を勉強したこともなかったし、英語に苦しめられたことはなかった。

 一応コメントしておきますが、僕は決して英語が苦手なわけではありません。むしろ、自慢じゃないのですが、と言いつつ自慢なのですが、どうも英語は才能があったらしく、中学校で初めて英語を勉強し始めて、中学二年生くらいには簡単な会話はでき、中学を卒業する頃には普通の日常会話はこなせるようになっていました。

 初めての海外はシンガポールで、大学生の時に英語力を買われて、ある会社の社長の代理で出張したのがきっかけでした。
 そのままの流れでいくつかの国で働き、今でも海外に住み、仕事も八割英語だし、日本語が喋れない外国人の子と付き合ったこともあるし、facebookの友達も半分以上が非日本語話者です。

 何かを考えるとき英語で考えて英語でレスポンスするというやつも、英語が日常になった頃から自然にできるようになりました。むしろ、英語話者と話してる時に日本語で考えることは稀で、それができないことはネイティブと話してる時一番の足かせになることも経験的に理解しました。
 つまり、英語って僕にとって頑張って勉強するものではなく、ナイフとフォークの使い方や自転車の乗り方同様に、生きていれば自然に覚えて自然に上手くなる日常習慣みたいなものだったんですね。

 だからこそ、自分が英語で自分の考えを論理的に説明できない事がとてつもなくショックでした。  今までだってディスカッションはたくさんして来たし、その中で自分自身の技術力や思考力の不足が足かせになったことはあっても、英語力そのものが足かせになった経験はなかったのです。
 むしろ、だからこそ圧倒的に先を行く彼らに追いつくために、必死で技術の勉強をして来たのですから。
 しかし、今、やっと技術者としてそこそこの信頼を得るに至った今、かつて歯牙にもかけた事がない英語力というスキルが自分に欠如していることに気づいてしまったのです。

言語を甘くみていた。

 考えてみれば当たり前のことなんですよね。
 扱う概念が高度になれば、それを説明するための言語表現も高度化する。
 その言語表現の高度化に対応するためには、言語そのものに関する知識や使用する経験がどうしても必要になる。

 とはいえ、これは言い訳なのですが、少なくとも僕は、言語で表現する能力に関して、僕はそれが訓練を必要とする能力の1つであるという認識はしてたつもりでした。
 しかしそれは、概念を自分なりに解釈し、論理性と情報を失わないように並べ替え、あとはそれを適当な言語で説明するだけ。つまり、重要なのは最初の2つの能力で、言語で説明するというのは終端処理、言ってみれば、書き起こしたパワーポイントをプリンタで印刷するくらいの誰にでもできることだと思っていたのです。
 こう言った言語そのものに対する思い上がりに、まさかこんな取り返しのつかない段階になって足を掬われるとは思ってもみなかった。

これから

 僕は海外で働くのが好きですし、これからももっといろんな国で働きたいと考えています。
 しかし、今の英語力でもっと責任のある仕事や決断を求められる場面、後輩や部下を引っ張って行く場面や今より高度な教育をしていかなきゃいけない場面に対応できるとはとても思えません。

 ジョルノジョバーナじゃないですが、僕にも人には明かしたくない目標があります。
 その目標にはどうしても英語は必要不可欠になりますし、こんなところで躓いたまま前に進むわけにはいきません。だからこそ、今一度、英語という奴に真面目に向き合わなきゃいけないのかなと感じています。
 むしろ、今気づけたこと、気づかせてくれるような同僚と働けていること、そんな同僚に引き合わせてくれた人達に巡り会えたことに、心ばかりの感謝しているという事を述べて今回の記事を締めたいと思います。

 なんか意識高い系の人みたいになっちゃったな、ま、いっか(笑  それでは!

JavaのSingleton実装方法5つとメリットデメリット使用場面とかいろいろ書いてみる

 こんにちは、G1GCを学んでからJVMにならクリーンされても構わないと思ったり思わなかったりするJava/Scala開発者です。

 昨日久しぶりに真面目な技術関連以外の記事を書いたのですが、全くアクセスがなくて落ち込んでいます。
そこそこいい内容だと思ったんだけどな…世知辛いのじゃあ…

話変わって、最近めっきり寒くなってきましたね…そろそろバイク通勤が辛くなってきました。
今日も今日とて朝はとても暖かったのに、帰りはザーザーぶりで、死ぬ思いをしながらバイクを漕いで帰ってきました。
いや、本当に雨の日にバイクなんて乗るものじゃない。。。

さてさて、たまにはDesign Pattenの話をば。
 

 Singletonパターンというデザインパターンはとても有名なので皆さんご存知かと思いますが、Javaにはいろいろと言語の仕様を使ったSingletonの書き方があるので、それを紹介していこうと思います。

 メリットを説明するときに、オブジェクト指向上唯一でなくてはいけないインスタンスを生成できるとか、抽象的な説明をされる事が多いデザインパターンですね。
 個人的には外部と通信したりミドルウェアアクセスしたりみたいな、重くて副作用の強い箇所を集めたうえでabstract factoryと組み合わせてFlyWeightチックに実装したりするときに使ったり、逆に完全なfunctionalな実装が必要な箇所で、Util系のクラスを使わずもっとオブジェクトチックな実装をするときに使うのが好きです。
 Singletonパターンではないですが、Spring BeanのSingletonポリシーとかは前者の使い方に近いSingletonの使い方してるのかなという印象ですがどうでしょう。
(ここは結構まさかりが飛んできそうなので一旦見逃してください。もっといい使い方あるよという方はぜひコメントに!)

 それでは本題!
 ちなみに、コードのコメントを日本語で書くのがあまり好きではないので、コメントだけ英語にしてあります。どこかからパクってきたわけじゃないです。

普通のSingleton

 class値にInstanceを持たせて、コンストラクタをprivateにすることで、外部クラスからのnewを禁止し、唯一性を担保する方法。

class NormalSingleton{  
  // Singleton instance.  
  private static final NormalSingleton INSTANCE = new NormalSingleton();  
  // private constructor to prevent instantiation from other classes.  
  private NormalSingleton() {}  
  // static method to get the instance.  
  public static NormalSingleton getInstance() {  
    return INSTANCE;  
  }  
}  

 みなさんご存知、一番普通な書き方ですね。軽いinstanceであればこれで十分です。instanceはstaticメンバなので、JVM起動時に読み込まれます。
 INSTANCEをpublicにする書き方もあるらしいですが、個人的にはクラスメンバは直接参照されたくないので、public staticメソッドから呼び出します。

メリット

・実装がシンプルで簡単
・初学者にもわかりやすい
・スレッドセーフ

デメリット

・JVM起動時に必ずインスタンスが生成されるため重いインスタンスには不向き
・コンストラクタがリフレクションでアクセス可能なので厳密にはシングルトンではない

用途

汎用的な実装方法なのであえて挙げるのもあれですが、あえて挙げるなら
・学習
・サーバサイドで頻繁に使われることが事前に分かっているクラス
・重くはないがオブジェクト指向的な意味でSingletonにしたいクラス
あたりでしょうか?

遅延生成

初期値を持たせず、最初のgetInstanceメソッドの呼び出しで Instanceを作成し、二度目以降は同一のinstanceを使わせる方法。

class DelaySingleton {  
  // field for singleton instance without any initial value (null)  
  private static DelaySingleton instance;  
  // private constructor to block the instantiation from other class.  
  private DelaySingleton () {}  
  // method to get the instance  
  public static getInstance() {  
    // create instance only in initial call of this method.  
    if (instance == null) {  
      instance = new DelaySingleton();  
    }  
    return instance;  
  }  
}  

 こちらもお馴染み、遅延生成するパターンです。少し重めのクラスなんかでよく使われます。実装は楽で明快なのでよく使われますが、実はこれ見ての通りThread Safeではないので、並列処理を行う環境では次に紹介するSynchronizedを使用してThreadSafeにしたパターンが使われます。

メリット

・実装がシンプルで簡単
・初学者にもわかりやすい
・重いクラスの生成を使用される直前まで遅延できる

デメリット

・非スレッドセーフ
・コンストラクタに加えinstanceまでfinalではないため、リフレクション経由で書き換え可能になり、よりSingletonではなくなる

用途

・学習
・マルチスレッドを使用しないバッチ処理など

SynchronizedでThreadSafe

 インスタンスを生成する箇所を同期ブロックで囲み、強固にnewの呼び出しを一回に絞る方法。

class SynchronizedSingleton {  
  // field for singleton instance without any initial value (null)  
  private static SynchronizedSingleton instance;  
  // private constructor to protect the instantiation from other class.  
  private SynchronizedSingleton(){}  
  // method to get the instance  
  public static SynchronizedSingleton getInstance(){  
    // check if the instance is already instantiated.  
    if(instance == null){  
      // block the multiple access from multiple thread  
      synchronized (SynchronizedSingleton.class) {  
        // check again, can be non-null,   
        // since the instance already generated just at the process just before   
        if(instance == null){  
          instance = new SynchronizedSingleton();  
        }  
      }  
    }  
    return instance;  
  }  
}  

 これもまた結構お馴染みかもしれません。デザインパターンの本や多くの入門記事で紹介されている方法です。ちなみにこの書き方、正直僕は嫌いです。

メリット

・スレッドセーフ
・重いインスタンスの生成を使用される直前まで遅延できる

デメリット

・最初の段階で大量にリクエストが来るとsynchronizedはパフォーマンス劣化につながる事がある
・コンストラクタに加えinstanceまでfinalではないため、リフレクション経由で書き換え可能になり、よりSingletonではなくなる
・冗長で書きづらい上に読みづらい

用途

・学習
・マルチスレッドを多用するバッチ
・tomcatなどで運用されるサーバサイドアプリケーション 
Synchronized型のSingletonは結構問題が多いので、個人的には後述のBill Push Singletonを使うことが多いです。

Bill Push Singleton

インナークラスのクラス値は初回参照時までメモリに読み込まれないというJavaの言語仕様を利用したSingleton。

class BillPushSingleton{  
  // class to hold the instance as final  
  private class SingletonHolder{  
    // this instance won't be loaded to memory until initial reference  
    // normally, class value will be loaded into memory when the class is loaded to JVM with class loader.  
    private static final BillPushSingleton INSTANCE = new BillPushSingleton();  
  }  
  // private constructor to prevent instantiation from other classes  
  private BillPushSingleton() {}  
  // method to get the instance  
  public static BillPushSingleton getInstance() {  
    // this instance will be instantiated at the initial call only.  
    return SingletonHolder.INSTANCE;     
  }  
}  

(僕の知る限り)日本ではあまり馴染みがない書き方かもしれませんが、海外の開発者が好んで使う書き方です。僕も簡素でエレガントだなと思うので、基本的に遅延生成のSingletonを作る場合はこの書き方を使います。

解説

 一応解説すると、通常のクラスのクラス値はクラスローダがクラスをJVMに読み込んだ時点でインスタンス化され、ヒープのpermanent領域に保持されます。(permanent領域がわからない人はオライリーのJavaパフォーマンスを読もう!)
 しかし、インナークラスに宣言された定数は、その親クラス自身が参照された段階ではnewされず、親クラスからそのインナークラスへの参照が発生した段階で初めてnewされてpermanent領域に保持されることになります。(実はこれ自体はクラスローダの仕様で、決して特殊なhackをしているわけではないのです。)

メリット

・実装がシンプルで簡単
・スレッドセーフ
・重いインスタンスの生成を使用される直前まで遅延できる
・Synchronizedによるパフォーマンス劣化の心配がない

デメリット

・リフレクション経由でインスタンス生成ができるため厳密にはSingletonではない
・JVMやクラスローダの知識がない人に説明するのがだるい(僕がこの記事を書いた一番大きな理由)

用途

・マルチスレッドを多用するバッチ
・tomcatなどで運用されるサーバサイドアプリケーション 

ちなみにこれでもまだ厳密なSingletonではありません。コンストラクタが残ってしまっている以上、どうしても厳密なSingletonにはできないのです。

Enum Singleton

Enumはグローバルに唯一のインスタンスであるというJavaの言語仕様を利用したSingleton

enum EnumSingleton {  
  // enum with only one element.  
  // this element will act as same with instance of other impl pattern.  
  INSTANCE("Now it is clear for you to see this is singleton, ahh??");  
  // Constructor to make this sample code looks like class.  
  // this constructor is unable to be called even from reflection as Java lang spec.  
  private EnumSingleton(String dummyMessage) {  
    this.dummyMessage = dummyMessage;  
  }  
  // dummy filed to make this sample code look like the other impl pattern  
  private String dummyMessage;  
  // dummy method to show this enum can work like normal class.  
  public String sampleMethod() {  
    return "this method is sample to show this is not just the normal enum.";  
  }  
}  

 こちらの実装方法は結構有名なのかもしれませんが、Javaの言語仕様としてEnumは厳密なSingletonであり、リフレクション経由でも新しいインスタンスを作ることはできません。
 enumとして実装されたSingletonだけが、本当のSingletonなのです。ちなみに僕は普通にキモいなと思うので、知識として知っているだけで使った事がありません。

メリット

・厳密なSingleton
・スレッドセーフ

デメリット

・実装がキモい、enumにする意味がわからない
・インターフェイスや抽象クラスが使えず、他のデザインパターンと組み合わせにくい
・遅延生成ができない
・なぜそうしたかと問われるとうまく説明ができない

用途

・マルチスレッドを多用するバッチ
・tomcatなどで運用されるサーバサイドアプリケーション 
・理想と現実の乖離に耐えられなくなって泣き出した開発者をなだめる

まとめ

 色々と僕の知ってるSingletonパターンの実装方法を紹介してきました。個人的にはBill Push Singletonと普通のSingletonだけ使えればいいかなと思ってます。
 ちなみにenum singletonをけちょんけちょんにいっていますが、それなりに伝統のある書き方なので、使ったからなんだということではないです。個人的な志向の話です。
 それでは!

背中を見て学べってそんなに悪だろうか

 たまには技術関連以外の記事をば。

 背中を見て学べ、という教育方法は、ここ最近あちらこちらで、それはそれはいろんな言い回しで批判されにされまくっている。
 自分がきちんと理解してないから人に教えられないんだとか、教育は上司の仕事なんだから見て覚えろは上司の怠慢だとか、あなたたちの世代とは必要な能力が違うんだから見て覚えるにも限度があるとか。
 もっとも、批判している本人たちはこの教育方法そのものよりも、むしろ特定の個人を頭に描き、その人物に面と向かって言えないことを、より大きな問題に消化して鬱憤を晴らしているように見えなくもない。その気持ちも理解できなくもないが、なんとなく、みっともないな、かっこ悪いなというのが、同世代の僕からの個人的な感想だ。

新人達の言い分と同情すべき上司達

 とは言え感情的な面では違和感を覚える一方、理論的に言えば彼らの言っていることも理解できなくもない。僕の大学時代の専門であった人的資本論的に言えば、技術革新が進めば進むほど、一つ一つの仕事を行うのに必要な知識や経験の量(人的資本の蓄積)は大きくなる。 昔は経験と感で行えたものが、新しい技術を使用することで、より効率的に行えるようになった一方、ある程度の専門知識を必要とするようになる。
 おそらく専門性を振りかざすまでもなく、誰もが経験的に理解していることだと思う。

 このように、社会が要求する能力がより高度にならざるを得ない以上、その能力の蓄積を助けるのは社会の責任であり、日本は体面上はその責務を果たしてきた。
 かつては大半が中卒で就職していた人々が、やがて高等学校を卒業するのが普通になり、だんだんと大卒の割合が増え、最近ではマスター以上の学位を要求する職種も少なくはなくなった。
 最も、ご存知の通り、それぞれの教育機関はその役割をきちんと果たしているとは言えず、またかつての終身雇用時代の名残からか、企業も学生たちに対して専門性を求めることはしない。専門性を求めない以上、業務に必要な専門性を教育するのは企業の役割であり、その教育の最前線にいるのは先ほどの槍玉に挙げられ続けている上司や先輩と呼ばれる人々だ。先ほども述べたように、技術が進めば進むほど押せなくてはいけない知識や経験の量は増える。つまり教育に割かれる時間も増えるのだ。

 あぁかわいそうに、教育のプロというわけでもないのに、自分の仕事もこなしながら、何もできないひよっこに一から手取り足取り教えなきゃいけない。その上少し手を抜けばツイッターで叩かれ5ちゃんで叩かれ上司にも叩かれ、ろくなもんじゃない、なんて日だ!
 っと、まぁそんなこんなで、変わる技術と変わらない社会、そしてその中にあるまた小さな社会の構成員の上と下、それぞれがうまい具合にバランスをとってこの不遇な状況を生み出している。
 僕から見れば、どちらかと言えば上の人の方がかわいそうだ。なにせ下の連中には社会に支持されガチな、わかりやすい正義がある。
 上の連中は立場上弱音も吐けず、社会に支持されるわかりやすい正義も持たず、自分を支持しない社会に押し付けられた余計な責務を日々全うしなきゃいけない。かわいそうで涙が出てくる。出てこない。

背中を見て学ぶのは意外と楽しい

 まぁ、別に上の人間を擁護することも、下の人間を叩くことも、本題ではないのでそろそろ本題に移りたい。
 少なくとも、背中を見て学べ、見て覚えろというのは、僕の人生経験上はそれほど悪いものではなかった。例えば武道や絵、ギターなどの趣味は、自分で軽く勉強したりさわりだけ教えてもらった後、実際に誰かが実演するのを見たり聞いたりして、模倣しながら覚えた。英語もエリック・クラプトンやクイーンの歌詞を覚えたり、学校に来ていたELT(外国人教師)の話し方を真似たりするうちに自然に覚えていった。
 僕の現職であるシステムエンジニアリングなど最も顕著だ。まず自習でHello World!をコンソールに出力することに始まり、DBのアクセスの仕方やHttpリクエストの仕方をネットや社内に散らばる他の人のコードから覚え、そのうちそれらのコードからデザインパターンやアーキテクチャの設計パターンを知り、そうやって新しく知った概念を、書店に走り、KIndleをあさり、ネットサーフィンしたりしながら独学で身につけていく。
 その中で誰かが手取り足取り教えてくれるのは環境のセットアップやバッチの叩き方くらいだろう。(この業界はアウトプットを共有する習慣があるので少し生きやすいところがあるのは事実だが。)

 誰かの模倣に始まり、振る舞いを観察し、その理由を解釈し、解釈したことを実践し、実践する中で問題に突き当たり、解決策を考え、何となく身について来たところで体系的な知識を学ぶ。そういったプロセスを経て、完全に自分のものにするという営みは、おそらくやったことがない人には想像もつかないくらいファンタスティックだ。超楽しい。
 ジムに通い始めて数日経ったある日、ふと胸板が少し厚くなってるのを感じた瞬間を思い出してほしい。あぁいう感じだ。

模倣はオリジナリティの源泉

 誰かの模倣をしながら、仕事や技能を徐々に身につけていくということは、また別の楽しみもある。それは、そのプロセスの中で、少しずつ自分なりの解釈や本質を認識し、オリジナリティを付加していけることだ。
 センター試験か何かの現代文の問題で、筆者自身がツイッターで「筆者そんなこと考えてなかったわ、ごめんな受験生笑」みたいなツイートをしてるのを見たことがある。あれは実はすごく面白くて、筆者自身がそういう認識で書いていなかったわけだが、読み手が論理的に解釈することで、その文章から新たな意味や視点を引き出している。これは筆者自身がそう認識していなかったとして、それが論理的に筋が通ることであれば、現実世界では正解といって良いものだと言うことだ。と、個人的には思う。
 見て学ぶというのも同じことで、誰かの模倣を繰り返す中で、その人の振る舞いの意味を自分なりに解釈し、その解釈を自分の行動の根底に置くことができる。
 例えば、なんでこのおじさんは座ってるお客さんに話しかけるときは少し首をかしげるのだろう、なぜこのお姉さまはループ処理でたまにStreamを使ってたまにforEachで回しているのだろうなど、模倣する対象の人が意識的もしくは無意識的に行なっている行動に自分なりの解釈を行う。
 その解釈を他の仕事や作業に応用することで、その人から直接教わった後模倣していただけでは得られなかったオリジナリティが生まれる。

 実際、StreamとforEachのかき分けくらい具体的な行動であっても、実は本人はなぜそういう風に書き始めたのかを言語化していないことは意外と多い。今までの経験上そう書いた方が効率が良いのは理解しているが、いざなぜかと問われると言語化するのに時間がかかる。
 なにせ本人にとってはもはや反射で行える程度には内面化しており、わざわざ言語化して記憶して置く必要がないものだからだ。
 こういった類いのものは、普通、事前に手取り足取り教えようとしてもなかなかパッと出てこない。

 当たり前だ、なにせ本人にとってはもう癖の領域のことなのだ。具体的な場面に遭遇することなく網羅して後輩や部下に教えることなどできない。
 しかし、例えば僕がその行動を自分なりに解釈し、先輩がこうしてる理由ってもしかしてこういうことですか?と問いかければ、彼女・彼はもしかしたらその問いをトリガにうまく言語化して説明してくれるかもしれない。  もし認識が違ったり、もしくは自分の解釈が実は優れていたりした場合、ディスカッションを通じてまた新しい視点をお互いが獲得できることもあるだろう。

 僕は、オリジナリティの源泉は本質に対する解釈とその抽象化、そしてそれらの概念の組み合わせだと思っている。正解があらかじめ上司や先輩から提示されていたとしたら、解釈に多様性は生まれないし、最終的な到達点はその人のクローンが関の山だろう。
 僕は中二病患者なので、誰かのクローンになりたいとは思わない。だからこそ、逆に誰かの模倣から始め、解釈を加え、オリジナリティを自分の行動に付加していくことは、誰かのクローンから脱却するために必要な手段だと考えている。これもまたファンタスティックだ、とても楽しい。

いいサブタイが浮かばないけどちょっと別の話

 では、誰かに手取り足取りきちんと教えてもらいながら覚えていく方法では、そういった体験はできないのかといえば、決してそんなことはない。ただ 頻度の問題だ。
 普通に生きていればなんで彼はこうするのか、なぜ彼女はこういう方法をとるのかと考える場面に出くわすことは決して珍しいことではない。ただ、事前にやり方や手順、その意味を誰かに教わっている場合、その理由を考える機会は失われる。つまり、考える頻度が減ってしまうのだ。
 背中を見て学べというような上司のもとでは、自然にそういう観察や解釈を行う機会が増える。これは実はとても良いトレーニングになる。こういう機会なしに誰かの動きや振る舞いを観察したり解釈したりを日常生活で行うのは、人間観察が趣味と声高に叫ぶ自意識過剰気味な大学生か研究が趣味の領域にまで達した文化人類学者などの人文科学者くらいのものだろう。

 高度な技術を持つ人間ほど僕にマンツーマンで指導してくれるような時間はない。ゆえに彼女や彼の技術を盗もうと思ったら、見て盗むしかないのだ。

 普段から見て盗む、見て学ぶということが習慣になっている人間とそれ以外では、仮にそういった人たちと話したり一緒に仕事したりする機会があったとき、得られるものには大きな差が出てくるだろう。
 なんでも教えてくれない上司や先輩というのは、最初は少しやりづらく感じたり、理不尽に感じたりすることはあるかもしれないけれども、なんとか食らいついていくと意外と学ぶことは多い。

自己擁護とまとめ

 一応断りをいれておくけれども、当然ながら、見て学んでも得ることがない上に効率が悪いことはある。例えば、経費精算の仕方や年末調整の仕方など、方法が決まっていて考える必要がないことに関しては、さっさと聞いてさっさと終わらせた方がいい。(自分が経理や人事担当者なら話は別だけども)
 そういうことは、上司なり担当の社員なりにバンバン聞いてさっさと終わらせてしまった方がいい。それすらも教えないというのであれば、上司がそのくだらないことに貴重な人件費を使っていいと判断したということなので、思う存分見ながら書き写してしまえばいい。それもダメならその時はそれこそ正義を振りかざしTwitterに正論の皮を被った愚痴を思う存分書き込めばいいと思う。

 まとめると、上司も自分の経験や知識をきちんと言語化できている訳ではなく、全てを事前に教えてもらえるというのは期待しない方がいい。自分なりに上司や先輩の背中を見ながらなんとなく解釈したり修正していくと、能力が自分の内面化するだけでなく、ただ教わるだけでは得られなかったオリジナリティが得られるかもよということだ。

 まぁ、言語化しないとか文書化しないのは絶対悪なんだけど、なんでもかんでもそうできる訳じゃないし、背中を見て学べ論も頭ごなしに否定しないでフォローしてみるといいことあるかもよっていう、それだけ言いたかった。  以上。

DynamoDBが全てのテーブルを暗号化。。。?

何やら不思議なニュースが見えたので翻訳していこうかと思います。 amazonがdynamodbの全てのデータを暗号化したというように見えます。 いつも通り、非公式の翻訳になるので、責任は取りかねます。気になった方は原文をご自身でご確認ください。

Amazon DynamoDB encrypts all customer data at rest

Amazon DynamoDB is a fully managed, nonrelational database that delivers reliable performance at any scale. Because of the flexible DynamoDB data model, enterprise-ready features, and industry-leading service level agreement, customers are increasingly moving to DynamoDB sensitive workloads such as financial and healthcare data, whose compliance regulations mandate data encryption. Amazon DybamoDBは完全にAWSによって管理された、どのようなスケールにおいても信頼に足るパフォーマンスを提供する非リレーショナルデータベースです。
DynamoDBの柔軟なデータモデル、エンタープライズ向け機能群、および業界最先端レベルの規約により、多くのお客様が金融データやヘルスケアデータなど、コンプライアンス規約によりデータの暗号化が求められる機密情報のワークロードをDynamoDBベースに移行しようとしています。

DynamoDB has encrypted all existing tables that were previously unencrypted by using a default AWS owned customer master key (CMK). When creating a new table, you can now use either the default AWS owned CMK or an AWS managed CMK. DynamoDBは全ての既存テーブルのうち、まだ暗号化されていないものに関して、デフォルトのAWSによって保持されるCustomer Master Key(MSK)によって暗号化しました。
新しいテーブルを作成する際には、デフォルトのAWSによって保持される鍵、もしくはAWSマネージドのCSKを使用して暗号化することができます。

Encryption at rest greatly reduces the operational burden and complexity involved in protecting sensitive data. DynamoDB encrypts data using industry-standard AES-256 algorithms, which ensure that only authorized roles and services can access sensitive data with access to the encryption keys audited by AWS CloudTrail. With encryption at rest, you can build security-sensitive applications that require strict encryption compliance and regulatory requirements.
今回のEncryption at restによって、顧客の機密情報に関する多くの複雑性やオペレーション負荷が大きく軽減されます。DynamoDBの暗号化には業界標準のAES-256アルゴリズムが使用されており、認可されたロールおよびサービスのみに対して、AWS Cloudtrailによって監査される秘密鍵を使用して機密情報に対してアクセスを許可することができます。

You do not have to make any code or application modifications to encrypt your data. Encryption at rest using the AWS owned CMK is provided at no additional charge. DynamoDB handles the encryption and decryption of your data transparently and continues to deliver the same single-digit millisecond latency that you have come to expect.   暗号化に祭して、あなたのアプリケーションやそのコードを変更する必要はありません。 Encryption at restはAWSによって保持されるCMKを使用しており、追加費用が求められることはありません。
DynamoDBはあなたのデータの暗号化と複合化を透過的に行い、これまで同様、あなたがDynamoDBに期待する一桁ミリ秒台のレイテンシを提供します。

These new encryption features are available in all standard AWS Regions. To learn more, see Amazon DynamoDB Encryption at Rest. これらの新しい暗号化機能は全ての通常AWSリージョンで使用可能です。さらに詳しい情報に関してはAmazon DynamoDB Encryption at Restのページをご覧ください。

Collections.emptyMapとCollections.EMPTY_MAPの違いを理解してなくて同僚にこっぴどく怒られたので自戒のためにここに書き記す

こんばんは、C1C2コンパイラの仕組みを知ってからJVM大好きっ子になったJava・Scala開発者です。(自分の呼称が安定しない)
 今日(もう昨日か)、同僚に指摘されて、初めてCollectionsの空Mapの違いに関してきちんと認識したので、次回の意味も込めてここに投稿しておこうと思います。
Javaは初めて書いた時からかなり経つのですが、非常に初歩的でありながら全く知らなかったので、いい勉強になりました。

まず結論から

本題とそのあとの解説はグダグダ長いので、先に結論から書く。
Collections.emptyMap:
新しい空のMapインスタンスを作成するため、変数や返り値の型にrow typeを使っていない限りは型推論され型安全である。
Collections.EMPTY_MAP:
row typeなcanonicalオブジェクトのためジェネリクスに型指定がなく型安全ではない。

オラクルの公式JavaDocにもきちんと記載されている。
https://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#emptyMap%28%29

Unlike this method, the field does not provide type safety. (このメソッドと違い、フィールド(のEMPTY_MAP)は型安全ではありません)

グダグダと長い本題と怒られた経緯

 昼食を終えデスクでオライリのマイクロサービスアーキテクチャをパラパラと流し読みしていると、日本で働く外国人の同僚(仮にライナスと呼ぶことにする)から、いささか不穏なSlackメッセージが届いた。

ライナス:プロフィールにon Vacationって書いてあるけど、まだ休暇中?
僕:こころは無限に休暇中…(嘘です戻りました)どうした?
ラ:いや、君のコードがあまりにひどくて泣きたくなっってきてね…
僕:言うねえ…
ラ:自分で見てゴミだと思わないかい?
僕:Repositoryとしては問題ないと思うんだけど…
ラ:Repositoryねえ…

 中々に剣呑である。(彼の名誉のために言っておくが、彼は技術面でのみ、特にくだらないミスや汚いコードに対してのみこのような言葉遣いになるが、普段はこの上なく紳士的でユーモアのあるナイスオタクガイである)

 言い訳をすれば、前期の追い込みの時に書いたクラスだったのでいくつか問題点があるのは認識していたが、そこまでひどい設計だとは思えなかった。
 彼はバックエンド班のリーダでなかなか多忙な人物なので、空いた時間でフィードバックをくれるようにお願いしてその場は別れた。
 夕方、ある程度作業が落ち着いてきた頃、彼からまたメッセージが届いた。

ライナス:そのクラスの問題点にはそろそろ気づけたかい?

 僕は思いつく限り設計の問題点をあげ、彼の反応を待った。
 しかし、彼から帰ってきたのはもっと単純な答えだった。

いや、きみね、そういう大層なことを言う前にまず基本をやりなさいよ。
1. ImmutableMap.Builder retvalBuilder = ImmutableMap.<String, T>builder(); これraw typeじゃん
2. return Collections.EMPTY_MAP;return Collections.emptyMap(); の違い知ってる?
3. return this.getXXXByIds(Arrays.asList(id)).getOrDefault(id, null); JavaDocでNPEに関してメンションしてる割に、nullをきちんと処理していないじゃん。なんでOptional返さないの? そもそも getOrDefault(x, null)get(x) は同じじゃん

 1と3に関しては見た瞬間、赤面するほど恥ずかしかった。穴があったら入りたい。素直にごめんなさいするしかない。反省の意味も込めてここに晒しちゃう。
 なにせ、こう言う基本的なことは僕自身がまた別の国で働く後輩たちに対して、レビュで繰り返し教えていることだったからだ。
 しかし、2に関しては全く問題だとは思っていなかった。と、言うのも、Collections.EMPTY_MAPはcanonicalかつImmutableだ。どうせ変更される事がないならcanonicalオブジェクトを使った方が(誤差ほどではあるが)メモリにも優しいし良いかなくらいに思っていた。

しかし、いざJavaDocを読むと、その意味がはっきりした。
きちんと書いてあるのだ、返り値にEMPTY_MAPを使う危険性が。

Unlike this method, the field does not provide type safety. (このメソッドと違い、フィールド(のEMPTY_MAP)は型安全ではありません)

そう、型安全ではないのだ、EMPTY_MAPは。
僕たちJavaプログラマは Javaのもつ強力な型づけ機能に守られて生きている。
非型安全なコードを書くプログラマは、両親に守られて生きていながら両親を軽んじる、反抗期の中学生と大差ないこの上なく未熟な存在なのだ。

(言い過ぎましたごめんなさい)

てか、そもそもなんでSingltonなわけでもないのに定数とメソッド二つ用意されてるの?

 Collections.EMPTY_MAPは、今の基準からすればそもそも必要のない定数なはずだが、リリースされたVersionを見てその理由がわかった。

Collections.EMPTY_MAPはJava3でリリースされ、Collections.emptyMapがリリースされたのはJava5なのだ。

 今でこそ昔からいましたよと言う顔で鎮座するジェネリクスだが、この機能がJavaに入ったのはJava5、結構最近である。(僕がJavaを始めた頃は型の指定が出来なかったのでいちいちcastしていた。)

 つまり、Collections.EMPTY_MAPはジェネリクスがない時代に作成されたため、型の指定を持たないcanonicalオブジェクトとして作成されたが、Java5になってジェネリクスが解禁されたため、より型安全なCollections.emptyMapメソッドが作成されたと言うことだろう。
 当然標準ライブラリは後方互換性を保たなくてはいけないため、Collections.EMPTY_MAPは今も元気に野を走り回っているわけである。
 

 ……でも、正直これ使い道あるのかな。Deprecatedに指定されてないってことは多分想定された用途があるんだろうけど、個人的に今のJavaで正しく使う使い方がわからない。どなたかご存知だったらコメントで教えてください。

まとめ

 Collectionsクラスの定数であるEMPTY_MAPは非型安全で、emptyMapメソッドは型安全。なので、基本的にJava5以上はemptyMapメソッドで空のMapを作った方が安全ということですね。

 小さなことではあるのですが、僕もライナスもJavaやJVM(そしてあらゆる言語や基盤、OS開発者)に対してこの上なく敬意を払っているので、こう言う小さなミスも十分怒られてしかるべきことだと認識しています。
 Javaを書き始めて結構たちますが、まだまだ知らないことがたくさんあって、本当に勉強のしがいのある言語だなぁと感じますね。

 ちなみに冒頭で触れたマイクロサービスアーキテクチャに関しては、別の記事をブログとQiitaでシコシコ書き溜めてるので、後日公開します。

 本題に全然関係ないまとめになっちゃったな、まぁいっか。では!

オライリー・マイクロサービスアーキテクチャを読む ~サービスのモデリングとコンテクストの境界~

読まなくていい導入

 マイクロサービスアーキテクチャというやつの出自を調べてわかった事は、どうやら彼は、僕たちが勝手にいだく彼への印象とそう変わりない、表裏のない人間だという事だ。
 彼の性格は非常にシンプルで、装いもこの上なくスマート、また人当たりも柔らく気さくでおしゃべりなナイスガイだ。
 しかし、彼について調べていくうちにふと気づいたことがある。彼は確かに気さくではあるが、どうも自分と他人との間に明確な壁を作っているようだ。
 彼自身は「なに、あまり深く関係を築きすぎると、いざって時に離れ難くなるだろ?」などと軽口を叩いていたが、どうもそこには彼なりの一貫した哲学のようなものがあるらしい。

さて、オライリー・マイクロサービスアーキテクチャを読むの2回目です。 (前回の記事はこちら)
前回はまとめごとにクソみたいなコメントを差し込むことで記事を読みづらくしてしまっていたので、今回は先にだーっとまとめを書いて、そのあとコメントを書いていくようにしようかなと。
一個一個コメント書くとすごい勉強になるけど、その代わりめちゃくちゃ時間がかかるしね。
それでは本題へ。

二つの概念

優れたマイクロサービスを設計するには、まず二つの重要な概念を常に念頭におく必要がある。
それは高凝集性疎結合である。

疎結合と高凝集性

関連する振る舞いをするものは同じ箇所に、関連しない振る舞いをするものは違う場所におく。
サービスを独立してリリースするためには、個別のサービスの振る舞いを変更するのに必要な箇所が一箇所にまとまっていなくてはいけない。
変更箇所が散在していると多くのサービスを同時にデプロイしなくてはいけなくなり、デプロイのコストとリスクが高まる。
関連する振る舞いを定義したソースは一箇所で管理し、それぞれのサービス同士の境界が疎結合になるようにドメイン設計する必要がある。
ネットワークを介して通信を行い、他のサービスとの結合部分を疎にすることで、サービスの独立性は保たれる。

境界づけられたコンテクストとドメインモデル

サービス同士の境界に関して設計する時、エリックエヴァンスがドメイン駆動設計の中で定義した境界づけられたコンテクストは重要な概念となる。
全てのドメインは内部に複数の境界を持ったコンテクストを保持する。それぞれのコンテクストは内部に境界の外部に対して秘匿されたものと、外部に対して共有されるものの二種類をモデルとして持つ。
この境界づけされたコンテクストは外部に対して公開されたインターフェイスを持ち、そのインターフェイスの設計が他のコンテクストと共有されるモデルを決定する。
前回の記事でメンションした通り、マイクロサービスの境界はドメインコンテクストと一致させる必要があるため、サービスはドメインコンテクストによって分割され、共有されたモデルによって外部と協調する。
逆に言えば、境界づけられたコンテクストから情報が欲しい場合は、この共有されたモデルを通じてコンテクストから情報を引き出す必要がある。

エリック・エヴァンスのドメイン駆動設計を読んだことがない人向けの説明  

ドメインというのは、大雑把に言えば画像共有や会計といった、システムが価値提供の対象とするビジネスそのもののことです。
境界づけられたコンテクストというのはそのドメインの中で画像タイムラインや領収証管理といった業務領域や関心範囲のことを言います。  

ちなみに境界づけられたコンテクストの定義に関して、当書の中で筆者は「明示的な境界によって強制される特定の債務」という言葉が一番気に入っていると述べています。

ドメイン駆動設計におけるモデルというのはそれだけで一記事書けそうなくらい難しい概念なのですが、簡単にいうと現実に存在する物質や概念をシステムの関心範囲のみで抽象化し、プログラム上で現実のビジネスを表現するために作成されるもの、みたいな感じです。  
例えばInstagramのような画像共有システムにおける投稿画像というモデルは、その画像そのもののデータ、どこで撮ったか、いつ投稿するかなどは関心範囲となり得ますが、その画像がiPhoneで取られたかニコンのD7500で取られたかなどはシステムの関心範囲ではないので捨象されます。(カメラガチ勢のカメラコミュニティサイトなら話は別ですが。)  

ドメイン駆動設計自体がプログラムと現実世界のビジネスを一致させることで、現場の人間と開発者の意識が対等に会話できることを目的の一つとしてるので、プログラムの概念の振る舞いや属性の実装を、できる限り人間が現実に行う挙動と合わせるための洗練されたオブジェクトみたいなものかなぁと僕は理解しています。(DDDはクソほど難しいのでまだ勉強中です。)

コンテクスの境界の定め方

コンテクスト境界はドメインに対する知識が不十分な状態で定めようとすると、ドメインに対する知識が深まった段階で境界認識が誤っていたことに気づき、疎結合を解かざるを得なくなることもある。
そのためドメイン知識が不十分な段階で急いでマイクロサービスに分割する必要はなく、徐々に分割していく方法を撮った方がいいケースが多い。
具体的には、モノリシックシステムにおけるモジュール化などの手法を用いて関連するコードを一箇所にまとめ、あらかじめできる限り疎結合になるように設計しておき、モジュールの境界がコンテクストの境界と一致する場所でサービスを分割するという手法がある。
全てのモジュールの境界がコンテクストの境界とは限らないが、モノリシックに作成されたシステムの中でモジュールの境界はコンテクストの境界としては非常に有力な候補である。
いずれにせよ、新規システムを始める場合、最初からマイクロサービスへの分割を急ぐ必要はなく、モノリシックから初めてコンテクスト境界を見定めるべきである。

ビジネス機能とコンテクストのインターフェイス

 コンテクストの境界とインターフェイスについて考えるとき、なんのデータを共有するかから考えると、データに対するCRUDだけを提供する貧血症な共有モデルに行き着くことが多い。
 インターフェイスは外部に対して公開したいサービスの機能そのものであり、「このサービスの責務は何か」「このサービスがその責務を全うするためにどのようなデータが必要か」という観点からモデリングしていく必要がある。

マイクロサービスのネスト

 あるマイクロサービスが徐々に肥大化してきてサービス分割を考える段階になった際に、取りうる手段は二つある。
 一つは完全に独立した対等なマイクロサービスを複数作成し、それぞれと直接コミュニケーションすることで機能を提供する方法。
 もう一つはあるサービスを受け口にして、このサービスが新たに分割された子サービスとコミュニケーションをとる方法である。
 前者はマイクロサービスの独立性が高くなるが、後者の方法をとると元の大きなコンクスト境界を残したままサービスを分割できる。
 外部から見た場合、大きなコンテクスト境界が内部的に分割されてようが、ただ巨大なマイクとサービスであろうが関係なく、また入れ子にすることで内部ロジックを外部コンテクストから秘匿化できるメリットもある。
 また、テスト時にも入れ子型のマイクロサービスは利点がある。入れ子型のマイクロサービスは、元の大きなコンテクスト境界の境界ラインを外部に対して保ち続ける。そのため、外部のマイクロサービスはこのコンクストから提供される機能を、内部的のより小さいコンテクストに対して気にかけることなくスタブすることができる。
 どちらの手法をとるかは、開発の組織体制によるものが大きい。分割された小さなサービスを全て元の大きなサービスを担当していたチームが管理するなら入れ子型の方がいいだろうし、独立した別個のチームで開発を行うのであれば、対等なトップレベルのサービスに分割するのが正しい手法となる。

以上。コメントは後日別記事で。 ではでは。