🆎

プロダクト開発を加速するためのユビキタス言語の活用法

2024/08/08に公開

ユビキタス言語の重要性と運用方法

みなさん、プロダクト開発においてユビキタス言語の定義を行っていますか?

ユビキタス言語は、プロダクト開発において重要な役割を果たします。

私は新規サービス開発チームのマネージャーを務めていますが、ユビキタス言語の定義によってプロダクト開発がスムーズに進行していると感じています。本記事では、ユビキタス言語の紹介と、私のチームでの具体的な運用方法について詳しく説明します。

ユビキタス言語は、DDD(ドメイン駆動設計)において重要な概念の一つです。プロダクト開発において特定の事象や概念を関係者全員が同じ言葉で表現することで、コミュニケーションの混乱や誤解を防ぎ、プロジェクトの効率を高めるために考案されました。

プロダクトを開発する中で、様々な言葉が登場します。例えば、ネット広告のプロダクトを開発している際には、以下のような用語が頻繁に使用されます。

  • 広告主
  • メディア
  • キャンペーン
  • 予算
  • グロス
  • ネット

この中でも「メディア」という言葉は注意が必要です。これは、メディアを運営している会社を指す場合もあれば、媒体そのものを指す場合もあります。もしユビキタス言語の定義がしっかりしていないと、以下のようなやりとりが生じる可能性があります。

「今月のメディア別の売上を出してください。」

この依頼が来た場合、メディアを運営している会社別の売上なのか、媒体別の売上なのかが分からないですよね。そのため、以下のような確認が必要になります。

「会社別ですか?それとも媒体別ですか?」

このような追加のやりとりが発生するだけならまだしも、誤った判断をしてしまうリスクも存在します。ユビキタス言語の定義をしっかりと行うことで、こうした混乱を防ぎ、プロジェクトをより効率的に進行させることができます。

もし、ユビキタス言語が定義されている場合、以下のように用語を明確にすることができます:

  • メディア:媒体
  • メディア運営会社:メディアをもっている会社

これにより、例えば以下のような指示を受けた場合でも迷いや誤解が生じることはありません。

「今月のメディア別の売上を出してください。」

→ これは「今月の媒体別の売上を出してください。」という意味になります。

「今月のメディア運営会社別の売上を出してください。」

→ これは「今月の会社別の売上を出してください。」という意味になります。

このようにユビキタス言語を活用すれば、関係者全員が同じ理解のもとで作業を進めることができ、効率的で正確なコミュニケーションが可能となります。

ユビキタス言語の管理と運用のポイント

ユビキタス言語は非常に大切ですが、その管理は一筋縄ではいきません。

ユビキタス言語の定期メンテナンス

よくあるパターンとして、最初はしっかりとユビキタス言語を定義して一覧にまとめたものの、その後のメンテナンスが行われず、実際の状況と初期の定義がどんどん乖離していくことがあります。これにより、ユビキタス言語が形骸化し、誰も参照しなくなりがちです。

プロダクト開発を進める中では、常に新しい言葉や概念が出現します。さらに、一度決めたユビキタス言語の意味を変更する必要が出てくることもあります。ドメイン知識が深まっていく過程で、最初に決めたユビキタス言語が不適切であることに気づくこともありますし、開発が中止された機能に関連する言葉を削除する必要が生じることもあります。このように、ユビキタス言語は生きた存在のように変化していくため、定期的なメンテナンスが不可欠です。

私のチームではサービス開発の初期段階からユビキタス言語の運用を始めました。当初は毎週1時間を使ってメンテナンスミーティングを行い、新しい用語の定義や既存の用語の見直しを行っていました。ある程度仕様決めが進み、新しい単語が出現しなくなった後は、毎週30分に短縮してメンテナンスを続けています。ミーティングでは、起案された新しいユビキタス言語や見直しの必要がある用語について優先的に議論します。対象がない場合は、更新日が古いユビキタス言語を確認し、必要があれば変更を検討します。

ユビキタス言語の運用

また、Notionの詳細ページでは会話のコメントや変更履歴を記載しています。

関係者への周知

ユビキタス言語は関係者全員が使用する前提で定義されるべきです。しかし、よくある問題として、開発チームだけでユビキタス言語を決めてしまい、ビジネスチームへの周知を怠ることがあります。これによって、開発チームとビジネスチームとの間で認識の齟齬が生じることがあります。

私たちは開発チームとプロジェクトマネージャー、広告運用担当者が定期的にミーティングを行い、ユビキタス言語について協議しています。本来はビジネスチーム全体と合同でミーティングを行うことが理想ですが、タイミングを合わせることが難しいため、ミーティングで仮決定した内容を共有し、合意を得るようにしています。

誰でも運用ができるようになっている

ユビキタス言語は定期的なメンテナンスが非常に重要です。そのため、運用を属人化しないよう努めています。

現在、管理にはすでに記載済みですが Notion を使用しています。一時期、バージョン管理のために GitHub でマークダウン形式の運用を検討しましたが、非エンジニアにとってはハードルが高すぎると判断し、取りやめました。

さらに、メンテナンスミーティングのファシリテーションは基本的に私が担当していますが、場合によっては他のメンバーに任せることもあります。これにより、チーム全体での一貫した理解とスムーズな運用を実現しています。

こうした取り組みにより、ユビキタス言語が誰でも扱いやすく、効率的に運用できるようになっています。

補足: ユビキタス言語のバージョン管理の改善

とはいえ、バージョン管理はやはり重要です。Notion だけではカバーしきれない部分がありました。この問題を解決するために、最近の開発合宿で GitHub を使ったバージョン管理システムを試作してみました。

現在はまだ試験段階であり、個人的に運用を試している段階ですが、効果が見込めると判断した場合にはチーム全体の運用に取り入れる予定です。

Github Actionsを利用したユビキタス言語のバージョン管理

  1. PythonのコードでNotion APIからデータ取得
    • Pythonスクリプトを使用して、Notion APIを叩き、管理しているユビキタス言語の一覧を取得します。
  2. データベースからマークダウンへの変換
    • Notionのデータベースから取得したユビキタス言語の一覧を、マークダウン形式のテーブルに変換します。
  3. Githubへのプッシュとプルリクエスト作成
    • 変換したマークダウン形式のユビキタス言語の一覧をGithubにプッシュし、プルリクエストを作成します。

PythonとGithub Actionsのソースコードも貼っておきます。

import requests
import json
import os

# Notion APIの設定
NOTION_API_URL = 'https://api.notion.com/v1/databases/' + \
    os.environ.get("NOTION_UBIQUITOUS_LANGUAGE_PAGE_ID") + '/query'
HEADERS = {
    'Authorization': 'Bearer ' + os.environ.get("NOTION_UBIQUITOUS_LANGUAGE_CONTROL_INTEGTATION_API_KEY"),
    'Notion-Version': '2022-06-28'
}

def fetch_notion_data():
    payload = {}
    response = requests.post(NOTION_API_URL, headers=HEADERS, json=payload)
    return response.json()

def create_markdown_table(data):
    if not data['results']:
        return "No data available."

    # 指定された順番でプロパティ名をリストに追加
    property_names = [
        '名前', '英名', 'カテゴリ', 'ステータス',
        '概念', '簡易説明'
    ]

    header_row = "| " + " | ".join(property_names) + " |\n"
    separator_row = "| " + " | ".join(["---"] * len(property_names)) + " |\n"

    # データ行を作成
    table_body = ""
    for row in data['results']:
        row_values = []
        for property_name in property_names:
            property_value = row['properties'].get(property_name, {})
            property_type = property_value.get('type', '')
            if property_type == 'title':
                value_str = property_value[property_type][0]['plain_text'] if property_value[property_type] else ""
            elif property_type == 'rich_text':
                value_str = property_value[property_type][0]['plain_text'] if property_value[property_type] else ""
            elif property_type == 'date':
                value_str = property_value.get(
                    property_type, {}).get('start', '')
            elif property_type == 'status':
                value_str = property_value.get(
                    property_type, {}).get('name', '')
            elif property_type == 'multi_select':
                value_str = ', '.join(
                    [item['name'] for item in property_value.get(property_type, [])])
            elif property_type == 'last_edited_time':
                value_str = property_value.get(property_type, '')
            elif property_type == 'created_time':
                value_str = property_value.get(property_type, '')
            elif property_type == 'created_by':
                value_str = property_value.get(property_type, {}).get('id', '')
            else:
                value_str = json.dumps(
                    property_value, ensure_ascii=False).replace('\n', '')

            # 簡易説明の改行を<br>に置換
            if property_name == '簡易説明':
                value_str = value_str.replace('\n', '<br>')

            row_values.append(value_str)
        table_body += "| " + " | ".join(row_values) + " |\n"

    return header_row + separator_row + table_body

def save_to_markdown(content, filename):
    with open(filename, 'w', encoding='utf-8') as f:
        f.write(content)

def main():
    data = fetch_notion_data()
    markdown_content = create_markdown_table(data)
    filename = "notion_data.md"

    save_to_markdown(markdown_content, filename)
    print(f"Markdownファイルを作成しました: {filename}")

if __name__ == "__main__":
    main()
name: Run Python Script on Manual Trigger

on:
  workflow_dispatch:

jobs:
  run-script:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: "3.12.3"

      - name: Install dependencies
        run: |
          python -m venv venv
          source venv/bin/activate
          pip install -r requirements.txt

      - name: Run Python script
        env:
          NOTION_UBIQUITOUS_LANGUAGE_PAGE_ID: ${{ secrets.NOTION_UBIQUITOUS_LANGUAGE_PAGE_ID }}
          NOTION_UBIQUITOUS_LANGUAGE_CONTROL_INTEGTATION_API_KEY: ${{ secrets.NOTION_UBIQUITOUS_LANGUAGE_CONTROL_INTEGTATION_API_KEY }}
        run: |
          source venv/bin/activate
          python main.py

      - name: Commit and push changes
        run: |
          # Gitを設定
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"

          # 変更があるか確認
          if git diff --exit-code; then
            echo "変更なし"
            exit 0
          fi

          # 変更を追加してコミット
          git add .
          git commit -m "Add generated markdown file"

          # 変更をoutput-branchにプッシュ(存在しない場合は作成)
          git push origin HEAD:refs/heads/output-branch -f

      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          commit-message: "Add generated markdown file"
          branch: output-branch
          base: main
          title: "Add generated markdown file"
          body: |
            This PR includes the generated markdown file.

最後に

ユビキタス言語の管理運用には一定の管理コストがかかりますが、その恩恵は計り知れません。特に、言葉の揺らぎによる不要な会話や誤解を減少させ、開発をスムーズに進めることができます。私の経験からも、ユビキタス言語の導入とメンテナンスは非常におすすめです。

フクロウラボ エンジニアブログ

Discussion