ドメイン駆動設計と ServiceNow®

Kenta Kosugi
11 min readJul 26, 2023

--

今回は徒然なるままに思いを記載してみる。

専門分野以外の知識が必要

ServiceNow®には Common Service Data Model(CSDM)と呼ばれる考え方がある。

共通サービスデータモデル (CSDM) は、Now Platform® のすべての ServiceNow® 製品に適用される、標準化され一貫した用語と定義のセットです。これらの用語と定義では、CSDM フレームワークの基礎を形成します。

ServiceNow® が利用者に製品を提供する際、定義している専門用語の集合と、その専門用語をデータベースに落とし込んだ際のデータモデルを定義しているものである。

以下は製品ドキュメントから抜粋した図になる。

上記を見ると、CSDM は5つのドメインから成り立っている。

  • Foundation ・・・ Now Platform® で扱う領域
  • Design・・・Application Portfolio Management で扱う領域
  • Build・・・DevOps 等で扱う領域
  • Manage Technical Services・・・IT Operations Management で扱う領域
  • Sell / Consume・・・Strategic Portfolio Management / Customer Service Management で扱う領域

ドメイン駆動設計では他のドメインのデータを参照する・させるには API による問い合わせやイベントによるデータの伝播を検討し、ドメインの間の関係は疎結合に保たれるように努める。なぜならドメイン(部門=チーム)とシステムが疎結合になっていることが品質とデリバリーのスピード、ひいては経常利益等の組織のパフォーマンスを向上させる主要因だからだ。詳しくは以下を参照願いたい。

しかし、CSDM の場合、自分が所属していないドメインのデータを直接参照しなければならない。ロールによっては所属外のドメインのデータの直接編集も可能である。

仮に ServiceNow® Vendor Risk Management で Vendor を登録したい人がいたとする。この人物は ServiceNow® を扱うにあたり、Vendor Risk Management のドメイン領域だけ知っていれば良いというわけではない。

Vendor は Foundation ドメインにおける Company(core_company) テーブルに登録されるのである。Foundation ドメインの詳細を知っていないと Vendor の登録ができない。

VRM

この状態はドメイン間が疎結合な状態であるとは言い難い。

ここまで説明すると、CSDM におけるドメインという考え方とドメイン駆動設計におけるドメインには大きな乖離があることがわかる。

ドメイン駆動設計で設計した場合 Vendor Risk Management ドメインにおける Vendor と Foundation ドメインにおける Company を同一のテーブルで扱うことは絶対にしない。なぜならドメインによって管理するデータの責務が異なるからである。

Vendor を管理するのは Vendor Risk Management のドメインの責務であり、Vendor Risk Management の領域で閉じるように設計する。これにより利用者は Vendor Risk Management の領域外の知識を必要とすることなく扱うことができる。

仮に Vendor Risk Management に専用の画面が用意されて、Vendor の登録ができるようになったとしよう。実際、Utah ではできるようになった。これで利用者は Vendor Risk Management の領域で閉じるようになった、めでたしめでたしではない。Vendor Risk Management の画面を通して登録された Vendor の実態は Foundation ドメインの Company にある。他の用途で登録されている Company と同列の扱いをされる。Foundation ドメインの利用者がデータを削除したり、データを変更する可能性は否めないのである。自分が責務を持って管理すべきデータであるにもかかわらずである。

特に Foundation ドメイン領域は ServiceNow® 全体の共有データを扱う領域であるため、こうした特徴を持っているデータが多い。しかし共有データだけではなく Design と Technical Service で共有しあうデータ等も多数存在する。

つまり、ServiceNow® を扱う上では他のドメインの知識も持っていなければ有効活用することができないということだ。

さらに ServiceNow® はこの問題を悪化させる機能がある。

ServiceNow® におけるデータの継承

ServiceNow® は継承という概念を取り扱っている。継承というのはオブジェクト指向に出てくるあれである。

例えば、ServiceNow® で扱う Task というテーブルを見てみる。このテーブルは Foundation ドメインで扱われるテーブルだ。

この Task テーブル、実は様々なテーブルが継承している。

継承関係にあるテーブル

上は Strategic Portfolio Management のサービスや IT Service Management で利用される代表的なテーブルを4つほど抜粋したものだ。

汎用的な使い方を実現させるために Task テーブルには「これ必要なのか?」と言った多数の列が用意されており、その数実に 75 項目に及ぶ。

Task を継承した各テーブルには Task が最初から持っている 75 の項目に加えてさらに列が追加されている。

さらに言うとこれら Task を継承した Demand、Incident、Problem、Change をさらに継承したテーブルも存在する。もっと言うとそのさらに先も・・・・

継承を大前提にシステムを設計した結果、明らかに YAGNI の法則に違反することになっているし、データモデルが肥大化しすぎている。さらに Foundation ドメインは共有前提のドメインであり、アンチパターンの Shared Database に当てはまっている。

オブジェクト指向の本来の継承

継承はオブジェクト指向プログラミングの特徴の一つだ。

オブジェクト指向のメリットの一つにポリモーフィズムが挙げられる。よく例として挙げられるのは動物クラスを継承した犬や猫の例だ。

今回はペット(Pet)クラスして以下を宣言する。ペットは泣くものとして、cry() ロジックが実装されている。

public abstract class Pet {
private String name;

public Pet (String name){
this.name = name;
}

public abstract void cry();
}

この動物クラスを継承して、犬クラスや猫クラスを作成する。

public class Dog extends Pet{
@Override
public void cry() {
System.out.println(this.name + ": ワン");
}
}
public class Cat extends Pet {
@Override
public void cry() {
System.out.println(this.name + ": ニャー");
}
}

これらを利用するシーンは以下のように記載される。

public void main(String[] args) {
List<Pet> pets = new ArrayList<Pet>();
pets.put(new Dog("太郎"));
pets.put(new Cat("花子"));

foreach(var pet in pets) {
pet.cry(); // ①
}
}

これを実際に動かすと以下となる。

太郎: ワン
花子: ニャー

ポリモーフィズムの良いところはどこなのだろうか。

ポイントは①の箇所。ロジックの呼び出す側は犬か猫かどうかは全く気にしていない。呼び出し側は犬や猫の詳細な cry() を意識して呼び出しているわけではなく、もう一段階ハイレベルなペットクラスの cry() を呼び出している認識である。

しかし、実際は犬クラスと猫クラスそれぞれに実装されている cry() ロジックが呼び出されている。

オブジェクト指向で Task を考えると、cry() の代わりに以下のようなビジネスロジックが考えられる。

  • Task を完了
  • Task を第三者に割り当てる
  • 完了予定日を設定する
  • 完了予定日を延期する
  • Task を削除
  • 承認を依頼
  • ・・・他いろいろ

ServiceNow® ではこれらデータモデルの周りで発生するべきビジネスロジックはドメインモデルとしてオブジェクト指向で実装されているわけではない。

ビジネスロジックはどこにあるのかというとトランザクションスクリプトとして Workflow に別に存在している。

Task オブジェクが Task を完了するというビジネスロジックを持っている場合、継承した Demand や Incident は Task が持つ同じビジネスロジックを持つことになる。言い換えると、Demand や Incident や Task ごとに完了するというビジネスロジックを実装する必要はないのである。加えてデータとそのデータにまつわるロジックは同じ場所(クラスファイル)に実装されるので、データに変更を加えた場合のロジックへの影響は局所的になる。

継承関係にあるテーブル

ServiceNow® の場合、データを保存するテーブルとロジックを保存する場所は別々である。テーブルの属性を継承して持たせることは可能なのだが、ロジックを持たせることはできない。つまり各テーブルごとに Workflow を持たざるを得ない。

これまで全社的に 2段階の承認が必要だったものが1段階で良くなったという変更が入った場合、オブジェクト指向の場合は大元の Task の修正だけで事足りるが、ServiceNow® の場合、Task、Demand、Incident、Problem、Change 等の Task を継承したすべてテーブルを参照する Workflow を全て見つけ出して修正する必要がある。

チェリーピッキング

ServiceNow® はオブジェクト指向の都合のいいところだけを抜き出している印象である。どのサービスも使う Fundation と呼ばれるドメイン領域を定義していることもドメイン駆動設計に至れておらず、メリットを享受できていない印象を受ける。

--

--

Kenta Kosugi

Javaアプリケーションサーバーの開発からCORBA製品のサポート、QA、証券外務員(第一種免許)、ストレージ屋、アーキテクト、SaaS屋と一貫性のない道を歩んでいます。Red Hatに復帰しました。