◆ SAMPLE · 開発ガイドライン抜粋
ERPNext.JP 開発ガイドライン
— Frappe / ERPNext v16 カスタムアプリを“自社で保守・拡張できる”状態にする設計思想 —
本書(抜粋版)は、ERPNext.JP の全体アーキテクチャと設計思想を図解でご紹介するサンプルです。
実際のソースライセンス版には、これに加えて実装コード・既知の落とし穴と対策・回帰テスト例・Playwright視覚受け入れの実装・デプロイ手順までを収録しています。
本抜粋版では、それらの「実装ノウハウ部分」は 🔒 ロック表示としています。
対象 Frappe/ERPNext v16
DB MariaDB + Redis
設計 Pragmatic DDD-lite
提供 合同会社 MY HATCH
00この資料について
ERPNext.JP は、Frappe/ERPNext v16 を日本の製造業・卸売業向けに拡張したカスタムアプリです。
本ガイドラインは「どこに何を書くべきか」「どこまで触ってよいか」「何を壊すと保守対象外になるか」を、開発者が自分で判断できるようにするための地図です。
▸ 開発の5つの黄金律(全文公開)
- 標準本体を直接編集しない。
apps/frappe / apps/erpnext への手当ては保守対象外になり得る。
- 業務差分はカスタムアプリ
erpnext_jp に閉じる。 Custom Field・fixtures・hooks・Page・Domain Service で表現する。
- 変更は小さく、テスト可能な単位で。 公開 API / DocType schema を変えるなら互換と移行手順を明示。
- UI 文言は翻訳ラッパーで囲む。 JS は
__()、Python は _()。
- 「描画された」と「正しく動く」は別物。 緑のテストを信じる前に、実際の画面と DB を踏む。
※ 以降、🔒 の付いたセクション/パネルは、完全版(ソースライセンス)に収録される実装ノウハウです。
01全体アーキテクチャ
Frappe は「フルスタック Web フレームワーク」、ERPNext は「その上の ERP アプリ」、ERPNext.JP は「さらに上に載る日本向けカスタムアプリ」。
3層は上ほど自由に書き換えてよく、下ほど触らないという単純な原則で積まれています。
図1 3層の積層構造。上の層は下の層の拡張ポイント(hooks / Custom Field)を使って振る舞いを足す。
実行時スタック — リクエストはどこを通るか
Frappe は Nginx → Gunicorn(Python)→ MariaDB を主軸に、重い処理を Redis Queue + Worker に逃がし、通知を Socket.IO が担う、モダンな構成です。
図2 実行時スタック。同期は Gunicorn→MariaDB、非同期は Redis Queue→Worker、通知は Socket.IO。
MariaDB の役割
全 DocType は tab<DocType> に保存。業務の「真実」はここにしか無い。検証は最後に DB の行で確認します。
Redis の3面
① cache(権限・meta・セッション)② queue(ジョブ待ち行列)③ socketio(通知 pub/sub)。別インスタンスで動かすのが定石。
02レイヤード設計(Pragmatic DDD-lite)
戦略的 DDD(ユビキタス言語・境界づけられたコンテキスト・業務中心の命名)は採用し、過剰な戦術 DDD は採用しない。
狙いは「重さ」ではなく「業務ルールが正しい場所にある」状態です。
図3 上の層は下の層を呼ぶが、下から上は呼ばない。業務判断は Domain に集約し、Page と API は薄く保つ。
| レイヤ | 責務 | やってはいけない |
| Presentation | 表示・入力・画面状態・API呼び出し | 業務ルール、権限判定、在庫/原価/承認の確定 |
| API | 入力検証・権限確認・domain呼び出し・DTO返却 | 大量SQL、伝票の直書き更新 |
| Domain | 業務ルール・状態遷移・計算・検証 | UI都合の整形、Desk固有のDOM知識 |
| Persistence | 保存・標準イベント・永続化制約 | controller に巨大ロジックを抱える |
| Integration | MCP・外部連携・標準挙動差し替え | core monkey patch、深い内部API依存 |
API は「薄い入口」— 実装サンプル 🔒
@frappe.whitelist()
def allocate_bom(work_order: str) -> dict:
user = frappe.session.user
req = AllocateBomRequest.from_input(work_order, user)
result = BomAllocationService(req).execute()
return result.to_dict()
# 良い形 / 悪い形の対比、DTO設計、例外設計…
🔒
実装コード・良い形/悪い形の対比はライセンス版に収録
API・Domain Service・DTO の実装サンプルと、避けるべきアンチパターンをコード付きで解説しています。
完全版に収録
03Core と Custom の境界 — hooks の仕組み
「標準を編集しないのに、どう挙動を変えるのか?」— 答えは hooks.py。Frappe は標準 DocType のライフサイクルに外部アプリから処理を差し込む拡張ポイントを公式に開けています。
図4 Core は hooks.py に登録された Custom 側の関数を「ライフサイクルの節目」で呼ぶ。Core のソースは1行も変えていない。
| hook | 何ができるか | レベル |
doc_events | 標準DocTypeの validate / on_submit 等に自分の関数を足す | Lv2 |
permission_query_conditions | listview の SQL にスコープを足す | Lv2 |
override_whitelisted_methods | 標準 whitelist API を自前実装に差し替える | Lv3 |
scheduler_events | 定期処理を登録する | Lv2 |
hooks.py の実装と差し替えの作法 🔒
doc_events = {
"Quotation": {"validate": "erpnext_jp.events.xxxx"},
"Material Request": {"on_update": "erpnext_jp.events.xxxx"},
}
override_whitelisted_methods = {
"erpnext.selling...make_sales_order": "erpnext_jp.overrides.xxxx",
}
# Lv3 を増やすときの判断基準・解除条件の残し方…
🔒
hooks.py の登録例・override の安全な使い方はライセンス版に収録
どの拡張ポイントで何ができるか、Lv3(標準差し替え)をいつ・どう使い、保守コストをどう残すかを実コードで解説します。
完全版に収録
04拡張許可レベル — 触ってよい深さ
どこまで踏み込んでよいかを3段階で明示します。これにより「直したつもりが保守対象外になる」事故を防ぎます。
| レベル | 対象 | 条件 |
| Lv1 自由 | Page・Report・API・Webhook受信・custom DocType | 命名規約・i18n・テスト・公開API互換を守る |
| Lv2 要レビュー | doc_events・scheduler・fixtures・Server Script | 副作用・権限・マイグレーション・ロールバックを確認 |
| Lv3 原則禁止 | override・core patch | 代替不能な場合のみ。理由・影響・解除条件を残す |
▸ 完全版では
各レベルの具体的な判断フロー、v16 アップグレード追従の手順、i18n(翻訳ラッパー)の適用範囲を、コード例とともに収録しています。
05実装パターンと落とし穴 🔒 ライセンス版に収録
過去に実際に事故った箇所を、原因と対策のセットで残しています。「無言で発火しない」「無言で件数が合わない」系の、気づきにくく被害の大きいバグを先回りで潰すための章です。
🔒ライフサイクルフックの「旧値」取得 — DB書き込み後に旧値を取り違える罠と、正しいAPI対応表。
🔒rollback が効かない「暗黙 commit」 — テストにゴミが残る原因と、安全な検証スクリプトの型。
🔒Raw SQL が会社スコープを素通りする — listview と集計の件数が食い違う問題と対策ヘルパ。
🔒Page の権限は DocType 権限ではない — 正しい権限判定 API。
🔒設計指針(Tell, Don't Ask / 完全コンストラクタ) — 変更してもデグレしないコードの土台。
def on_mr_update(doc, method=None):
before = doc.get_doc_before_save()
prev = before.workflow_state if before else None
if prev == doc.workflow_state:
return
... # ← 正しい実装と、なぜ間違うのかの解説はライセンス版
🔒
実コード・原因解説・回帰テスト例を収録
それぞれの落とし穴について「なぜ起きるか/正しい書き方/二度踏まないためのテスト」を具体的に解説しています。
完全版に収録
06デグレ(後退バグ)を防ぐ 🔒 一部ロック
機能追加でいちばん怖いのは「動いていたものが静かに壊れる」こと。考え方は1つ —
「描画された」を「正しく動く」と取り違えない。テストは層で重ね、各バグは必ず回帰テストで封じます。
図5 下ほど速く多く、上ほど遅く少なく。土台に静的チェック。視覚受け入れだけ/単体だけ、では穴が残る。
▸ 緑のテストが嘘をつく3パターン(全文公開)
- grep 型テスト:「文字列がソースにある」ことを確認しても、その画面が実際に描画される保証にはならない。
toBeVisible 止まり:要素が DOM にあるだけで、正しいデータ・正しい権限で動く保証はない。
- 描画スイープだけで GREEN 宣言:画面が出る ≠ 機能が動く。権限 403 や登録即死は実機の機能操作で初めて出る。
デグレを止める実践チェックリスト 🔒
🔒回帰テスト・CIゲート・実振る舞い検証・契約テスト・クリーン土台検証 など、後退バグを止める具体策一式。
🔒変更規模に応じた検証量の決め方と、収束の判断基準。
07Playwright で「視覚受け入れ」 🔒 ライセンス版に収録
画面系の最終確認は実機ブラウザ(Playwright/Chromium)でスクリーンショット+コンソール/ネットワーク予算ゼロで。
「要素が存在する」ではなく「正しく描画され、エラーを1つも出さずに動く」を合格条件にするのがコツです。
① console エラー = 0
未捕捉例外が1件でも出たら不合格。ReferenceError や null 参照をここで捕まえる。
② network 4xx/5xx = 0
403/404/500 を1件でも踏んだら不合格。権限漏れ・没ページ・API破損が可視化される。
③ 実データで描画
空テーブルでなく実データで一覧・フォーム・モーダルが出るか。スクショを証跡に。
test("受注一覧:エラー予算ゼロで描画", async ({ page }) => {
const errors = []; const bad = [];
page.on("console", m => { if (m.type()==="error") errors.push(m.text()); });
page.on("response", r => { if (r.status()>=400) bad.push(r.url()); });
await page.goto("/app/order-management");
expect(errors).toEqual([]); // console 予算 0
expect(bad).toEqual([]); // network 予算 0
}); // 実装の全文+7つのコツはライセンス版
🔒
Playwright 実装テンプレートと7つのコツを収録
console/network 予算ゼロの実装、role ベースのセレクタ、flaky を避ける待ち方、証跡スクショ運用などを具体コードで解説します。
完全版に収録
08環境・デプロイ・チェックリスト 🔒 ライセンス版に収録
ローカル/VPS の環境構築、最小セットアップ手順、デプロイ時の確認、Workspace の既知制約、出荷前の自己点検チェックリストと FAQ を収録しています。
🔒最小セットアップ(get-app → install-app → migrate → start)と現行バージョン前提。
🔒VPS デプロイの確認項目(bench/site の取り違え防止、rsync、cache、Workspace 反映)。
完全版(ソースライセンス)について
ERPNext.JP は、標準 ERPNext を日本の製造業・卸売業向けに拡張したソース提供型のERPです。
本ガイドラインの完全版は、ソースライセンスをご契約のお客様に実装コード・検証ノウハウ・運用手順まで含めてお渡しします。
自社開発チームや SIer による内製・拡張・長期保守を前提とした構成です。
📐 設計まで開示
アーキテクチャ・DDD-lite 設計方針・責務マップを文書化。属人化しない開発体制へ。
🧩 拡張の自由
標準を壊さず業務差分を足す作法。Lv1〜Lv3 の境界が明確で安全に拡張できる。
🛡 デグレに強い
回帰テスト・CIゲート・視覚受け入れまで、後退バグを止める検証設計を同梱。
🇯🇵 日本仕様
受注/購買/生産/在庫/原価/EDI/品質/モバイル現場入力を日本の現場向けに補強。
※ 本書は抜粋(プレビュー)版です。🔒 部分の実装ノウハウは完全版に収録しています。リンク先は掲載先に合わせて設定してください。