From 6b3d57803c4cdbf7814c774f2c83e4945b1dc069 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 16 Apr 2026 11:32:33 +0900 Subject: [PATCH 1/7] Expand Chapter 10: semantic logging from stub to manual Replace the 63-line placeholder with a full chapter covering auto-recorded metamorphosis logs, the Been carrier, event contexts, produced JSON structure, and the bridge to LDD. Co-Authored-By: Claude Opus 4.6 --- manuals/1.0/ja/10-semantic-logging.md | 153 +++++++++++++++++++++----- 1 file changed, 124 insertions(+), 29 deletions(-) diff --git a/manuals/1.0/ja/10-semantic-logging.md b/manuals/1.0/ja/10-semantic-logging.md index e4a8508..0644420 100644 --- a/manuals/1.0/ja/10-semantic-logging.md +++ b/manuals/1.0/ja/10-semantic-logging.md @@ -1,7 +1,7 @@ --- layout: docs-ja title: "意味的ログ" -category: Draft +category: Manual permalink: /manuals/1.0/ja/10-semantic-logging.html --- @@ -13,51 +13,146 @@ permalink: /manuals/1.0/ja/10-semantic-logging.html ## 概要 -Be Frameworkは、オブジェクトの変容プロセスを構造化されたログとして自動記録する**意味的ログ**機能を実装しています。 +Be Frameworkでは、オブジェクトの変容が構造化ログとして自動記録されます。 -### 基本コンセプト +従来のログはイベントの断片的な記録です。`[INFO] User registered` と書かれたテキスト行からは、何がどう変わってその結果に至ったのかは読み取れません。意味的ログは変容の完全なストーリーを記録します。何が何になり、何を根拠にそうなったのか。 -**従来のログ**:イベントの断片的な記録 -**意味的ログ**:オブジェクトの完全な変容ストーリーの記録 +技術的基盤は[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)です。型安全な構造化ログ、Open/Event/Closeパターン、JSONスキーマ検証を提供します。 + +## 自動記録される変容ログ + +`Becoming`エンジンは変容のたびにログを自動出力します。開発者が記録コードを書く必要はありません。 ```php -// オブジェクトの変容が... #[Be(RegisteredUser::class)] -final readonly class UserInput { /* ... */ } +final readonly class UnverifiedEmail +{ + public function __construct( + public string $value, + ) {} +} +``` + +この`UnverifiedEmail`を`Becoming`に渡すだけで、以下の構造が記録されます。 + +- **open**: 変容の開始。どのクラスから何への変容か、引数の出自(`#[Input]`か`#[Inject]`か) +- **events**: 変容の途中で起きた出来事 +- **close**: 変容の完了。最終オブジェクトのプロパティと変容先 + +openとcloseはフレームワークが自動で書きます。eventsは開発者が書く。この分担が意味的ログの基本構造です。 -final readonly class RegisteredUser { /* ... */ } +## `$been` — 存在証明 -// 自動的に構造化ログとして記録される +Finalオブジェクトは「自分が何者であるか」を知っています。`$been`はその証拠です。 + +```php +final class RegisteredUser { - "metamorphosis": { - "from": "UserInput", - "to": "RegisteredUser", - // 完全な変容情報... - } + public readonly int $userId; + public readonly Been $been; + + public function __construct( + #[Input] string $value, + #[Inject] EmailVerifier $verifier, + #[Inject] UserRepository $users, + #[Inject] Been $been, + ) { + if (! $verifier->check($value)) { + throw new UnbecomingException('email format failed'); + } + + $this->userId = $users->insert(['email' => $value]); + $this->been = $been + ->with(new EmailFormatAssertedContext( + email: $value, + pattern: '/^[^@]+@[^@]+$/', + )) + ->with(new UserInsertedContext( + userId: $this->userId, + email: $value, + )); + } +} +``` + +`Been`は`#[Inject]`でDIコンテナから受け取ります。`with()`を呼ぶたびに新しい`Been`が返り、同時にSemanticLoggerのストリームにイベントが書き込まれます。不変コレクションでありながらライブ書き込みを行う。この二面性は意図的な設計です。 + +構築が完了したとき、`$been`はこのオブジェクトがなぜ今の状態にあるのかを型付きイベントの列として保持しています。外部のテストが検証するまでもなく、オブジェクト自身が自分の来歴を知っている。 + +## イベントコンテキスト + +`Been`に渡すイベントは`AbstractContext`のサブクラスです。 + +```php +final class EmailFormatAssertedContext extends AbstractContext +{ + public const string TYPE = 'email_format_asserted'; + public const string SCHEMA_URL = 'https://myvendor.example.com/schemas/email-format-asserted.json'; + + public function __construct( + public readonly string $email, + public readonly string $pattern, + ) {} } ``` -## 技術的基盤 +`TYPE`はログ上のイベント種別、`SCHEMA_URL`はそのイベントのJSONスキーマを指します。コンストラクタのプロパティがそのままJSONの`context`フィールドになります。 -[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)と統合: +フレームワークはドメインイベントを一切提供しません。何を「起きたこと」として記録するかはアプリケーションが決めます。 -- **型安全な構造化ログ** -- **Open/Event/Close パターン** -- **JSONスキーマ検証** -- **階層的操作追跡** +## 出力されるJSON -## 提供価値 +上記のコードを実行すると、以下のJSONが出力されます。 -### 開発・デバッグ -オブジェクト変容の完全な追跡により、複雑な処理フローの理解と問題特定が容易になります。 +```json +{ + "open": { + "type": "metamorphosis_open", + "context": { + "fromClass": "MyVendor\\MyApp\\UnverifiedEmail", + "beAttribute": "#[Be(RegisteredUser::class)]", + "immanentSources": { + "value": "MyVendor\\MyApp\\UnverifiedEmail::value" + }, + "transcendentSources": { + "verifier": "MyVendor\\MyApp\\EmailVerifier", + "users": "MyVendor\\MyApp\\UserRepository", + "been": "Be\\Framework\\SemanticLog\\Been" + } + } + }, + "events": [ + { + "type": "email_format_asserted", + "context": { "email": "alice@example.com", "pattern": "/^[^@]+@[^@]+$/" } + }, + { + "type": "user_inserted", + "context": { "userId": 42, "email": "alice@example.com" } + } + ], + "close": { + "type": "metamorphosis_close", + "context": { + "properties": { "userId": 42, "value": "alice@example.com" }, + "be": { "finalClass": "MyVendor\\MyApp\\RegisteredUser" } + } + } +} +``` -### 監査・コンプライアンス -すべての変容が構造化データとして記録されるため、完全な監査証跡を提供できます。 +openが「何から何へ、どの材料で」、eventsが「途中で何が起きたか」、closeが「結果として何を持っているか」。変容の全体がひとつのJSONに収まっています。 -### システム分析 -オブジェクトの成長パターンと処理効率の分析が可能になります。 +`$been`プロパティ自身はcloseの`properties`から自動的に除外されます。ログが自分自身を含めば無限再帰になる。 ---- +## ログ駆動開発への接続 + +このJSONを眺めると、ひとつのことに気づきます。`$been`が内部に持つイベント列と、SemanticLoggerが外部に出力するJSON — この二つは同じものの表と裏です。 -**詳細な使用方法、設定例、実践的なサンプルについては、ドキュメントを後日整備します。** +ということは、このJSONを先に手で書いて、そこからPHPクラスを生成することもできるのではないか。 + +その発想がログ駆動開発(LDD)です。詳しくは[ログ駆動開発](./13-vision-ldd.html)の章で扱います。 + +--- +フレームワークの全体像は[リファレンス](./11-reference-resources.html)へ ➡️ From 454f21bc0df565fd37972fb7371ab0d156a9f346 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 16 Apr 2026 17:22:50 +0900 Subject: [PATCH 2/7] Remove pattern field from code examples Align with framework PR #59 fix: email verification uses filter_var, not regex. EmailFormatAssertedContext carries only the email address. Co-Authored-By: Claude Opus 4.6 --- manuals/1.0/ja/10-semantic-logging.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/manuals/1.0/ja/10-semantic-logging.md b/manuals/1.0/ja/10-semantic-logging.md index 0644420..e7999a9 100644 --- a/manuals/1.0/ja/10-semantic-logging.md +++ b/manuals/1.0/ja/10-semantic-logging.md @@ -65,7 +65,6 @@ final class RegisteredUser $this->been = $been ->with(new EmailFormatAssertedContext( email: $value, - pattern: '/^[^@]+@[^@]+$/', )) ->with(new UserInsertedContext( userId: $this->userId, @@ -91,7 +90,6 @@ final class EmailFormatAssertedContext extends AbstractContext public function __construct( public readonly string $email, - public readonly string $pattern, ) {} } ``` @@ -124,7 +122,7 @@ final class EmailFormatAssertedContext extends AbstractContext "events": [ { "type": "email_format_asserted", - "context": { "email": "alice@example.com", "pattern": "/^[^@]+@[^@]+$/" } + "context": { "email": "alice@example.com" } }, { "type": "user_inserted", From f0d97b15d08d4904c87688d706c356a43502f731 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 16 Apr 2026 17:37:57 +0900 Subject: [PATCH 3/7] Rewrite Chapter 10: separate proof ($been) from log (SemanticLogger) Restructure around developer perspective: $been for existence proof first, SemanticLoggerInterface for hierarchical logging second, auto-recorded metamorphosis last. Co-Authored-By: Claude Opus 4.6 --- manuals/1.0/ja/10-semantic-logging.md | 82 ++++++++++++++++----------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/manuals/1.0/ja/10-semantic-logging.md b/manuals/1.0/ja/10-semantic-logging.md index e7999a9..0890b49 100644 --- a/manuals/1.0/ja/10-semantic-logging.md +++ b/manuals/1.0/ja/10-semantic-logging.md @@ -13,37 +13,24 @@ permalink: /manuals/1.0/ja/10-semantic-logging.html ## 概要 -Be Frameworkでは、オブジェクトの変容が構造化ログとして自動記録されます。 +従来のログは「何が起きたか」を記録します。 -従来のログはイベントの断片的な記録です。`[INFO] User registered` と書かれたテキスト行からは、何がどう変わってその結果に至ったのかは読み取れません。意味的ログは変容の完全なストーリーを記録します。何が何になり、何を根拠にそうなったのか。 - -技術的基盤は[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)です。型安全な構造化ログ、Open/Event/Closeパターン、JSONスキーマ検証を提供します。 - -## 自動記録される変容ログ - -`Becoming`エンジンは変容のたびにログを自動出力します。開発者が記録コードを書く必要はありません。 - -```php -#[Be(RegisteredUser::class)] -final readonly class UnverifiedEmail -{ - public function __construct( - public string $value, - ) {} -} +``` +[INFO] User registered: alice@example.com ``` -この`UnverifiedEmail`を`Becoming`に渡すだけで、以下の構造が記録されます。 +意味的ログは「なぜそうなったか」を記録します。メールの形式が検証され、ユーザーテーブルにIDが払い出され、その二つの事実を根拠にこのオブジェクトは`RegisteredUser`になった — その全体が構造化データとして残ります。 -- **open**: 変容の開始。どのクラスから何への変容か、引数の出自(`#[Input]`か`#[Inject]`か) -- **events**: 変容の途中で起きた出来事 -- **close**: 変容の完了。最終オブジェクトのプロパティと変容先 +Be Frameworkには二つの意味的記録の仕組みがあります。 -openとcloseはフレームワークが自動で書きます。eventsは開発者が書く。この分担が意味的ログの基本構造です。 +- **`$been`** — Finalオブジェクトが自分の来歴を保持する証明(proof) +- **`SemanticLoggerInterface`** — 階層的な操作を記録するログ(log) + +技術的基盤は[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)です。 ## `$been` — 存在証明 -Finalオブジェクトは「自分が何者であるか」を知っています。`$been`はその証拠です。 +`$been`はFinalオブジェクトの来歴を型付きイベントの列として保持します。 ```php final class RegisteredUser @@ -74,9 +61,9 @@ final class RegisteredUser } ``` -`Been`は`#[Inject]`でDIコンテナから受け取ります。`with()`を呼ぶたびに新しい`Been`が返り、同時にSemanticLoggerのストリームにイベントが書き込まれます。不変コレクションでありながらライブ書き込みを行う。この二面性は意図的な設計です。 +`Been`は`#[Inject]`でDIコンテナから受け取ります。`with()`を呼ぶたびに新しい`Been`が返り、同時にSemanticLoggerのストリームにもイベントが書き込まれます。 -構築が完了したとき、`$been`はこのオブジェクトがなぜ今の状態にあるのかを型付きイベントの列として保持しています。外部のテストが検証するまでもなく、オブジェクト自身が自分の来歴を知っている。 +`Been`は証明に徹します。階層構造やスコープの概念はありません。「何がこのオブジェクトを今の状態にしたか」をフラットなイベント列で保持する — それだけです。 ## イベントコンテキスト @@ -98,9 +85,40 @@ final class EmailFormatAssertedContext extends AbstractContext フレームワークはドメインイベントを一切提供しません。何を「起きたこと」として記録するかはアプリケーションが決めます。 -## 出力されるJSON +## 意味的ログ -上記のコードを実行すると、以下のJSONが出力されます。 +階層的な操作記録が必要な場合は、`SemanticLoggerInterface`を直接インジェクトします。 + +```php +final class RegisteredUser +{ + public function __construct( + #[Input] string $value, + #[Inject] UserRepository $users, + #[Inject] SemanticLoggerInterface $logger, + ) { + // 意図を宣言(open) + $id = $logger->open(new DbTransactionContext(table: 'users')); + + // 途中の出来事を記録(event) + $this->userId = $users->insert(['email' => $value]); + $logger->event(new RowInsertedContext(userId: $this->userId)); + + // 結果を記録(close) + $logger->close(new TransactionResultContext(committed: true), $id); + } +} +``` + +open/event/closeは[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)の階層構造をそのまま使います。意図(intent)→ 出来事(occurrences)→ 結果(result)の三層で、ひとまとまりの操作を記録できます。 + +`$been`が「このオブジェクトは何者か」の証明であるのに対し、`SemanticLoggerInterface`は「何がどう行われたか」の詳細な記録です。 + +## 変容の自動記録 + +`$been`や`SemanticLoggerInterface`とは別に、`Becoming`エンジンは変容そのものをopen/closeで自動記録します。開発者がこの記録コードを書く必要はありません。 + +出力されるJSONの全体像です。 ```json { @@ -139,17 +157,15 @@ final class EmailFormatAssertedContext extends AbstractContext } ``` -openが「何から何へ、どの材料で」、eventsが「途中で何が起きたか」、closeが「結果として何を持っているか」。変容の全体がひとつのJSONに収まっています。 +openが変容の意図(何から何へ、どの材料で)、eventsが`$been->with()`で記録された出来事、closeが結果(最終プロパティと変容先)です。 -`$been`プロパティ自身はcloseの`properties`から自動的に除外されます。ログが自分自身を含めば無限再帰になる。 +`$been`プロパティ自身はcloseの`properties`から自動的に除外されます。 ## ログ駆動開発への接続 -このJSONを眺めると、ひとつのことに気づきます。`$been`が内部に持つイベント列と、SemanticLoggerが外部に出力するJSON — この二つは同じものの表と裏です。 - -ということは、このJSONを先に手で書いて、そこからPHPクラスを生成することもできるのではないか。 +`$been`が内部に持つイベント列と、SemanticLoggerが外部に出力するJSON。この二つは同じものの表と裏です。 -その発想がログ駆動開発(LDD)です。詳しくは[ログ駆動開発](./13-vision-ldd.html)の章で扱います。 +ということは、このJSONを先に手で書いて、そこからPHPクラスを生成することもできる。その発想がログ駆動開発(LDD)です。詳しくは[ログ駆動開発](./13-vision-ldd.html)の章で扱います。 --- From 224e100c2ec0398fb781f7c113516edfba2ad99b Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 16 Apr 2026 19:43:11 +0900 Subject: [PATCH 4/7] Refine Chapter 10: add log vs $been comparison table, improve flow - Add descriptive/constitutive comparison table in overview - Rewrite LDD section as open possibility, remove link to Ch.13 - Remove $been exclusion implementation detail - Improve opening contrast with side-by-side output examples Co-Authored-By: Claude Opus 4.6 --- manuals/1.0/ja/10-semantic-logging.md | 46 ++++++++++++++++++++------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/manuals/1.0/ja/10-semantic-logging.md b/manuals/1.0/ja/10-semantic-logging.md index 0890b49..fa9de86 100644 --- a/manuals/1.0/ja/10-semantic-logging.md +++ b/manuals/1.0/ja/10-semantic-logging.md @@ -13,24 +13,48 @@ permalink: /manuals/1.0/ja/10-semantic-logging.html ## 概要 -従来のログは「何が起きたか」を記録します。 +従来のログ: ``` [INFO] User registered: alice@example.com +[INFO] Verification passed +[INFO] Insert into users table ``` -意味的ログは「なぜそうなったか」を記録します。メールの形式が検証され、ユーザーテーブルにIDが払い出され、その二つの事実を根拠にこのオブジェクトは`RegisteredUser`になった — その全体が構造化データとして残ります。 +意味的ログ: + +```json +{ + "open": { "from": "UnverifiedEmail", "to": "RegisteredUser" }, + "events": [ + { "type": "email_format_asserted", "context": { "email": "alice@example.com" } }, + { "type": "user_inserted", "context": { "userId": 42, "email": "alice@example.com" } } + ], + "close": { "properties": { "userId": 42, "value": "alice@example.com" } } +} +``` + +ひとつの変容がひとつのJSONに収まります。変容元と変容先、途中の出来事、最終プロパティ — すべてが型付きの構造化データとして記録され、JSONスキーマで検証できます。 Be Frameworkには二つの意味的記録の仕組みがあります。 - **`$been`** — Finalオブジェクトが自分の来歴を保持する証明(proof) - **`SemanticLoggerInterface`** — 階層的な操作を記録するログ(log) +| | log | `$been` | +|----------|------------------------------|----------------------------------| +| 性質 | descriptive(記述) | constitutive(構成) | +| 視点 | 第三者(観測者) | 一人称 | +| 問い | 何が起きたか | なぜ今の私なのか | +| 文法 | doing | being | +| 役割 | 記録 | 証明 | +| 除去可否 | できる(開発専用可) | できない(identityの一部) | + 技術的基盤は[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)です。 ## `$been` — 存在証明 -`$been`はFinalオブジェクトの来歴を型付きイベントの列として保持します。 +Finalオブジェクトのコンストラクタで`Been`をインジェクトし、`with()`で出来事を記録していくと、そのオブジェクトがなぜ今の状態にあるかの証明になります。 ```php final class RegisteredUser @@ -61,9 +85,7 @@ final class RegisteredUser } ``` -`Been`は`#[Inject]`でDIコンテナから受け取ります。`with()`を呼ぶたびに新しい`Been`が返り、同時にSemanticLoggerのストリームにもイベントが書き込まれます。 - -`Been`は証明に徹します。階層構造やスコープの概念はありません。「何がこのオブジェクトを今の状態にしたか」をフラットなイベント列で保持する — それだけです。 +`Been`は`#[Inject]`でDIコンテナから受け取ります。受け取った`Been`には変容の履歴がすでに記録されています。開発者は`with()`で、Finalオブジェクトの内部でしか知り得ない出来事 — メールを検証した、ユーザーを挿入した — を追記します。 ## イベントコンテキスト @@ -83,7 +105,7 @@ final class EmailFormatAssertedContext extends AbstractContext `TYPE`はログ上のイベント種別、`SCHEMA_URL`はそのイベントのJSONスキーマを指します。コンストラクタのプロパティがそのままJSONの`context`フィールドになります。 -フレームワークはドメインイベントを一切提供しません。何を「起きたこと」として記録するかはアプリケーションが決めます。 +DDDでいうドメインイベント — ビジネス上「起きたこと」を表すオブジェクトです。Finalオブジェクトが完了までに経験した事実を、アプリケーション固有のイベントコンテキストとして定義します。 ## 意味的ログ @@ -116,7 +138,7 @@ open/event/closeは[Koriym.SemanticLogger](https://github.com/koriym/Koriym.Sema ## 変容の自動記録 -`$been`や`SemanticLoggerInterface`とは別に、`Becoming`エンジンは変容そのものをopen/closeで自動記録します。開発者がこの記録コードを書く必要はありません。 +`$been`や`SemanticLoggerInterface`とは別に、変容そのものもフレームワークがopen/closeで自動記録します。開発者がこの記録コードを書く必要はありません。 出力されるJSONの全体像です。 @@ -159,13 +181,13 @@ open/event/closeは[Koriym.SemanticLogger](https://github.com/koriym/Koriym.Sema openが変容の意図(何から何へ、どの材料で)、eventsが`$been->with()`で記録された出来事、closeが結果(最終プロパティと変容先)です。 -`$been`プロパティ自身はcloseの`properties`から自動的に除外されます。 - ## ログ駆動開発への接続 -`$been`が内部に持つイベント列と、SemanticLoggerが外部に出力するJSON。この二つは同じものの表と裏です。 +従来のログは実行の記録です。コードが走った後に生まれ、デバッグに使われ、やがて消えます。 + +このJSONは違います。実行の記録であると同時に、変容の仕様でもあります。「`UnverifiedEmail`が`RegisteredUser`になる過程で`email_format_asserted`と`user_inserted`が起きる」— これは過去の事実の記述としても、未来の期待の宣言としても読めます。しかも型付きの構造化データなので、AIが読み書きできるDSLとしても機能します。 -ということは、このJSONを先に手で書いて、そこからPHPクラスを生成することもできる。その発想がログ駆動開発(LDD)です。詳しくは[ログ駆動開発](./13-vision-ldd.html)の章で扱います。 +記録、仕様、DSL。この三つが同じJSONに重なるとき、ログからコードを生成し、コードからログを生成する循環が可能になりえます。 --- From a1a16b4a653bde850d2927788fe73dd8d2bf051d Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 16 Apr 2026 21:10:42 +0900 Subject: [PATCH 5/7] Refine Chapter 10: clarify structured JSON vs line-based logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Explain traditional logs as disconnected text lines vs one JSON per transformation - Replace vague "すべて" with "何が何になり、なぜそうなったか" - Remove inaccurate "除去可否" row from comparison table - Add "存在の証明" to the DSL section, mention test-level schema validation - Rename section titles for clarity - Move technical reference link to footer Co-Authored-By: Claude Opus 4.6 --- manuals/1.0/ja/10-semantic-logging.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/manuals/1.0/ja/10-semantic-logging.md b/manuals/1.0/ja/10-semantic-logging.md index fa9de86..7f01f55 100644 --- a/manuals/1.0/ja/10-semantic-logging.md +++ b/manuals/1.0/ja/10-semantic-logging.md @@ -34,7 +34,9 @@ permalink: /manuals/1.0/ja/10-semantic-logging.html } ``` -ひとつの変容がひとつのJSONに収まります。変容元と変容先、途中の出来事、最終プロパティ — すべてが型付きの構造化データとして記録され、JSONスキーマで検証できます。 +従来のログは行単位のテキストが時系列に並ぶだけで、どの行が同じ操作に属するかは読み手の推測に委ねられます。 + +意味的ログでは、ひとつの変容がひとつのJSONに収まります。変容元と変容先、途中の出来事、最終プロパティ — 「何が何になり、なぜそうなったか」が型付きの構造化データとして記録され、JSONスキーマで検証できます。 Be Frameworkには二つの意味的記録の仕組みがあります。 @@ -48,9 +50,6 @@ Be Frameworkには二つの意味的記録の仕組みがあります。 | 問い | 何が起きたか | なぜ今の私なのか | | 文法 | doing | being | | 役割 | 記録 | 証明 | -| 除去可否 | できる(開発専用可) | できない(identityの一部) | - -技術的基盤は[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)です。 ## `$been` — 存在証明 @@ -107,7 +106,7 @@ final class EmailFormatAssertedContext extends AbstractContext DDDでいうドメインイベント — ビジネス上「起きたこと」を表すオブジェクトです。Finalオブジェクトが完了までに経験した事実を、アプリケーション固有のイベントコンテキストとして定義します。 -## 意味的ログ +## SemanticLoggerInterface — 階層的な操作記録 階層的な操作記録が必要な場合は、`SemanticLoggerInterface`を直接インジェクトします。 @@ -181,14 +180,16 @@ open/event/closeは[Koriym.SemanticLogger](https://github.com/koriym/Koriym.Sema openが変容の意図(何から何へ、どの材料で)、eventsが`$been->with()`で記録された出来事、closeが結果(最終プロパティと変容先)です。 -## ログ駆動開発への接続 +## ログからDSLへ 従来のログは実行の記録です。コードが走った後に生まれ、デバッグに使われ、やがて消えます。 -このJSONは違います。実行の記録であると同時に、変容の仕様でもあります。「`UnverifiedEmail`が`RegisteredUser`になる過程で`email_format_asserted`と`user_inserted`が起きる」— これは過去の事実の記述としても、未来の期待の宣言としても読めます。しかも型付きの構造化データなので、AIが読み書きできるDSLとしても機能します。 +このJSONは違います。実行の記録であると同時に、変容の仕様でもあり、存在の証明でもあります。どこから来て、どう成ったのか、何であるのかの記録です。「`UnverifiedEmail`が`RegisteredUser`になる過程で`email_format_asserted`と`user_inserted`が起きる」— これは過去の事実の記述としても、未来の期待の宣言としても読めます。しかも型付きの構造化データなので、AIが読み書きできるDSLとしても機能します。 -記録、仕様、DSL。この三つが同じJSONに重なるとき、ログからコードを生成し、コードからログを生成する循環が可能になりえます。 +記録、仕様、証明、DSL。この四つが同じJSONに重なるとき、JSONスキーマによるテスト並みの厳密な検証と、ログからコードを生成しコードからログを生成する循環が可能になりえます。 --- +技術的基盤: [Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger) + フレームワークの全体像は[リファレンス](./11-reference-resources.html)へ ➡️ From a7ab542b269722fa5fe3659c58227b7be644b419 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 16 Apr 2026 21:21:42 +0900 Subject: [PATCH 6/7] Clarify Been injection context and $been vs Logger usage - Explain what Been already contains when injected (from/to recorded at metamorphosis start) - Clarify that $been is proof of identity, Logger is for intermediate steps like traditional logs - Note that $been is usually sufficient Co-Authored-By: Claude Opus 4.6 --- manuals/1.0/ja/10-semantic-logging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manuals/1.0/ja/10-semantic-logging.md b/manuals/1.0/ja/10-semantic-logging.md index 7f01f55..3f6a826 100644 --- a/manuals/1.0/ja/10-semantic-logging.md +++ b/manuals/1.0/ja/10-semantic-logging.md @@ -84,7 +84,7 @@ final class RegisteredUser } ``` -`Been`は`#[Inject]`でDIコンテナから受け取ります。受け取った`Been`には変容の履歴がすでに記録されています。開発者は`with()`で、Finalオブジェクトの内部でしか知り得ない出来事 — メールを検証した、ユーザーを挿入した — を追記します。 +`Been`は`#[Inject]`でDIコンテナから受け取ります。受け取った`Been`にはフレームワークが変容開始時に記録した変容元・変容先の情報がすでに含まれています。開発者は`with()`で、Finalオブジェクトの内部でしか知り得ない出来事 — メールを検証した、ユーザーを挿入した — を追記します。 ## イベントコンテキスト @@ -133,7 +133,7 @@ final class RegisteredUser open/event/closeは[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)の階層構造をそのまま使います。意図(intent)→ 出来事(occurrences)→ 結果(result)の三層で、ひとまとまりの操作を記録できます。 -`$been`が「このオブジェクトは何者か」の証明であるのに対し、`SemanticLoggerInterface`は「何がどう行われたか」の詳細な記録です。 +`$been`はFinalオブジェクトが何であるかの証明です。`SemanticLoggerInterface`は従来のログに近い、途中経過の詳細な記録です。通常は`$been`で足ります。 ## 変容の自動記録 From f2a7e865944a046d57fb4d803e8f26a44fdc46aa Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 16 Apr 2026 21:35:31 +0900 Subject: [PATCH 7/7] Translate Chapter 10 (Semantic Logging) to English Replace draft placeholder with full translation matching the Japanese version. Co-Authored-By: Claude Opus 4.6 --- manuals/1.0/en/10-semantic-logging.md | 193 ++++++++++++++++++++++---- 1 file changed, 163 insertions(+), 30 deletions(-) diff --git a/manuals/1.0/en/10-semantic-logging.md b/manuals/1.0/en/10-semantic-logging.md index 1801c49..f8f9993 100644 --- a/manuals/1.0/en/10-semantic-logging.md +++ b/manuals/1.0/en/10-semantic-logging.md @@ -1,7 +1,7 @@ --- layout: docs-en title: "Semantic Logging" -category: Draft +category: Manual permalink: /manuals/1.0/en/10-semantic-logging.html --- @@ -9,54 +9,187 @@ permalink: /manuals/1.0/en/10-semantic-logging.html > "What we record becomes memory; what we remember becomes truth" > -> —Adaptation of Orwell's concept from '1984' (1949) +> —Adapted from Orwell's concept in *1984* (1949) ## Overview -Be Framework implements **semantic logging** functionality that automatically records object metamorphosis processes as structured logs. +Traditional logs: -### Basic Concept +``` +[INFO] User registered: alice@example.com +[INFO] Verification passed +[INFO] Insert into users table +``` + +Semantic logs: + +```json +{ + "open": { "from": "UnverifiedEmail", "to": "RegisteredUser" }, + "events": [ + { "type": "email_format_asserted", "context": { "email": "alice@example.com" } }, + { "type": "user_inserted", "context": { "userId": 42, "email": "alice@example.com" } } + ], + "close": { "properties": { "userId": 42, "value": "alice@example.com" } } +} +``` + +Traditional logs are just lines of text arranged chronologically — the reader is left to guess which lines belong to the same operation. + +With semantic logging, one metamorphosis fits in one JSON. Source and destination, intermediate events, final properties — "what became what, and why" is recorded as typed, structured data and can be validated with JSON Schema. + +Be Framework provides two semantic recording mechanisms: -**Traditional Logs**: Fragmented event records -**Semantic Logs**: Complete metamorphosis story records of objects +- **`$been`** — Proof that the Final object carries its own history (proof) +- **`SemanticLoggerInterface`** — A log that records hierarchical operations (log) + +| | log | `$been` | +|--------------|------------------------|-----------------------------| +| Nature | descriptive | constitutive | +| Perspective | third-person (observer)| first-person | +| Question | What happened? | Why am I what I am now? | +| Grammar | doing | being | +| Role | record | proof | + +## `$been` — Proof of Existence + +Inject `Been` into the Final object's constructor and record events with `with()`. This becomes proof of why the object is in its current state. ```php -// Object metamorphosis... -#[Be(RegisteredUser::class)] -final readonly class UserInput { /* ... */ } +final class RegisteredUser +{ + public readonly int $userId; + public readonly Been $been; + + public function __construct( + #[Input] string $value, + #[Inject] EmailVerifier $verifier, + #[Inject] UserRepository $users, + #[Inject] Been $been, + ) { + if (! $verifier->check($value)) { + throw new UnbecomingException('email format failed'); + } + + $this->userId = $users->insert(['email' => $value]); + $this->been = $been + ->with(new EmailFormatAssertedContext( + email: $value, + )) + ->with(new UserInsertedContext( + userId: $this->userId, + email: $value, + )); + } +} +``` + +`Been` is received from the DI container via `#[Inject]`. The received `Been` already contains the source and destination information recorded by the framework at the start of metamorphosis. The developer appends events that only the Final object's internals can know — email was verified, user was inserted — using `with()`. + +## Event Context -final readonly class RegisteredUser { /* ... */ } +Events passed to `Been` are subclasses of `AbstractContext`. -// Automatically recorded as structured logs +```php +final class EmailFormatAssertedContext extends AbstractContext { - "metamorphosis": { - "from": "UserInput", - "to": "RegisteredUser", - // Complete metamorphosis information... - } + public const string TYPE = 'email_format_asserted'; + public const string SCHEMA_URL = 'https://myvendor.example.com/schemas/email-format-asserted.json'; + + public function __construct( + public readonly string $email, + ) {} } ``` -## Technical Foundation +`TYPE` is the event type in the log, and `SCHEMA_URL` points to the JSON Schema for that event. Constructor properties become the JSON `context` field as-is. + +These are domain events in DDD terms — objects representing "what happened" in business terms. Define the facts that the Final object experienced on its way to completion as application-specific event contexts. + +## SemanticLogger — Hierarchical Operation Recording -Integrated with [Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger): +When hierarchical operation recording is needed, inject `SemanticLoggerInterface` directly. + +```php +final class RegisteredUser +{ + public function __construct( + #[Input] string $value, + #[Inject] UserRepository $users, + #[Inject] SemanticLoggerInterface $logger, + ) { + // Declare intent (open) + $id = $logger->open(new DbTransactionContext(table: 'users')); + + // Record intermediate events (event) + $this->userId = $users->insert(['email' => $value]); + $logger->event(new RowInsertedContext(userId: $this->userId)); + + // Record result (close) + $logger->close(new TransactionResultContext(committed: true), $id); + } +} +``` -- **Type-safe structured logging** -- **Open/Event/Close pattern** -- **JSON schema validation** -- **Hierarchical operation tracking** +open/event/close uses the hierarchical structure of [Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger) directly. Intent → occurrences → result — these three layers record a cohesive operation. -## Value Provided +`$been` is proof of what the Final object is. `SemanticLoggerInterface` is a detailed record of intermediate steps, closer to traditional logging. Usually `$been` is sufficient. -### Development & Debugging -Complete tracking of object metamorphosis makes it easy to understand complex processing flows and identify problems. +## Automatic Metamorphosis Recording -### Audit & Compliance -Since all metamorphoses are recorded as structured data, complete audit trails can be provided. +Separate from `$been` and `SemanticLoggerInterface`, the framework automatically records the metamorphosis itself with open/close. Developers do not need to write this recording code. -### System Analysis -Analysis of object growth patterns and processing efficiency becomes possible. +Here is the full JSON output: + +```json +{ + "open": { + "type": "metamorphosis_open", + "context": { + "fromClass": "MyVendor\\MyApp\\UnverifiedEmail", + "beAttribute": "#[Be(RegisteredUser::class)]", + "immanentSources": { + "value": "MyVendor\\MyApp\\UnverifiedEmail::value" + }, + "transcendentSources": { + "verifier": "MyVendor\\MyApp\\EmailVerifier", + "users": "MyVendor\\MyApp\\UserRepository", + "been": "Be\\Framework\\SemanticLog\\Been" + } + } + }, + "events": [ + { + "type": "email_format_asserted", + "context": { "email": "alice@example.com" } + }, + { + "type": "user_inserted", + "context": { "userId": 42, "email": "alice@example.com" } + } + ], + "close": { + "type": "metamorphosis_close", + "context": { + "properties": { "userId": 42, "value": "alice@example.com" }, + "be": { "finalClass": "MyVendor\\MyApp\\RegisteredUser" } + } + } +} +``` + +open records the intent of metamorphosis (what to what, with which materials), events record the occurrences from `$been->with()`, and close records the result (final properties and destination). + +## From Logs to DSL + +Traditional logs are records of execution. They are born after code runs, used for debugging, and eventually discarded. + +This JSON is different. It is simultaneously a record of execution, a specification of metamorphosis, and a proof of existence. Where it came from, what it became, what it is. "`UnverifiedEmail` becomes `RegisteredUser` through `email_format_asserted` and `user_inserted`" — this reads as both a description of past fact and a declaration of future expectation. Moreover, as typed structured data, it functions as a DSL that AI can read and write. + +Record, specification, proof, DSL. When these four converge in the same JSON, rigorous validation on par with tests through JSON Schema, and a cycle of generating code from logs and logs from code, become possible. --- -**Detailed usage methods, configuration examples, and practical samples will be documented at a later date.** \ No newline at end of file +Technical foundation: [Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger) + +For the full framework overview, see [Reference](./11-reference-resources.html) ➡️