business-rule-patterns について
ServiceNow 開発者向けに、ビジネスルールの実行タイミング、レコード操作、フィールド変更の検出を解説するスキルです。性能面の指針や代表的な実装パターンも示します。
主な機能
- 01Before、After、Async、Display の使い分け
- 02current と previous によるレコード処理
- 03フィールド変更の検出と実行条件の設定
- 04自動割り当てと連鎖更新のパターン
- 05ルールのパフォーマンス改善
活用シーン
- 01レコードの検証とフィールドの自動入力
- 02更新後の関連レコード作成
- 03時間のかかる連携処理のバックグラウンド実行
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ serac-labs/ serac --skill business-rule-patternsインストール後、Claude Code で/business-rule-patternsを入力して読み込まれたか確認します。
詳細
ServiceNow のビジネスルールに関するベストプラクティス
ビジネスルールは、レコードの表示、挿入、更新、削除時に実行されるサーバーサイドスクリプトです。
各タイプの使い分け
| タイプ | タイミング | ユースケース | パフォーマンスへの影響 |
|---|---|---|---|
| Before | データベースへの書き込み前 | 現在のレコードの検証、変更 | 低 |
| After | データベースへの書き込み後 | 関連レコードの作成、通知 | 中 |
| Async | バックグラウンド(コミット後) | 高負荷の処理、連携 | なし(バックグラウンド) |
| Display | フォームの読み込み時 | フォーム表示の変更、デフォルト値の設定 | 低 |
使用可能なオブジェクト
// In Business Rules, these are always available:
current // The record being operated on
previous // The record BEFORE changes (update/delete only)
gs // GlideSystem utilities
Before ビジネスルール
検証やフィールド操作に使用します。
// Prevent update if condition not met
;(function executeRule(current, previous) {
if (current.state == 7 && previous.state != 6) {
current.setAbortAction(true)
gs.addErrorMessage("Must resolve before closing")
}
})(current, previous)
// Auto-populate fields
;(function executeRule(current, previous) {
if (current.isNewRecord()) {
current.setValue("caller_id", gs.getUserID())
current.setValue("opened_by", gs.getUserID())
}
})(current, previous)
Before ルールでは、次の処理を行わないでください。
current.update()の呼び出し(再帰が発生します!)- 他のテーブルへのクエリ(処理速度を維持してください)
- 外部 API の呼び出し
After ビジネスルール
関連レコードの操作に使用します。
// Create child record when priority is P1
;(function executeRule(current, previous) {
if (current.priority.changesTo(1)) {
var task = new GlideRecord("task")
task.initialize()
task.setValue("short_description", "P1 Follow-up: " + current.number)
task.setValue("parent", current.sys_id)
task.insert()
}
})(current, previous)
// Update parent record
;(function executeRule(current, previous) {
var parent = new GlideRecord("problem")
if (parent.get(current.problem_id)) {
parent.setValue("related_incidents", parent.related_incidents + 1)
parent.update()
}
})(current, previous)
Async ビジネスルール
トランザクションをブロックすべきではない、高負荷の処理に使用します。
// External integration
;(function executeRule(current, previous) {
var integrator = new ExternalSystemIntegration()
integrator.syncIncident(current.sys_id)
})(current, previous)
// Send custom notification
;(function executeRule(current, previous) {
gs.eventQueue("incident.priority.high", current, current.assigned_to, gs.getUserID())
})(current, previous)
便利なメソッド
current メソッド
current.isNewRecord() // True if insert
current.isValidRecord() // True if record exists
current.getValue("field") // Get field value
current.setValue("field", val) // Set field value
current.setAbortAction(true) // Cancel the operation
current.operation() // 'insert', 'update', 'delete'
current.isActionAborted() // Check if aborted
フィールドの変更検出
current.priority.changes() // Field changed (any value)
current.priority.changesTo(1) // Changed TO this value
current.priority.changesFrom(3) // Changed FROM this value
current.priority.nil() // Field is empty
previous の比較
// Check if field was modified
if (current.state != previous.state) {
gs.info("State changed from " + previous.state + " to " + current.state)
}
// Check specific change
if (current.assigned_to.changes() && !previous.assigned_to.nil()) {
gs.info("Reassignment occurred")
}
条件の例
ルールの実行タイミングを制限するには、条件を使用します。
| 条件 | 意味 |
|---|---|
current.active == true | アクティブなレコードのみ |
current.isNewRecord() | 挿入時のみ |
current.priority.changes() | 優先度が変更された場合のみ |
gs.hasRole('admin') | 管理者のみ |
current.assignment_group.nil() | 未割り当ての場合のみ |
パフォーマンスに関するベストプラクティス
- 条件を使用する - ルールの実行タイミングを制限します
- Before ルールを高速に保つ - 可能であればクエリを行いません
- 連携には Async を使用する - トランザクションをブロックしません
- Display ルールを避ける - フォームの読み込みが遅くなります
- Order を設定する - 数値が小さいものから実行されます(100~500 の範囲)
- 「when to run」を確認する - insert、update、delete、query
よくあるパターン
自動割り当て
// Before Insert/Update
if (current.assignment_group.changes() && !current.assignment_group.nil()) {
var members = new GroupMembers(current.assignment_group)
current.assigned_to = members.getNextAvailable()
}
カスケード更新
// After Update
if (current.state.changesTo(7)) {
// Closed
var tasks = new GlideRecord("task")
tasks.addQuery("parent", current.sys_id)
tasks.addQuery("state", "!=", 7)
tasks.query()
while (tasks.next()) {
tasks.setValue("state", 7)
tasks.update()
}
}
コメント
Markdown 対応