🏪

コンビニで住民票を誤発行しないシステムを考えてみた

2023/05/14に公開

はじめに

マイナンバーカードを使ってコンビニで住民票を発行するシステムについて、どうやったら誤発行を防げるかを考えてみました。というより、サクッとサンプル実装を作ってみました。実際に印刷はできないのでPDFを作成しています。

https://github.com/ikemo3/conveni-pdf-example

UUIDを使う必要性はない

今回のシステムの要件として、次の3つが考えられます。

  1. 誤発行は絶対してはいけない
  2. 求められるスループットはそれほど多くない
  3. 接続できるクライアントの場所や数は管理できる

こういうシステムの場合、UUIDを使う必要性は全くありません。採番処理が集中管理できるのならそれが一番楽です。もし採番処理を分散させる必要があるなら、3番目の要件から、クライアント(この場合はプリンタか)に番号を割り振って、 {yyyymmddhhmms}-{クライアントのID} とすれば十分です。

今回は2番目の要件から、集中管理で十分だと考えてサンプル実装を作ってみました。

システム概要

サンプル実装のシステムはざっとこんな感じです。

  • クライアント: コンビニのプリンタ
  • リクエストAPI: 印刷イメージ作成を要求するためのAPI
    • 単に名前を渡していますが、実際はマイナンバーカードから取得した文字列(たぶんsubみたいなもの)を渡す感じかと
  • 結果取得API: 作成したPDFを返すためのAPI
  • PostgreSQL: シーケンスとテーブル
  • Redis: ワーカーに要求を投げるためのキュー
  • 共有ボリューム: PDFを共有するためのボリューム
  • ワーカープロセス
    • キューからデータを受け取る
    • 住民票作成に必要なデータを取得
    • データからPDFを作成

誤発行を避けるための仕組み

PostgreSQLのシーケンスを使っています。原理的にはPostgreSQLの serial やMySQLの auto_increment でも可能です。

シーケンスのデフォルトはbigintで、開始が1とすると、2^64 / 2 = 9223372036854775807まで使えるようです。日本の人口が約1.25億人なので、1人が1秒ごとに住民票を発行して2344年かかる計算です。

create sequence request_id start 1;

このシーケンス値を元に、 YYYYMMDDhhmmss_{sequence}.pdf というファイル名を作成しています。まあ一意性を担保するためには日時は入れる必要はないんですが、この辺りは要求によって変わりそうなので好きにしてください。

そしてこのファイル名をテーブルに入れています。テーブルは次の定義です。

create table request (
    id int not null primary key,
    pdf_url varchar(1024) not null unique,
    request_data json not null
);

先ほど作成したファイル名を含むURLを一意制約付きで入れています。ファイル名としては一意性が確保されているのは明らかですが、検索するときに便利なので。

それからキュー(Redis)にリクエストを投げて、リクエストAPIの戻り値としてはURLを返しています。

リクエストと作成を分離する

リクエストAPIではPDFを直接作成するのではなく、キューに入れて終わりにして、実際の処理は別のワーカープロセスが行っています。なぜなら、PDF作成の元になるデータ取得は外部APIを使っているためエラーが出る可能性が十分あるからです。PDF作成も時間がかかるため、分離しておいた方が無難です。

なお、サンプル実装では常駐プロセスを1つにして無限ループ(Redisの取得でブロッキングされますが)していますが、例えばAWSならSQS + Lambdaを使うことで並列実行可能です。実際には外部APIが処理できるリクエスト数によりますが。

ワーカープロセスは次の処理を行います。

  1. キューからデータを受け取る
  2. 住民票作成に必要なデータを取得
  3. データからPDFを作成

おわりに

TwitterでUUIDがバズっているのを見て、「いやいや今回はそもそもUUIDいらないシステムだから」とモヤモヤしたので作りました。

実際にはGETで重要な処理を行っていたりといろいろと問題があるサンプルコードですが、UUIDなくてもこの方法でいけるよというのは十分示せるかと思います。

(おまけ)外部APIの腐った仕様

2.は外部APIを呼び出していますが、この外部APIはあえて腐った仕様にしています。具体的には次のようなものです。本当はSOAPを使いたかったんですが、面倒だったのでやめました。

  • APIのURLが /IF01/
  • レスポンスがXMLでShift_JISエンコーディング
  • 中身のXMLが「番号タグ」

XMLはこんな形式です。このDTほにゃららが「番号タグ」です。オンラインで確定申告をした人がいれば、もらったXMLファイルを確認してみてください。「ZLA00000」とか「WMA00000」とか謎の番号がついています。

<root>
<DT0001>テスト一郎</DT0001>
<DT0002>19700101</DT0002>
<DT0003>神奈川県横浜市以下略</DT0003>
<DT0004>1</DT0004>
</root>

普通はこんなXMLになるはずです。

<root>
<name>テスト一郎</name>
<birthday>19700101</birthday>
<address>神奈川県横浜市以下略</address>
<gender>1</gender>
</root>
<root>
<名前>テスト一郎</名前>
<生年月日>19700101</生年月日>
<住所>神奈川県横浜市以下略</住所>
<性別>1</性別>
</root>

この番号タグは当然ながら変換表がないとまともに解釈できません。実際に見たことはありませんが、間違いなくExcel方眼紙で管理されていることでしょう。この番号タグは根深い問題で、1999年当時のメーリングリストでの嘆きがここで見られます。エンコーディングをISO-2022-JPにして見てください。

https://web.archive.org/web/20020130160809/http://www.aland.to/~autumn/xmllog/xmlusers.00000018.txt

開発者体験を最悪にするこの「番号タグ」の根絶に向けてデジタル庁は頑張って欲しいです。

Discussion