Camundaにおけるプロセスバージョン管理とデプロイ戦略

ビジネスプロセスの変更管理における課題

企業システムにおいて、業務フローは頻繁に改訂される重要な要素です。誤ったプロセス定義の適用は、業務停止やデータの不整合を引き起こす可能性があります。Camundaはプロセスのバージョン管理機能を備えており、安全なアップデートとロールバックを実現します。

バージョン管理の仕組み

Camundaでは、各プロセス定義に対して自動的にバージョン番号が割り当てられます。

// 最新バージョンの取得
ProcessDefinition definition = repositoryService
    .createProcessDefinitionQuery()
    .processDefinitionKey("orderFlow")
    .latestVersion()
    .singleResult();

int version = definition.getVersion();
String deployId = definition.getDeploymentId();
属性説明初期値
バージョン番号連番で管理される整数1から開始し、デプロイごとに増加
デプロイ識別子グローバルに一意なUUIDランダム生成
プロセスキーBPMNファイル内のprocess idXMLで指定された値
アクティブフラグ現在有効なバージョンか否か最新版のみtrue

デプロイ手法

基本的なデプロイ操作

// プロセスと関連リソースの一括登録
Deployment registration = repositoryService.createDeployment()
    .addClasspathResource("bpmn/order.bpmn")
    .addClasspathResource("dmn/validation.dmn")
    .name("注文処理v2.0")
    .enableDuplicateFiltering(true)
    .deploy();

// 登録結果の確認
List<ProcessDefinition> definitions = repositoryService
    .createProcessDefinitionQuery()
    .deploymentId(registration.getId())
    .list();

段階的デプロイメント

ブルー・グリーン方式
// 新旧並行運用パターン
public class BlueGreenDeployer {
    
    public Deployment perform(ProcessEngine engine, String key, String... files) {
        RepositoryService repo = engine.getRepositoryService();
        
        // 新バージョンの登録
        Deployment next = repo.createDeployment()
            .addClasspathResources(Arrays.asList(files))
            .name("BG Deploy: " + key)
            .deploy();
        
        // 既存バージョンの一時停止
        deactivateOldVersions(repo, key);
        
        return next;
    }
    
    private void deactivateOldVersions(RepositoryService service, String key) {
        List<ProcessDefinition> all = service
            .createProcessDefinitionQuery()
            .processDefinitionKey(key)
            .list();
            
        ProcessDefinition active = service
            .createProcessDefinitionQuery()
            .processDefinitionKey(key)
            .latestVersion()
            .singleResult();
            
        for (ProcessDefinition item : all) {
            if (!item.getId().equals(active.getId())) {
                service.suspendProcessDefinitionById(item.getId());
            }
        }
    }
}
カナリアリリース
// 小規模テスト投入
public class CanaryDeployer {
    
    public void release(ProcessEngine engine, String key, double ratio) {
        // 新規バージョン登録
        Deployment deploy = engine.getRepositoryService()
            .createDeployment()
            .addClasspathResource("bpmn/test.bpmn")
            .deploy();
        
        // 乱数によりルーティング
        Random rand = new Random();
        RuntimeService runtime = engine.getRuntimeService();
        
        if (rand.nextDouble() < ratio) {
            runtime.startProcessInstanceByKey(key + "-test");
        } else {
            runtime.startProcessInstanceByKey(key);
        }
    }
}

ロールバック機構

デプロイ単位での巻き戻し

// デプロイメント全体の削除制御
public class RollbackController {
    
    private final RepositoryService repo;
    private final RuntimeService runtime;
    
    public RollbackController(ProcessEngine engine) {
        this.repo = engine.getRepositoryService();
        this.runtime = engine.getRuntimeService();
    }
    
    public boolean revert(String deployId, boolean forceRemove) {
        try {
            long activeCount = getActiveInstanceCount(deployId);
            
            if (activeCount > 0 && !forceRemove) {
                throw new IllegalStateException(
                    "進行中のインスタンスが存在します: " + activeCount);
            }
            
            return executeRevert(deployId, forceRemove);
            
        } catch (Exception ex) {
            log.error("ロールバック失敗: " + deployId, ex);
            return false;
        }
    }
    
    private long getActiveInstanceCount(String deployId) {
        List<ProcessDefinition> defs = repo
            .createProcessDefinitionQuery()
            .deploymentId(deployId)
            .list();
            
        long total = 0;
        for (ProcessDefinition def : defs) {
            total += runtime.createProcessInstanceQuery()
                .processDefinitionId(def.getId())
                .count();
        }
        return total;
    }
    
    private boolean executeRevert(String deployId, boolean force) {
        if (force) {
            repo.deleteDeployment(deployId, true, true);
        } else {
            repo.deleteDeployment(deployId);
        }
        return true;
    }
}

本番環境での運用ノウハウ

事前チェックリスト

確認項目詳細検証手段
BPMN構文チェックXML形式の正当性Model APIによる解析
データ互換性変更前後の型一致テストケース実行
パフォーマンス評価メモリ/CPU使用量負荷試験ツール利用
復旧計画緊急時の対応手順スクリプト動作確認

監視設定

// デプロイ状況の追跡
public class HealthWatcher {
    
    public void observe(String deployId) {
        Map<String, Object> stats = new HashMap<>();
        stats.put("running", countRunning(deployId));
        stats.put("completed", countFinished(deployId));
        stats.put("errors", countErrors(deployId));
        stats.put("avgTime", computeAvgDuration(deployId));
        
        if ((Long)stats.get("errors") > limit) {
            sendNotification("異常検知: " + deployId);
        }
    }
    
    private long countFinished(String deployId) {
        return processEngine.getHistoryService()
            .createHistoricProcessInstanceQuery()
            .deploymentId(deployId)
            .finished()
            .count();
    }
}

実務例:ショッピングカートフローの更新

背景

ECサイトの受注プロセスに新たな承認ステップを追加する必要があり、バージョン3.2から4.0へ移行します。

移行手順

// 注文処理のバージョンアップ
public class OrderFlowUpdater {
    
    public void upgrade(ProcessEngine engine) {
        RepositoryService repo = engine.getRepositoryService();
        
        // 現行バージョンのバックアップ作成
        Deployment snapshot = backupCurrent(repo, "orderFlow");
        
        // ブルー・グリーンデプロイ実施
        Deployment newVer = repo.createDeployment()
            .addClasspathResource("bpmn/order_v4.bpmn")
            .addClasspathResource("dmn/rules.dmn")
            .name("Order Flow v4.0 - Blue")
            .enableDuplicateFiltering(true)
            .deploy();
        
        // 少量トラフィックを新バージョンへ
        shiftTraffic(engine, "orderFlow", 0.1);
        
        // 新バージョンの安定性確認後完全移行
        if (checkStability(newVer.getId())) {
            finalizeTransition(engine, "orderFlow");
        } else {
            restorePrevious(repo, snapshot.getId());
        }
    }
    
    private void shiftTraffic(ProcessEngine eng, String key, double percent) {
        // トラフィック比率の動的調整ロジック
    }
}

緊急復帰プラン

// 即時復旧マニュアル
public class EmergencyRecovery {
    
    public static final Map<String, String> RECOVERY_MAP = Map.of(
        "orderFlow-v4", "orderFlow-v3.2",
        "payment-v2.1", "payment-v2.0"
    );
    
    public boolean activate(String flowName) {
        String target = RECOVERY_MAP.get(flowName);
        if (target == null) {
            throw new IllegalArgumentException("復旧情報未定義: " + flowName);
        }
        
        return switchToVersion(flowName, target);
    }
}

Camundaの柔軟なバージョン管理により、企業はリスクを最小限に抑えながら継続的なプロセス改善が可能です。適切なデプロイ戦略と監視体制を整えることで、安定した業務運用を維持できます。

タグ: Camunda BPMN ワークフロー管理 プロセスエンジン Java

8月27日 16:49 投稿