NGW開発BLOG
← 記事一覧へ
·#064

つぎはぎのメニューを、一本の ngwMenu にまとめた話

同じデータから3通りに描かれていたNGWのメニューを、単一のサーバ描画メニュー ngwMenu に一本化した。標準とドロップダウンを1つの土台で出し分けるハイブリッド構成へ。150超のアプリを一切触らず、差引 −2,403 行。動くだけのコードを、読んで美しいコードへ近づける記録。

同じデータなのに、描き方が3通りあった

NGW の画面の一番上にはメニューが出る。文書管理・財務支援・人事給与といったサブシステムのアイコンが横に並び、いま開いているアプリ専用のメニューがその下に続く。ユーザーから見れば、ただのメニューだ。

中を開けると、そのメニューを描くコードは 3系統あった。

  • 新しい ngwMenu — サーバ側でHTMLを組み立てる新方式。
  • 2026ドロップダウンajaxNgwMenuFunc.php1,516行
  • 旧アイコン列ajaxNgwFunc.php の中の fncMakeNgwMenu / fncMakeSubSystemMenu

職員ごとの設定値 STAFF_MENU_TYPE で、このどれを使うかが分岐する。厄介なのはここからだ。150を超えるアプリが、画面を移動するたびに MakeNgwMenu / MakeSubSystemMenu を同期AJAX(async:false)で呼び、メニューの入れ物 #NgwMenu を毎回まるごと詰め直していた。

データ源はどれも同じだ。以前の記事で書いた NGWT_APP_MENU テーブルの JSON。同じデータを読んで、同じメニューを、3通りのコードで描いていた。 描画関数どうしは約9割が同じ内容で、権限の判定も、自治体ごとの語の置き換えも、コピペで枝分かれしていた。

つぎはぎ、という言葉がちょうどいい。今回はこれを一本の布に縫い直す話だ。

ハイブリッドとは何か — 1つの土台、2つの見え方

めざした形を先に書く。新しい ngwMenuハイブリッドだ。何と何のハイブリッドか。

データ源はこれまで通り NGWT_APP_MENU の JSON ひとつ。職員ごとの STAFF_MENU_TYPE によって、同じデータから2つの見え方を出し分ける。

  • 標準メニュー — サブシステムのアイコン列を常時表示する。下位アプリを持つサブシステムには、アイコンにドロップダウンが付く。アプリ内のメニュー(AppMenu)は本文の先頭に置く。
  • ドロップダウン(DD)メニュー — HOME 画面ではアイコン列でナビゲートし、アプリを開いている間は HOME ロゴのドロップダウンでナビゲートする。AppMenu は画面の最上部に持ち上げる。

標準メニュー。上部にサブシステムのアイコンが横一列に並び、左上のHOMEロゴをクリックするとサブシステム名(予定表・グループ・予約・特別職・共有文書・回覧板・行政無線・文書管理・決裁・財務支援…)がドロップダウンで縦に開く。文書管理・財務支援などサブアプリを持つ項目には右向きの三角が付く

標準メニュー。アイコン列を常時表示しつつ、HOMEロゴからは同じ顔ぶれをドロップダウンでも開ける。どちらも同じ `NGWT_APP_MENU` のデータを描いたもの。

ハイブリッドの核心は、両モードが同じ土台(アイコン列=NGW_MENU)を共有していることにある。標準モードでもサブシステムにはドロップダウンが付き、DDモードでも HOME ではアイコン列を出す。どちらか一方に倒すのではない。同じ部品を、文脈に応じて出したり畳んだりする。二択ではなく、地続きの一つの仕組みにした。

いちばんわかりやすいのが文書管理だ。

標準メニューで、アイコン列の「文書管理」にマウスを乗せた状態。文書管理アイコンの真下にドロップダウンが開き、収受文書・起案文書・会議録・公示令達・棚管理・簿冊管理・簿冊関連・引継廃棄・書庫管理・決裁状況・文書検索・設定が縦に並ぶ

サブシステム「文書管理」のアイコンに付くドロップダウン。40超の下位アプリを、アイコン列を離れずにたどれる。これが「標準メニューでありながらドロップダウンも効く」=ハイブリッドの核だ。

アプリを開くと、DDモードはこうなる。上段の余白を AppMenu に譲り、ナビゲーションは HOME ロゴのドロップダウンに畳む。

起案文書アプリを開いた状態のDDメニュー。画面最上部に「起案を開く・起案印刷・要回答検索・新規起案・登録保存・発信処理」というアプリ内メニュー(AppMenu)が並び、左上のHOMEロゴのドロップダウンからサブシステム一覧をたどれる

DDモードでアプリ(起案文書)を開いたところ。AppMenu が最上部に上がり、サブシステム間の移動は HOME ロゴのドロップダウンへ。同じ土台を、文脈に合わせて畳み替えている。

大きく壊さず、一歩ずつ縫い直す

150超のアプリが依存している土台を触る。一気に全部を差し替えれば、必ずどこかで事故る。安全に一歩ずつ進めた。コミットの順番が、そのまま作業の順番だ。

  1. 一本化 — ヘッダーの「旧メニューを使う」false 分岐を撤去し、常に新方式を通す。
  2. 旧AJAX撤去ajaxNgwMenuFunc.php(1,516行)を丸ごと削除。呼び出していた再充填JSは、互換のため no-op 化。
  3. 未使用コード除去・分離ajaxNgwFunc.php から旧アイコン列ビルダーを取り除き、アプリ内メニューを別モジュールへ切り出す。
  4. リネーム — 移行期の仮称 menu2 を、正式名 ngwMenu へ。
  5. DD改善 — HOME とアプリ内で挙動を分け、AppMenu の置き場所を整える。
  6. 体裁調整 — 本文とのすき間、アイコンの収まりを詰める。

一番効いたのは、2番の no-op 化だ。

150超のアプリを、1行も触らずに済ませる

普通なら「メニューの呼び出し方を変えたなら、呼んでいる150箇所を全部直す」となる。それはしなかった。

新しい ngwMenu はサーバ側で描画される。ページを開いた時点でメニューはもう出来上がっている。アプリ側が遷移のたびにメニューを詰め直す必要は、もうない。

各アプリが呼んでいる MakeNgwMenu / MakeSubSystemMenu の中身を空にした。関数はそのまま残るので、150超の呼び出し元は1行も変えなくていい。呼ばれても何も起きないだけだ。副産物として、async:false の同期往復——画面遷移のたびにサーバの応答をブラウザが待っていた、あの引っかかり——も根こそぎ消えた。コードが減って、体感速度まで上がった。

AppMenu は、静的に吸収してはいけなかった

一本化の途中で一度手が止まった。アプリ内メニュー(AppMenu)だ。

上段メニューはサーバ静的描画に寄せられた。ならば AppMenu も同じように吸収すればいい。そう思った。間違いだった。AppMenu は、いま選択中の行・モーダルかどうか・登録モードか・その人の権限といった、画面のその瞬間の状態に依存して動的に変わる。サーバ描画にまるごと寄せると、この動的更新が壊れる。

吸収ではなく、モジュール分離にとどめた。AppMenu は新設した ngwAppMenuFunc.php+808行)へ独立させ、動的AJAXのまま残す。上段メニューは唯一・サーバ描画。アプリ内メニューは動的・独立モジュール。役割で2つに分ける。 これが今回の設計判断の芯だ。

同じデータ源・3系統の描画、という Before は、同じデータ源・役割の違う2モジュール、という After になった。

フラグは、触れない場所にあった

新旧を切り替えるフラグ _USE_NGWMENU2_ は、DB(NGWT_CONSTANT)から読んで define される。ところが、それを受ける設定ファイルは www-data 所有かつ .gitignore の中だった。こちらからは編集できない。

フラグを消したいのに、フラグの定義に手が届かない。しばらく唸った。そして発想を変えた。分岐そのものを無条件化して、フラグを不要にする。 切り替えの必要がなくなれば、触れないフラグがどこにあっても関係ない。つぎはぎを消すというのは、こういう外堀も含めて、である。

数字で見る

項目
差引行数−2,403 行(+948 / −3,351)
変更ファイル13 ファイル・6 コミット
ajaxNgwMenuFunc.php全削除 −1,516 行
ajaxNgwFunc.php2,160 → 637 行
ngwAppMenuFunc.php(新設)+808 行(AppMenuの移設ぶん)

追加のほとんどは AppMenu を切り出した新モジュールだ。実質はほぼ純減で、差引 −2,403 行。消したのは重複と未使用コード。残したのは動くために本当に必要なものだけ。

念のため、消す前に旧メニュー一式は tag menu-legacy-2026-07-13 と branch として退避してある。戻れる状態を作ってから、戻らなくていい形に進んだ。

AIとの協働作業での学び

つぎはぎは一度に生まれない。良かれと思った継ぎ足しが積み重なって、いつの間にか一枚布から遠ざかる。今回の3系統も、それぞれの時点では正しい判断だったはずだ。だからほどくときも一気には壊せない。false 分岐を消す。旧AJAXを消す。呼び出し元を no-op で無害化する。名前を正す。戻れる粒度に切って、一歩ずつ。 AI と組むと、この「小さく安全なコミットに割る」計画づくりと、「150箇所を触らず一括で無害化する」横着の効くやり方が、格段に回しやすい。

今回いちばん確かめたかったのはこれだ。新しい NGW は、動くだけでは足りない。 同じデータを3通りに描いていたコードは、動いてはいた。ユーザーは困っていなかった。それでも、読んで美しくないコードは次の誰かを必ず困らせる。次の誰かとは、半年後の自分である。

……というのを、私は ajaxNgwFunc.php の2,160行を上から読みながら痛感していた。書いたのは私だ。文句を言う相手がいない。つぎはぎを、一本の布へ。動くだけのコードから、読んで美しいコードへ。