🤫

設計テンプレートと筆者の記事の読み方

2024/11/13に公開

概要

筆者はすべてのサービスを同じ手順で設計しているので、ブログも同じように形式的に書きます。
当方のブログの構造とその理由についてここにまとめますので、一読してもらえれば他の記事が読みやすくなると思います。

なお、このテンプレート自体も改良を重ねており、記事の執筆時期が古かったりすると、テンプレートと完全に一致しない場合もあります。その点ご了承ください。

※本記事では、機械的な設計手順についてコードで表現している部分があります。また設計用語として数学的な表記も借用しています。少し特殊に見えるかもしれませんが、より正確な説明のための工夫としてご理解ください。

ブログテンプレート

# 概要
興味を引く概要、あと画像とかで実際どうなるかを最初に示す

# インフラ
docker-compose.yamlを示したり、aws構成図などを用いてインフラの説明などを行う

# 構成
mermaidとかでドメインとかアダプタの図を書いとく
必須ではない、ドメインに含めてもよさげ

# ドメイン
プロジェクトの中核となる「ドメイン」は、特定の技術([TECH_EXAMPLE_1]や[TECH_EXAMPLE_2]など)に依存しない、純粋なビジネスロジックを表現する部分です。

たとえば「[CORE_FUNCTIONALITY]」という機能は、[PREREQUISITES_LIST]ことだけを前提に設計できます。[IMPLEMENTATION_DETAILS]は、この時点では考えなくて良いのです。

このように技術的な実装から切り離すことで、仕様変更に強く、テストが書きやすく、コードの意図が明確になります。以下のコードは、そのドメインレイヤーの実装です。

``go
type UserRepository interface {
    FindByEmail(email string) (User, error)
}
``

# アダプタの実装
前章で示したドメインを、実際の技術を使って実装していきます。ここでは具体的に:

[TECH_IMPLEMENTATION_LIST]
使用しています。

先ほどのドメインレイヤーで定義したインターフェース([INTERFACE_LIST])に対して、それぞれの具体的な実装([CONCRETE_IMPLEMENTATION_LIST])を提供します。この方式のメリットは、例えば[EXAMPLE_REPLACEMENT]に置き換えたい場合、新しい[NEW_IMPLEMENTATION_CLASS]クラスを作るだけで、他のコードは一切変更する必要がないという点です。

また、これらの実装を[INTEGRATION_POINT]として統合することで、[BENEFIT_DESCRIPTION]。以下が各実装のコードです:

``go
type SqlUserRepository struct {
    db *sql.DB
}

func (r MysqlUserRepository) FindByEmail(email string) (User, error) {   
    return user, nil
}
``

# まとめ
全体のまとめと考察

設計手順

1. PoC作成

いきなりドメイン作成は難しいのでPoCを書いてイメージを掴みます。

2. ドメイン作成

1. インデックス化

入力コーパスをTextUnit(テキストユニット)に分割します。これらは、プロセスの残りの部分で分析可能な単位として機能し、出力において詳細な参照を提供します。
LLMを使用して、TextUnitから全ての実体(エンティティ)、関係性、重要な主張を抽出します。
Leiden手法を用いてグラフの階層的クラスタリングを実行します。これを視覚的に確認するには、上記の図1をご覧ください。各円は実体(例:人物、場所、組織)を表し、大きさはその実体の次数を、色はそのコミュニティを表しています。
各コミュニティとその構成要素のサマリーをボトムアップで生成します。これによりデータセットの全体的な理解を助けます。

2. Proof Tree作成

最上位のドメインを再帰的にサブドメインに分割します。
構成図では、ドメインの内容を木構造で表します。

from typing import List


class Lemma:
    """補題を表現するクラス。

    各補題は、他の補題(self.lemmas)が証明済みであることを前提として
    証明を構築することができます。

    Attributes:
        statement: 補題の内容を表現する文
        lemmas: この補題の証明で前提として使用する補題のリスト
    """

    def __init__(self, statement=None):
        self.statement = statement
        self.lemmas = []


class SolutionArchitect:
    def __init__(self, context_repository):
        self.context_repository = context_repository

    def build(self, theorem: Lemma) -> Lemma:
        """証明木を構築する。

        与えられた定理を基本補題まで再帰的に分解し、proof treeを構築します。
        構築後は、基本補題から順に証明を行うことで、全体の証明が完成します。

        Args:
            theorem: 証明したい定理・補題

        Returns:
            証明木が構築された定理
        """
        if self.is_primitive(theorem):
            return theorem

        for lemma in self.decompose(theorem):
            theorem.lemmas.append(self.build(lemma))

        return theorem

    def is_primitive(self, lemma: Lemma):
        """
        基本補題(primitive lemma)かどうかを判定
        - 他の補題から導出されない基本的な命題
        - これ以上分解すると本質的な意味が失われる
        - 公理や定義から直接証明できる
        などの条件を想定
        """
        ...

    def decompose(self, lemma: Lemma) -> List[Lemma]:
        """
        補題をより基本的な補題に分解
        - 結合された命題の分離
        - 証明のステップへの分解
        などの分解方法を想定
        """
        ...


# TODO: 前述のGraphRAGを利用したContextの取得が可能なリポジトリが必要
context_repository = ...

theorem = Lemma("Complex theorem statement")
proof_tree = SolutionArchitect(context_repository).build(theorem)

proof treeができたら、すべての基本補題をMockを用いて実装し、それらを組み合わせて原理実証を行います。

3. インフラ準備

データベースや外部サービスなどのインフラを準備します。
例えば、docker compose upしたりawsのインスタンス建てたりします。

4. アダプタ作成

ドメインで定義したインターフェースに従って、実際のインフラ(データベースや外部サービス)を使用した実装を行います。すべてのMockを置き換えたら完成です。

Discussion