コンテンツにスキップ

戦略的設計

はじめに

本サイトにつきまして、【IT技術の知見】はじめに - 俺の技術ノート をご認識のほど宜しくお願いいたします。


01. ドメイン

ドメインとは

ビジネスモデル全体で見たときに、ソフトウェア化の対象となる業務領域のこと。

ドメインの分割方法

業務領域を分割するときの粒度は小さいほどよい。

業務領域を細分化するうえで境目を見分ける方法として、会社の組織図から、部署ごとに担当する業務の種類を確認するとよい。

また、ビジネスモデルにおける一連の業務フローのなかで、業務担当者の属性が変化するタイミングに着目するとよい。

担当者が変われば、業務をより小さな単位で抽出できるはずである。 例えば、コールセンター部 (電話対応業務、顧客管理業務) や法人営業部 (受注業務) がある。 また、マーケティング部 (ユーザー集客業務、広告運用業務) や管理部 (会計業務、財務業務、採用業務) がある。

このときの注意点として、業務領域は、ToB や ToC に関わらないことである。

例えば、ToC なドメインには、マーケティング部による業務 (ユーザー集客業務、広告運用業務) がある。

例:インターネット広告代理店

インターネット広告代理店の例。

ビジネスモデルに基づく複数のドメインを以下に示す。

業務フローの担当者の変化として、まず問い合わせで注文を受けて広告アカウントを作成する『営業担当者』、制作した広告をアカウントに入稿する『制作担当者』、入稿された広告を運用して広告効果を高める『マーケティング担当者』、最後に広告の依頼者に料金を請求する『経理担当者』が挙げられる。

これにより、インターネット広告代理店のビジネスモデルは、各担当者に対応するドメインへ分割できる。

internet_advertising_agency_domain

例:完全個室ジムを運営するハコジム

hacogym_business_model

完全個室ジムを運営するハコジムの例。

ビジネスモデルに基づく複数のドメインを以下に示す。

業務フローの担当者の変化として、まず個室ジムに適する物件を探す『物件担当者』、ジムのトレーナーを採用して会員に紹介する『採用担当者』、個室ジムの利用会員を獲得する『営業担当者』が挙げられる。

これにより、ハコジムのビジネスモデルは、各担当者に対応するドメインへ分割できる。

hacogym_domain

例:監視 SaaS

プラットフォームエンジニアリングでは、エンジニアの技術的な業務がドメインになる。

例えば、監視 SaaS では監視業務領域がドメインになる。

適切な監視手法の選択、適切な TCP/IP のプロトコルの選択、アラートの巡回が業務である。

例:オンライン書店

  • カタログ管理
  • 在庫管理
  • 顧客分析
  • 決済管理

例:病院システム

  • 情報管理
  • 予約管理


02. コアドメイン、サブドメイン、ドメインエキスパート

コアドメイン、サブドメインとは

広義のドメインの問題領域のうち、他と差別化する最も重要な業務領域がコアドメイン、コアドメインから使われる支援的または汎用的な業務領域がサブドメインである。

core-domain_sub-domain_bounded-context

ドメインエキスパートとは

各ドメインのドメインエキスパートと、エンジニアが話し合いながら、ドメイン内の主要業務をコアドメイン、補助的な業務をサブドメインに分類する。

コアドメインやサブドメイン相当の業務に詳しい人 (アプリケーションの利用者本人、利用者の関心が高い人) がドメインエキスパートになることが多い。

*具体例*

業務アプリケーションの開発では、ビジネス業務(決済、請求、送金など)がドメインになる。

実際にビジネス側の人がドメインエキスパートを担ってもよいが、プロジェクトマネージャーがドメインエキスパートになるほうが現実的である。

*具体例*

プラットフォームエンジニアリングでは、エンジニアの技術的な業務 (監視業務、セキュリティ対策など) がドメインになる。

そのため、プラットフォーム SaaS ではドメインエキスパートはエンジニア(SRE など)になる。

サブドメインの委譲

コアドメインのソフトウェアは内製である必要があるが、サブドメインのソフトウェアは外製/内製のいずれでも問題ない。

必要であれば、他の業務系パッケージや業務系 SaaS (例:決済代行のベリトランス、経理 SaaS のマネーフォワード、総務人事 SaaS のサイボウズなど) にドメインロジックを委譲し、その API をコールしてデータを取得するような設計でもよい。

ただ、コアドメインが外部に依存すると、その SaaS で障害が起こったときにコアドメインのシステムも停止する可能性があるため、注意が必要である。

例:完全個室ジムを運営するハコジム

完全個室ジムを運営するハコジムの例。

ドメインのうちで、個室ジムドメインに基づくコアドメインとサブドメインを以下に示す。

コアドメインは予約ドメイン、それ以外はサブドメインとしている。

hacogym_subdomain

EC サイトを運営するアスクルの例。

ドメインのうちで、個人向け販売ドメイン (サイト名は LOHACO) に基づくコアドメインとサブドメインを以下に示す。

配送/注文/商品/ユーザ管理/在庫/受注をそれぞれサブドメインとしている (コアドメインは明言されていない) 。


03. ユビキタス言語

ユビキタス言語とは

同じ境界づけられたコンテキスト内で 1 個の意味合い/定義付けを持った『名詞』や『動詞』のこと。

ドメインエキスパート間で、特定の『名詞』や『動詞』の意味合い/定義づけが異なる場合、これを異なる名前からなるユビキタス言語として定義づける。

domain-model

ユビキタス言語を見つける議論フレームワーク

イベントストーミングを実施し、業務フローやそのなかでドメインイベントの名詞や動詞から、ユビキタス言語を見つける。

議論のなかで発生したドメインイベントを線で結ぶ。

event-storming_domain-event.png

Figma などのツールを使用して、画面ごとにドメインイベントをつなげ、フロントエンドやバックエンドの機能ロジックを設計していく。

event-storming_domain-event.png

抽出例

単語 意味合い 対応するドメインモデル
... ... ...
... ... ...


04. 境界づけられたコンテキスト

境界づけられたコンテキストとは

ドメインエキスパートの業務フローの立ち位置が異なれば、同じ『名詞』や『動詞』であっても、異なる意味合い/定義づけのユビキタス言語が使用される。

異なるユビキタス言語を元にして、境界づけられたコンテキストを設定する。

このとき、ユビキタス言語は、他の境界づけられたコンテキストでは通じないものであればあるほどよい。

境界づけられたコンテキストそれぞれのユビキタス言語に合わせて、異なる名前でモデリングしていく。

境界づけられたコンテキストを定義しない場合、異なるユビキタス言語をコアドメインやサブドメイン間で共有することとなり、それぞれの関心に無関係なデータを保持することになってしまう。

コンテキストの境界の見つけ方

アプリケーションがモデリングしている業務フローと、その担当者を考えるとわかりやすい。

ビジネス全体のどの部分をアプリケーションにしているのかによるが、例えば Amazon で以下の登場人物がいるとする。

担当者 境界づけられたコンテキスト名 ドメインモデルで関心のある状態
受注担当者 受注管理 注文内容・値段
↓
在庫管理者 在庫管理 在庫数
↓
発送担当者 発送管理 発送状況
↓
経理担当者 請求管理 請求額・支払期限・入金状況
↓
精算管理 事業者間の受け払い金額・精算状況
↓
仕入れ担当者 仕入れ管理 仕入れ状況

商品 (Item) というオブジェクトがあったときに、業務フローのなかで商品を扱う担当者の文脈 (コンテキスト) によって、商品ドメインモデルの持つ状態への関心が変わる。

また、同じ担当者でも、扱う業務によってドメインモデルの状態への関心が変わる。

つまり、「異なる担当者間」と「同じ担当者が扱う異なる業務間」に、コンテキストの境界がある。

例えば、経理担当者は、請求管理では請求額や支払期限、精算管理では事業者間の受け払い金額や精算状況に関心がある。

ここでいう状態とは、具体的に商品ドメインモデルを実装したときにプロパティとしてオブジェクトに持たせている状態である。

その境界は、業務の目的、用語、ルールの違いから判断する。

別々のマイクロサービスとして分割し、それぞれのマイクロサービス上で異なる商品ドメインモデルを定義し、異なる状態を持たせることになる。

反対にモノリスだと、1 つの商品ドメインモデルにコンテキスト関係なくすべての種類の状態を持たせて、すべてのコンテキストで再利用する。

*表*

各業務を別のマイクロサービスに分けた場合の設計例を示す。

担当者 境界づけられたコンテキスト名 業務内容 マイクロサービス名 集約名 集約内のエンティティ
受注担当者 受注管理 受注担当者が注文内容と金額を確認し、注文を受け付ける。 ordering サービス Order(注文) Order エンティティ(注文)、OrderItem エンティティ(注文明細)
↓
在庫管理者 在庫管理 在庫管理者が在庫数を確認し、在庫を引き当てる。 inventory サービス Inventory(在庫) Inventory エンティティ(在庫)
↓
発送担当者 発送管理 発送担当者が商品と配送先を確認し、発送状況を更新する。 shipping サービス Shipment(発送) Shipment エンティティ(発送)、ShipmentItem エンティティ(発送明細)
↓
経理担当者 請求管理 経理担当者が請求明細、金額、支払期限を確認し、請求書の発行と入金確認を行う。 billing サービス Invoice(請求) Invoice エンティティ(請求)、InvoiceItem エンティティ(請求明細)
↓
精算管理 経理担当者が事業者間の受け払い金額を確定し、精算状況を確認する。 settlement サービス Settlement(精算) Settlement エンティティ(精算)、SettlementItem エンティティ(精算明細)
↓
仕入れ担当者 仕入れ管理 仕入れ担当者が仕入れ先と数量を決め、発注する。 purchasing サービス PurchaseOrder(仕入れ) PurchaseOrder エンティティ(仕入れ注文)、PurchaseOrderItem エンティティ(仕入れ明細)

例:監視 SaaS

監視 SaaS の境界づけられたコンテキストを、コアドメイン、支援サブドメイン、汎用サブドメインに分類した例を示す。 各ノードは境界づけられたコンテキストを表し、矢印は依存する側から依存先へ向ける。

*表*

担当者 境界づけられたコンテキスト名 業務内容 マイクロサービス名 集約名 集約内のエンティティ
運用担当者 監視 運用担当者が監視グループ、監視リクエスト、SLO を設定し、監視結果を確認する。 monitoring サービス Monitoring(監視) MonitoringGroup エンティティ(監視グループ)、MonitoringRequest エンティティ(監視リクエスト)
↓
インシデント管理 運用担当者がインシデントを起票し、対応経過と解決を記録する。 incident サービス Incident(インシデント) Incident エンティティ(インシデント)、IncidentHistory エンティティ(対応履歴)
↓
通知 運用担当者が通知連携を設定し、アラートを受け取る。 notification サービス Notification(通知) NotificationIntegration エンティティ(通知連携)
↓
管理者 ユーザー管理 管理者が利用者を登録し、情報変更や利用停止を行う。 user サービス User(ユーザー) User エンティティ(ユーザー)
↓
利用者 認証 利用者が認証情報を入力し、ログインする。 authentication サービス Account(認証アカウント) Account エンティティ(認証アカウント)
↓
管理者 認可 管理者がロールと権限を割り当て、操作できる範囲を決める。 authorization サービス Role(ロール) Role エンティティ(ロール)、Permission エンティティ(権限)
↓
クォータ管理 管理者が利用上限を設定し、変更する。 quota サービス Quota(クォータ) Quota エンティティ(クォータ)
flowchart LR
  subgraph core["コアドメイン"]
    monitoring("監視")
    incident("インシデント管理")
  end
  subgraph supporting["支援サブドメイン"]
    notification("通知")
  end
  subgraph generic["汎用サブドメイン"]
    userManagement("ユーザー管理")
    authentication("認証")
    authorization("認可")
    quotaManagement("クォータ管理")
  end

  monitoring -.->|利用上限を参照| quotaManagement
  monitoring -.->|インシデントの対応状況を参照| incident
  monitoring -.->|通知先の設定を参照| notification
  userManagement -.->|認証アカウントを参照| authentication
  userManagement -.->|ユーザーの権限を参照| authorization
  userManagement -.->|ユーザーに紐づく監視設定を参照| monitoring
  userManagement -.->|ユーザーの利用上限を参照| quotaManagement
  authorization -.->|認証済みユーザーの識別情報を参照| authentication

例:書籍 EC サイト

書籍 EC サイトの例。

コアドメインとサブドメインに基づいたユビキタス言語と境界づけられたコンテキストを以下に示す。

書籍の品ぞろえと在庫管理を強みとするサイトを想定し、仕入れと在庫管理をコアドメイン、マーケティングを支援サブドメインに分類する。 各ノードは境界づけられたコンテキストを表し、矢印は依存する側から依存先へ向ける。 同じ書籍を扱っていても、業務ごとに異なるモデルとデータを持つ。

在庫管理は仕入れで扱う書籍情報を参照し、マーケティングは在庫状況を参照してクーポンの対象書籍を決める例とする。 認証などの汎用サブドメインは、この図では省略する。

バイヤー (仕入れ) 部、マーケティング部、在庫管理部のドメインエキスパートは、『本 (商品) 』という名詞に対する意味合い/定義づけが異なる。

そのため、それぞれを『本』『クーポン』『在庫』というユビキタス言語として定義できる。 モデル名/データ名は、それぞれのユビキタス言語へ合わせた名前になる。

例えば、マーケの境界づけられたコンテキストでは、モデル名は Coupon となる。 また、割引期間データを保持する必要がある。 一方で、仕入部や在庫部ではこのデータは不要である。

一方、書籍の ISBN はすべての境界づけられたコンテキストのモデルに必要なデータである。

境界づけられたコンテキストを定義しない場合、1 個の商品モデルがすべてのデータを保持することとなり、それぞれのドメインエキスパートが関心を持たないデータも保持することになってしまう。

*表*

担当者 境界づけられたコンテキスト名 業務内容 マイクロサービス名 集約名 集約内のエンティティ
バイヤー 仕入れ バイヤーが書籍を選定し、仕入れ条件と数量を決める。 purchasing サービス Book(本) Book エンティティ(本)
↓
マーケ担当者 マーケティング マーケ担当者がクーポンの対象書籍と割引期間を決める。 marketing サービス Coupon(クーポン) Coupon エンティティ(クーポン)
↓
在庫管理者 在庫管理 在庫管理者が在庫数を確認し、在庫を引き当てる。 inventory サービス Inventory(在庫) Inventory エンティティ(在庫)
flowchart LR
  subgraph core["コアドメイン"]
    purchasing("仕入れ")
    inventoryManagement("在庫管理")
  end
  subgraph supporting["支援サブドメイン"]
    marketing("マーケティング")
  end

  inventoryManagement -.->|書籍情報を参照| purchasing
  marketing -.->|在庫状況を参照| inventoryManagement

book_ec_ubiquitous_language

書籍モデルの実装例は次のとおりである。

// カタログコンテキストの書籍モデル
type Book struct {
    // 書籍の一意の識別子
    Isbn string
    // 書籍のタイトル
    Title string
    // 書籍の著者
    Author string
    // 書籍の説明
    Description string
    // 書籍の販売価格
    Price int
}

func NewBook(isbn string, title string, author string, description string, price int) *Book {
    return &Book{
        Isbn:        isbn,
        Title:       title,
        Author:      author,
        Description: description,
        Price:       price,
    }
}
/** カタログコンテキストの書籍モデル */

interface Book {
  /** 書籍の一意の識別子 */
  isbn: string;
  /** 書籍のタイトル */
  title: string;
  /** 書籍の著者 */
  author: string;
  /** 書籍の説明 */
  description: string;
  /** 書籍の販売価格 */
  price: number;
}
function createBook(
  isbn: string,
  title: string,
  author: string,
  description: string,
  price: number,
): Book {
  return {
    isbn,
    title,
    author,
    description,
    price,
  };
}
/** カタログコンテキストの書籍モデル */
class Book
{
    public function __construct(
        /** 書籍の一意の識別子 */
        public string $isbn,
        /** 書籍のタイトル */
        public string $title,
        /** 書籍の著者 */
        public string $author,
        /** 書籍の説明 */
        public string $description,
        /** 書籍の販売価格 */
        public int $price,
    ) {
    }
}
type BookStatus string

const (
    InStock    BookStatus = "IN_STOCK"
    OutOfStock BookStatus = "OUT_OF_STOCK"
)

// 在庫コンテキストの書籍モデル
type Book struct {
    // 書籍の一意の識別子
    Isbn string
    // 書籍のタイトル
    Title string
    // 書籍の在庫数
    QuantityAvailable int
    // 保管場所
    Location string
    // 書籍の在庫ステータス
    Status BookStatus
}

func NewBook(isbn string, title string, quantityAvailable int, location string, status BookStatus) *Book {
    return &Book{
        Isbn:              isbn,
        Title:             title,
        QuantityAvailable: quantityAvailable,
        Location:          location,
        Status:            status,
    }
}
/** 在庫コンテキストの書籍モデル */

interface Book {
  /** 書籍の一意の識別子 */
  isbn: string;
  /** 書籍のタイトル */
  title: string;
  /** 書籍の在庫数 */
  quantityAvailable: number;
  /** 保管場所 */
  location: string;
  /** 書籍の在庫ステータス */
  status: "IN_STOCK" | "OUT_OF_STOCK";
}
function createBook(
  isbn: string,
  title: string,
  quantityAvailable: number,
  location: string,
  status: "IN_STOCK" | "OUT_OF_STOCK",
): Book {
  return {
    isbn,
    title,
    quantityAvailable,
    location,
    status,
  };
}
enum BookStatus: string
{
    case InStock = "IN_STOCK";
    case OutOfStock = "OUT_OF_STOCK";
}

/** 在庫コンテキストの書籍モデル */
class Book
{
    public function __construct(
        /** 書籍の一意の識別子 */
        public string $isbn,
        /** 書籍のタイトル */
        public string $title,
        /** 書籍の在庫数 */
        public int $quantityAvailable,
        /** 保管場所 */
        public string $location,
        /** 書籍の在庫ステータス */
        public BookStatus $status,
    ) {
    }
}

例:完全個室ジムを運営するハコジム

完全個室ジムを運営するハコジムの例。

個室ジムドメインのコアドメインとサブドメインに基づく境界づけられたコンテキスト。

認証コンテキスト、予約コンテキスト、顧客管理コンテキスト、銀行支払いコンテキスト、クレジットカード支払いコンテキストがある。

*表*

以下のマイクロサービス名、集約名、エンティティ名は、上記のコンテキストに基づく説明用の設計例であり、実際のサービス構成や実装名を示すものではない。

担当者 境界づけられたコンテキスト名 業務内容 マイクロサービス名 集約名 集約内のエンティティ
利用者 認証 利用者が認証情報を入力し、ログインする。 authentication サービス Account(認証アカウント) Account エンティティ(認証アカウント)
↓
予約 利用者が個室ジムの利用日時を選び、予約やキャンセルを行う。 reservation サービス Reservation(予約) Reservation エンティティ(予約)
↓
顧客管理担当者 顧客管理 顧客管理担当者が顧客情報を登録・変更し、利用停止を行う。 customer サービス Customer(顧客) Customer エンティティ(顧客)
↓
利用者、経理担当者 銀行支払い 利用者が銀行を通じて料金を支払い、経理担当者が入金を確認する。 bank-payment サービス BankPayment(銀行支払い) BankPayment エンティティ(銀行支払い)
↓
クレジットカード支払い 利用者がカードで料金を支払い、経理担当者が決済状況を確認する。 card-payment サービス CardPayment(クレジットカード支払い) CardPayment エンティティ(クレジットカード支払い)

hacogym_bounded-context

例:契約請求管理アプリケーションを提供するアルプ

契約請求管理アプリケーションを提供するアルプの例。

コアドメインとサブドメインに基づいたユビキタス言語と境界づけられたコンテキストを以下に示す。

契約管理コンテキスト、商品管理コンテキスト、請求管理コンテキストがある。

アルプでは、ユビキタス言語を定期的に認識合わせするために、週次でユビキタス言語を追加し、また定義を更新している。

*表*

以下のマイクロサービス名、集約名、エンティティ名は、上記のコンテキストに基づく説明用の設計例であり、実際のサービス構成や実装名を示すものではない。

担当者 境界づけられたコンテキスト名 業務内容 マイクロサービス名 集約名 集約内のエンティティ
契約担当者 契約管理 契約担当者が契約商品、契約期間、請求条件を決め、契約を更新する。 contract サービス Contract(契約) Contract エンティティ(契約)、ContractItem エンティティ(契約明細)
↓
商品管理担当者 商品管理 商品管理担当者が契約対象の商品情報を登録・変更する。 product サービス Product(商品) Product エンティティ(商品)
↓
請求担当者 請求管理 請求担当者が請求明細と金額を確認し、請求書を発行する。 billing サービス Invoice(請求) Invoice エンティティ(請求)、InvoiceItem エンティティ(請求明細)

契約と請求の管理を強みとする設計例として、契約管理と請求管理をコアドメイン、商品管理を支援サブドメインに分類する。 各ノードは境界づけられたコンテキストを表し、矢印は依存する側から依存先へ向ける。 この分類と依存関係は説明用の例である。

flowchart LR
  subgraph core["コアドメイン"]
    contract("契約管理")
    billing("請求管理")
  end
  subgraph supporting["支援サブドメイン"]
    product("商品管理")
  end

  contract -.->|契約対象の商品を参照| product
  billing -.->|契約の請求条件を参照| contract

contract_billing_management_ubiquitous_language

例:会計アプリケーションを提供する freee

会計アプリケーションを提供する freee の例。

財務会計コンテキスト、会計ワークフローコンテキスト、従業員管理コンテキストがある。

以下のマイクロサービス名、集約名、エンティティ名は、上記のコンテキストに基づく説明用の設計例であり、実際のサービス構成や実装名を示すものではない。

担当者 境界づけられたコンテキスト名 業務内容 マイクロサービス名 集約名 集約内のエンティティ
経理担当者 財務会計 経理担当者が仕訳を登録し、会計記録を確認する。 accounting サービス JournalEntry(仕訳) JournalEntry エンティティ(仕訳)、JournalEntryLine エンティティ(仕訳明細)
↓
従業員、承認者 会計ワークフロー 従業員が会計申請を提出し、承認者が内容を確認して承認する。 accounting-workflow サービス AccountingApplication(会計申請) AccountingApplication エンティティ(会計申請)、ApprovalStep エンティティ(承認ステップ)
↓
人事担当者 従業員管理 人事担当者が従業員情報を登録・変更し、在籍状況を更新する。 employee サービス Employee(従業員) Employee エンティティ(従業員)

財務会計を強みとする設計例として、財務会計をコアドメイン、会計ワークフローを支援サブドメイン、従業員管理を汎用サブドメインに分類する。 各ノードは境界づけられたコンテキストを表し、矢印は依存する側から依存先へ向ける。 この分類と依存関係は説明用の例である。

flowchart LR
  subgraph core["コアドメイン"]
    accounting("財務会計")
  end
  subgraph supporting["支援サブドメイン"]
    workflow("会計ワークフロー")
  end
  subgraph generic["汎用サブドメイン"]
    employee("従業員管理")
  end

  accounting -.->|承認済みの会計申請を参照| workflow
  workflow -.->|申請者と承認者の従業員情報を参照| employee

freee_bounded-context

個人間送金ドメイン

個人間送金業務の担当者が変わる境目を見つけ、それを境界づけられたコンテキストにする。

以下の表・図は一般的な設計例であり、特定の実装のサービス構成や呼び出し関係を示すものではない。

精算は集計と事業者別の処理に分け、口座エイリアス管理と送金明細の作成も取り上げる。

*表*

担当者 境界づけられたコンテキスト名 業務内容 マイクロサービス名 集約名 集約内のエンティティ
送金人 送金取引 送金人が送金先と金額を指定し、送金状況を確認する。 remittance サービス Remittance(送金) Remittance エンティティ(送金)、RemittanceHistories エンティティ(送金履歴)、RemittanceStatuses エンティティ(送金ステータス)
↓
受取人 請求 受取人が送金人に支払いを依頼し、入金を確認する。 payment サービス Payment(請求) Payment エンティティ(請求)、TransactionHistory エンティティ(取引履歴)
↓
利用者 銀行口座 利用者が送金元口座や受取口座を登録し、口座情報を確認する。 BankAccount サービス BankAccount(銀行口座) BankAccount エンティティ(銀行口座)
↓
口座エイリアス 利用者が口座エイリアスを登録・変更し、送金先の口座を指定する。 account-alias サービス BankAccount(銀行口座)、ValueAccount(バリュー口座) BankAccount エンティティ(銀行口座)、ValueAccount エンティティ(バリュー口座)。それぞれ別の集約とする。
↓
利用者、本人確認担当者 ユーザー 利用者が情報を登録し、本人確認担当者が本人確認や利用停止を行う。 user サービス User(ユーザー) User エンティティ(ユーザー)
↓
精算担当者 精算集計 精算担当者が対象期間を指定し、取引件数と金額の集計結果を確認する。 settlement-aggregation サービス AggregationSettlement(精算集計) AggregationSettlement エンティティ(精算集計)
↓
銀行間精算 精算担当者が銀行間の受け払い金額と精算ファイルを確認する。 bank-settlement サービス BankSettlementFile(銀行間精算ファイル) BankSettlementFile エンティティ(銀行間精算ファイル)、BankSettlementRecord エンティティ(銀行間精算レコード)
↓
バリュー事業者精算 精算担当者がバリュー事業者ごとの受け払い金額と精算ファイルを確認する。 value-provider-settlement サービス ValueProviderSettlementFile(バリュー事業者精算ファイル) ValueProviderSettlementFile エンティティ(バリュー事業者精算ファイル)、ValueProviderSettlementRecord エンティティ(バリュー事業者精算レコード)
↓
照合担当者 送金照合 照合担当者が送金記録と外部データを突き合わせ、差分を調べる。 reconciliation サービス Reconciliation(照合) Reconciliation エンティティ(照合)、ReconciliationResult エンティティ(照合結果)、Difference エンティティ(差分)
↓
業務担当者 レポーティング 業務担当者が送金履歴、送金明細、精算結果、照合結果を出力する。 reporting サービス Reporting(レポーティング) RemittanceHistory エンティティ(送金履歴)、RemittanceDetailFile エンティティ(送金明細ファイル)
↓
送金明細 業務担当者が銀行やバリュー事業者向けの送金明細を取得・確認する。 remittance-detail サービス BankDailyRemittanceDetailFile(銀行日次送金明細ファイル) BankDailyRemittanceDetailFile エンティティ(銀行日次送金明細ファイル)
↓
管理者 送金取引管理ポータル 管理者が送金取引を照会し、例外対応や再処理を行う。 web-portal サービス RemittanceManagement(送金取引管理) web ポータルのフロントエンドアプリケーション

個人間送金の取引処理を強みとする設計例として、送金取引をコアドメイン、ユーザーを汎用サブドメイン、その他を支援サブドメインに分類する。

各ノードは境界づけられたコンテキストを表し、破線は依存する側から参照先へ向ける。

口座情報の取得と送金の呼び出しをAPI層が調整する設計とし、送金取引から口座エイリアスへの直接の矢印は付けない。

flowchart LR
  subgraph core["コアドメイン"]
    remittance("送金取引")
  end
  subgraph supporting["支援サブドメイン"]
    payment("請求")
    bankAccount("銀行口座")
    accountAlias("口座エイリアス")
    aggregation("精算集計")
    bankSettlement("銀行間精算")
    valueSettlement("バリュー事業者精算")
    reconciliation("送金照合")
    reporting("レポーティング")
    remittanceDetail("送金明細")
    portal("送金取引管理ポータル")
  end
  subgraph generic["汎用サブドメイン"]
    user("ユーザー")
  end

  remittance -.->|送金元と受取先の口座を参照| bankAccount
  remittance -.->|送金人と受取人を参照| user
  remittance -.->|支払対象の請求を参照| payment
  payment -.->|請求先と請求元のユーザーを参照| user
  bankAccount -.->|口座所有者を参照| user
  aggregation -.->|送金履歴を参照| remittance
  bankSettlement -.->|銀行間の精算集計結果を参照| aggregation
  valueSettlement -.->|バリュー事業者の精算集計結果を参照| aggregation
  reconciliation -.->|照合対象の送金記録を参照| remittance
  reporting -.->|送金履歴を参照| remittance
  reporting -.->|銀行間の精算結果を参照| bankSettlement
  reporting -.->|バリュー事業者の精算結果を参照| valueSettlement
  reporting -.->|照合結果を参照| reconciliation
  remittanceDetail -.->|送金履歴を参照| remittance
  portal -.->|送金取引の状況を参照| remittance
  portal -.->|照合結果を参照| reconciliation

銀行口座決済ドメイン

決済・送金、加盟店の運用、精算で扱う業務の違いから、境界づけられたコンテキストを分ける。

サービス名とモデル名は一般化し、集約とドメイン分類は設計例とする。

売上・返金では取引サマリが子の取引を管理するが、その他の集約境界は候補である。

加盟店業務と精算業務では、同じ加盟店でも扱う情報と変更の目的が異なる。

*表*

担当者 境界づけられたコンテキスト名 業務内容 マイクロサービス名 集約名 集約内のエンティティ
利用者 送金 利用者が送金先と金額を指定し、送金結果を確認する。 remittance サービス Remittance(送金) RemittanceHistory エンティティ(送金履歴)、RemittanceStatus エンティティ(送金ステータス)
↓
請求支払 利用者が請求内容を確認し、支払いを行う。 bill-payment サービス BillPayment(請求支払) BillPaymentHistory エンティティ(請求支払履歴)、BillPaymentInquiryHistory エンティティ(請求照会履歴)
↓
口座・エイリアス管理 利用者が口座に紐づくエイリアスを登録し、利用限度額を確認する。 user-account サービス User(利用者)、AccountAlias(口座エイリアス) User エンティティ(利用者)、AccountAlias エンティティ(口座エイリアス)、UserLimitAmount エンティティ(利用限度額)
↓
運用担当者 請求取引管理 運用担当者が請求取引と最新の状態を確認する。 bill-transaction サービス BillTransaction(請求取引) BillTransaction エンティティ(請求取引)、BillTransactionStatus エンティティ(請求取引ステータス)
↓
照合担当者 送金照合 照合担当者が送金記録と外部取引を突き合わせ、差分を調べる。 remittance-reconciliation サービス RemittanceReconciliation(送金照合) ReconciliationProgress エンティティ(照合処理の進捗)
↓
請求支払照合 照合担当者が請求支払記録と外部取引を突き合わせ、差分を調べる。 bill-payment-reconciliation サービス BillPaymentReconciliation(請求支払照合) ReconciliationProgress エンティティ(照合処理の進捗)
↓
加盟店担当者 加盟店・店舗・端末管理 加盟店担当者が加盟店、店舗、端末を登録し、利用可否を変更する。 merchant-management サービス Merchant(加盟店)、ShopGroup(店舗グループ)、Shop(店舗)、Terminal(端末) Merchant エンティティ(加盟店)、ShopGroup エンティティ(店舗グループ)、Shop エンティティ(店舗)、Terminal エンティティ(端末)
↓
売上・返金 加盟店担当者が売上を確定し、必要に応じて返金する。 sales-transaction サービス TransactionSummary(取引サマリ) TransactionSummary エンティティ(取引サマリ)、Transaction エンティティ(売上・返金取引)
↓
業務担当者 決済取引照会・出力 業務担当者が決済取引を検索し、取引一覧を出力する。 transaction-query サービス —(照会・出力用モデル) —(取引と加盟店の情報を参照する)
↓
運用担当者 金融機関管理 運用担当者が金融機関の利用条件やメンテナンス予定を設定する。 bank-management サービス Bank(金融機関)、BankReservation(変更予約) Bank エンティティ(金融機関)、BankReservation エンティティ(変更予約)、BankLimitAmount エンティティ(限度額)、BankMaintenanceTime エンティティ(メンテナンス予定)
↓
業務担当者 請求元管理 業務担当者が請求元と請求項目を登録する。 biller-management サービス Biller(請求元)、BillItem(請求項目) Biller エンティティ(請求元)、BillItem エンティティ(請求項目)
↓
接続管理担当者 決済事業者管理 接続管理担当者が決済事業者と接続条件を設定する。 payment-provider-management サービス PaymentProvider(決済事業者) PaymentProvider エンティティ(決済事業者)、PaymentProviderApi エンティティ(接続先API)、PaymentProviderKey エンティティ(認証鍵)
↓
精算担当者 精算先加盟店管理 精算担当者が入金先口座を登録し、変更を予約する。 settlement-merchant-management サービス SettlementMerchant(精算先加盟店)、MerchantBankAccount(入金先口座) SettlementMerchant エンティティ(精算先加盟店)、MerchantReservation エンティティ(加盟店変更予約)、MerchantBankAccount エンティティ(入金先口座)、MerchantBankAccountReservation エンティティ(口座変更予約)
↓
精算金融機関管理 精算担当者が金融機関別の手数料を設定し、変更を予約する。 settlement-bank-management サービス SettlementBank(精算金融機関)、BankFee(手数料) SettlementBank エンティティ(精算金融機関)、BankReservation エンティティ(変更予約)、BankFee エンティティ(手数料)、BankFeeReservation エンティティ(手数料変更予約)
↓
加盟店担当者 入金情報照会 加盟店担当者が入金額と明細を確認する。 merchant-deposit-query サービス —(照会用モデル) —(入金情報と入金明細を参照する)
↓
運用担当者 ファイル・伝送管理 運用担当者が業務ファイルと伝送結果を確認する。 file-management サービス、file-transmission サービス BusinessFile(業務ファイル)、Transmission(伝送) BusinessFile エンティティ(業務ファイル)、Transmission エンティティ(伝送)
↓
バッチ実行管理 運用担当者がバッチ処理の実行状況を確認する。 batch-management サービス Batch(バッチ) Batch エンティティ(バッチ)
↓
精算担当者 精算分類・連絡先管理 精算担当者が業務の分類と金融機関の連絡先を設定する。 settlement-classification-management サービス Classification(分類)、ContactPerson(連絡先担当者) Classification エンティティ(分類)、ContactPerson エンティティ(連絡先担当者)
↓
管理者 管理者アカウント 管理者が加盟店業務や精算業務の担当者を登録し、権限を設定する。 merchant-user-management サービス、settlement-user-management サービス Administrator(管理者) Administrator エンティティ(管理者)、LoginHistory エンティティ(ログイン履歴)、AccessControl エンティティ(アクセス制御)
↓
業務担当者 お知らせ・メール管理 業務担当者がお知らせを登録し、メールの内容や宛先を管理する。 notice-management サービス、mail-management サービス Notice(お知らせ)、Mail(メール)、MailTemplate(メールテンプレート) Notice エンティティ(お知らせ)、Mail エンティティ(メール)、MailAddress エンティティ(宛先)、MailTemplate エンティティ(メールテンプレート)
↓
管理者 操作履歴管理 管理者が業務担当者の操作履歴を確認する。 operation-history サービス OperationHistory(操作履歴) OperationHistory エンティティ(操作履歴)

決済・送金処理を強みとする設計例として、送金、請求支払、売上・返金をコアドメインに分類する。

管理者アカウント、お知らせ・メール管理、操作履歴管理は汎用サブドメイン、その他は支援サブドメインとする。

各ノードはコンテキスト候補を表し、破線は呼び出す側から呼び出し先、またはデータを利用する側から管理元へ向ける。

API呼び出しとDB参照・更新は、矢印のラベルで区別する。

照会用モデルを独立した集約とは扱わず、未確認の参照関係は図に含めない。

flowchart LR
  subgraph core["コアドメイン"]
    remittance("送金")
    billPayment("請求支払")
    sales("売上・返金")
  end
  subgraph supporting["支援サブドメイン"]
    account("口座・エイリアス管理")
    billTransaction("請求取引管理")
    remittanceReconciliation("送金照合")
    billReconciliation("請求支払照合")
    merchant("加盟店・店舗・端末管理")
    query("決済取引照会・出力")
    bank("金融機関管理")
    biller("請求元管理")
    provider("決済事業者管理")
    settlementMerchant("精算先加盟店管理")
    settlementBank("精算金融機関管理")
    deposit("入金情報照会")
    file("ファイル・伝送管理")
    batch("バッチ実行管理")
    classification("精算分類・連絡先管理")
  end
  subgraph generic["汎用サブドメイン"]
    administrator("管理者アカウント")
    notice("お知らせ・メール管理")
    history("操作履歴管理")
  end

  remittance -.->|利用限度額を参照| account
  billPayment -.->|利用限度額を参照| account
  bank -.->|決済事業者情報を参照| provider
  sales -.->|加盟店・端末情報を取得| merchant
  billTransaction -.->|請求元情報を参照| biller
  billTransaction -.->|金融機関の限度額を参照| bank
  billTransaction -.->|決済事業者情報を参照| provider
  remittanceReconciliation -.->|送金履歴をDB参照・更新| remittance
  billReconciliation -.->|請求支払履歴をDB参照・更新| billPayment
  batch -.->|加盟店の変更予約の反映を依頼| settlementMerchant
  batch -.->|金融機関の業務ファイル作成を依頼| settlementBank
  batch -.->|分類・手数料ファイル作成を依頼| classification
  batch -.->|メール送信処理を依頼| notice
  settlementMerchant -.->|登録ファイルを取得し状態を更新| file
  settlementMerchant -.->|メールテンプレート取得・送信登録を依頼| notice

QR 決済ドメイン

QR 決済業務の担当者が変わる境目を見つけ、それを境界づけられたコンテキストにする。 集約とエンティティ、コア・支援・汎用の分類は設計例とする。

*表*

担当者 境界づけられたコンテキスト名 業務内容 マイクロサービス名 集約名 集約内のエンティティ
利用者 QR 決済取引 利用者が QR コードを読み取り、決済内容を確認して支払いを行う。 transaction サービス Transaction(取引) Transaction エンティティ(取引)
↓
加盟店担当者 加盟店契約 加盟店担当者が利用可能な決済手段と精算条件を決め、契約を登録する。 merchant サービス MerchantContract(契約) MerchantContract エンティティ(加盟店契約)、Merchant エンティティ(加盟店)
↓
精算担当者 精算 精算担当者が取引結果を確認し、事業者間の受け払い金額を確定する。 payment サービス Payment(精算) Payment エンティティ(精算)
↓
利用者、本人確認担当者 ユーザー 利用者が情報を登録し、本人確認担当者が本人確認や利用停止を行う。 user サービス User(ユーザー) User エンティティ(ユーザー)
↓
照合担当者 QR 決済照合 照合担当者が決済記録と外部データを突き合わせ、差分を調べる。 reconciliation サービス Reconciliation(照合) Reconciliation エンティティ(照合)、ReconciliationResult エンティティ(照合結果)、Difference エンティティ(差分)
↓
管理者 QR 決済取引管理ポータル 管理者が決済取引を照会し、例外対応や再処理を行う。 web-portal サービス QrPaymentManagement(QR 決済取引管理) web ポータルのフロントエンドアプリケーション
↓
コンプライアンス担当者 マネーロンダリング対策の判定 コンプライアンス担当者が判定条件を設定し、遮断された取引を確認する。 anti-money-laundering サービス AntiMoneyLaunderingCheck(マネーロンダリング対策の判定) AntiMoneyLaunderingCheck エンティティ(マネーロンダリング対策の判定)、BlockedTransaction エンティティ(遮断取引)
↓
接続管理担当者 決済事業者接続 接続管理担当者が決済事業者との接続条件を設定し、通信状況を確認する。 payment-connector サービス PaymentIntegration(決済連携) PaymentIntegration エンティティ(決済連携)
↓
API 管理者 API 認証 API 管理者が認証トークンを発行・失効し、接続元の認証情報を管理する。 api-authentication サービス AuthenticationToken(認証トークン) AuthenticationToken エンティティ(認証トークン)
↓
管理者 管理者ユーザー管理 管理者が担当者のアカウントを登録し、権限変更や無効化を行う。 user-management サービス User(管理者ユーザー) User エンティティ(管理者ユーザー)

各ノードは境界づけられたコンテキストを表し、破線は依存する側から依存先へ向ける。 取引処理は決済の方向ごとに分かれ、加盟店管理の照会はインバウンド、マネーロンダリング対策の判定はアウトバウンドで行う。

flowchart LR
  subgraph core["コアドメイン"]
    transaction("QR 決済取引")
  end
  subgraph supporting["支援サブドメイン"]
    merchant("加盟店契約")
    settlement("精算")
    reconciliation("QR 決済照合")
    portal("QR 決済取引管理ポータル")
    antiMoneyLaundering("マネーロンダリング対策の判定")
    connector("決済事業者接続")
  end
  subgraph generic["汎用サブドメイン"]
    user("ユーザー")
    authentication("API 認証")
    administrator("管理者ユーザー管理")
  end

  transaction -.->|加盟店の利用可否を照会| merchant
  transaction -.->|マネーロンダリング対策の判定を依頼| antiMoneyLaundering
  transaction -.->|加盟店照会・決済を依頼| connector
  reconciliation -.->|取引を取得し照合結果を反映| transaction
  portal -.->|管理者の認証・ユーザー管理を依頼| administrator

決済利用者と管理者のユーザー管理は別に扱い、API 認証への呼び出しは API 受付層で行う。 精算取引の照会は実装されているが、日次精算ファイル作成バッチは未実装のため、精算の依存関係は図に含めない。


05. ドメインモデルの種類

隠蔽モデル

特定の境界づけられたコンテキストのみで使用するドメインモデルのこと。

bounded-context_domain-model_type

共有モデル

異なる境界づけられたコンテキスト間で、一部の状態を共有するドメインモデルのこと。

図では、在庫品目モデルがそれに相当する。

bounded-context_domain-model_type


06. コンテキストマップ

コンテキストマップとは

広義のドメイン全体を俯瞰する図のこと。

コアドメイン、サブドメイン、境界づけられたコンテキストを定義した後、これらの関係性を視覚化する。

異なるサブドメインの間で異なるユビキタス言語を使用する場合、境界づけられたコンテキストはサブドメインをまたがない。

一方で、同じユビキタス言語を使用する場合、境界づけられたコンテキストは複数のサブドメインにまたがる。

できる限り、各境界づけられたコンテキストでは異なるユビキタス言語を使用する。 そして、境界づけられたコンテキストが複数のサブドメインへまたがらないようにしたほうがよい (これ重要) 。

context-map

記法

完全個室ジムを運営するハコジムの例。

個室ジムドメインのそれぞれの境界づけられたコンテキストに基づくモデリング。

コアドメインの予約コンテキストとスマートロックコンテキストは、1 個のマイクロサービスとして内製化している。

一方で、それ以外の境界づけられたコンテキストは外製化している。

hacogym_subdomain_modeling

コンテキストマップとテーブルの関係

コンテキストマップに、各境界づけられたコンテキストに属するテーブルを点線の矢印で追加する。

データベースを 1 個のままにして、その中で境界づけられたコンテキストごとにテーブルを区切る場合、異なる境界づけられたコンテキストのデータを 1 個のデータベースで管理する「Shared Database パターン」を採用していることになる。

一方で、境界づけられたコンテキストごとにデータベースを分ける場合、「Database per service パターン」を採用していることになる。

監視 SaaS の例。

Shared Database パターンの場合、1 個のデータベースの中で、境界づけられたコンテキストごとにテーブルを区切る。

flowchart LR
  subgraph core["コアドメイン"]
    monitoring("監視")
    incident("インシデント管理")
  end

  subgraph supporting["支援サブドメイン"]
    notification("通知連携")
  end

  subgraph generic["汎用サブドメイン"]
    userManagement("ユーザー管理")
    authorization("認可")
    authentication("認証")
    quotaManagement("クォータ管理")
  end

  monitoring --> quotaManagement
  monitoring --> incident
  monitoring --> notification
  userManagement --> authentication
  userManagement --> authorization
  userManagement --> monitoring
  userManagement --> quotaManagement
  authorization --> authentication

  subgraph database["データベース"]
    userManagementTables@{ shape: st-rect, label: "User" }
    authorizationTables@{ shape: st-rect, label: "Role<br>Permission" }
    authenticationTables@{ shape: st-rect, label: "Account" }
    quotaManagementTables@{ shape: st-rect, label: "Quota" }
    monitoringTables@{ shape: st-rect, label: "MonitoringGroup<br>MonitoringRequest" }
    incidentTables@{ shape: st-rect, label: "Incident<br>IncidentHistory" }
    notificationTables@{ shape: st-rect, label: "NotificationIntegration" }
  end

  userManagement -.-> userManagementTables
  authorization -.-> authorizationTables
  authentication -.-> authenticationTables
  quotaManagement -.-> quotaManagementTables
  monitoring -.-> monitoringTables
  incident -.-> incidentTables
  notification -.-> notificationTables

Database per service パターンの場合、境界づけられたコンテキストごとにデータベースを分ける。

flowchart LR
  subgraph core["コアドメイン"]
    monitoring("監視")
    incident("インシデント管理")
  end

  subgraph supporting["支援サブドメイン"]
    notification("通知連携")
  end

  subgraph generic["汎用サブドメイン"]
    userManagement("ユーザー管理")
    authorization("認可")
    authentication("認証")
    quotaManagement("クォータ管理")
  end

  monitoring --> quotaManagement
  monitoring --> incident
  monitoring --> notification
  userManagement --> authentication
  userManagement --> authorization
  userManagement --> monitoring
  userManagement --> quotaManagement
  authorization --> authentication

  subgraph databases["データベース群"]
    subgraph notificationDatabase["通知連携のデータベース"]
      notificationTables@{ shape: st-rect, label: "NotificationIntegration" }
    end
    subgraph incidentDatabase["インシデント管理のデータベース"]
      incidentTables@{ shape: st-rect, label: "Incident<br>IncidentHistory" }
    end
    subgraph monitoringDatabase["監視のデータベース"]
      monitoringTables@{ shape: st-rect, label: "MonitoringGroup<br>MonitoringRequest" }
    end
    subgraph quotaManagementDatabase["クォータ管理のデータベース"]
      quotaManagementTables@{ shape: st-rect, label: "Quota" }
    end
    subgraph authenticationDatabase["認証のデータベース"]
      authenticationTables@{ shape: st-rect, label: "Account" }
    end
    subgraph authorizationDatabase["認可のデータベース"]
      authorizationTables@{ shape: st-rect, label: "Role<br>Permission" }
    end
    subgraph userManagementDatabase["ユーザー管理のデータベース"]
      userManagementTables@{ shape: st-rect, label: "User" }
    end
  end


  notification -.-> notificationTables
  incident -.-> incidentTables
  monitoring -.-> monitoringTables
  quotaManagement -.-> quotaManagementTables
  authentication -.-> authenticationTables
  authorization -.-> authorizationTables
  userManagement -.-> userManagementTables