diff --git a/manuals/1.0/en/11-reference-resources.md b/manuals/1.0/en/11-reference-resources.md index 327aec3..b1ea020 100644 --- a/manuals/1.0/en/11-reference-resources.md +++ b/manuals/1.0/en/11-reference-resources.md @@ -20,4 +20,5 @@ permalink: /manuals/1.0/en/11-reference-resources.html ## Development Reference - [Naming Standards](./convention/naming-standards.html) — Being-oriented naming principles +- [Directory Layout](./convention/directory-layout.html) — Canonical `src//` slots and what each is for - [Philosophy Behind](./12-philosophy-behind.html) — Philosophical roots of ontological programming diff --git a/manuals/1.0/en/convention/directory-layout.md b/manuals/1.0/en/convention/directory-layout.md new file mode 100644 index 0000000..7ac9821 --- /dev/null +++ b/manuals/1.0/en/convention/directory-layout.md @@ -0,0 +1,225 @@ +--- +layout: docs-en +title: "Be Framework Directory Layout" +category: Convention +permalink: /manuals/1.0/en/convention/directory-layout.html +--- + +# Be Framework Directory Layout + +> "The facts in logical space are the world." +> +> —Ludwig Wittgenstein (*Tractatus Logico-Philosophicus*, 1.13, 1921) + +## Source map + +| dir | role | manual | +|---|---|---| +| `src/Input/` | Pipeline entry. Declares `#[Be(...)]`. | [Input Classes](../02-input-classes.html) | +| `src/Final/` | Terminus. `#[Input]` data + `#[Inject]` services. | [Final Objects](../04-final-objects.html) | +| `src/Semantic/` | Semantic variables. Class name = parameter name. | [Semantic Variables](../06-semantic-variables.html) | +| `src/Exception/` | Semantic-validation exceptions with `#[Message]` for i18n. | [Error Handling](../09-error-handling.html) | +| `src/Reason/` | Raison d'être — capabilities an existence requires, gathered into one. | [Reason Layer](../08-reason-layer.html) | +| `src/Module/` | Ray.Di modules. `MODULE=` env switches the active module. | [Ray.Di Manual](https://ray-di.github.io/manuals/1.0/en/index.html) | +| `src/Becoming/` | Framework wiring — `BecomingInterface` implementations/decorators. | [Becoming](../04a-becoming.html) | +| `src/Being/` | Branching — `$being` discriminator + `#[Be([A, B])]`. | [Being Classes](../03-being-classes.html) | +| `src/LogContext/` | Semantic-log event classes attached to `Been`. | [Semantic Logging](../10-semantic-logging.html) | +| `src/Moment/` | Moment — holds a Potential from Reason, realized via `be()`. | [Metamorphosis Patterns](../05-metamorphosis-patterns.html) | + +## `src/Input/` + +```php +#[Be(HelloFinal::class)] +final readonly class HelloInput +{ + public function __construct( + #[Input] public string $name, + ) {} +} +``` + +An Input is the first domain class, built from data handed in from outside. `#[Be(...)]` declares what it becomes next — one candidate, or several. + +→ [Input Classes](../02-input-classes.html) + +## `src/Final/` + +```php +final readonly class HelloFinal +{ + public string $message; + + public function __construct( + #[Input] string $name, + #[Inject] Greeting $greeting, + ) { + $this->message = $greeting->say($name); + } +} +``` + +The terminus of metamorphosis. No `#[Be(...)]`. `#[Input]` is immanence (what came from the previous form); `#[Inject]` is transcendence (services from outside). Evidence of completion is recorded via `#[Inject] Been`. + +→ [Final Objects](../04-final-objects.html) + +## `src/Semantic/` + +```php +final class Email +{ + #[Validate] + public function validate(string $email): void + { + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + throw new InvalidEmailException(); + } + } +} +``` + +The class name becomes the parameter name. `#[Validate]` auto-applies to every argument named `$email`, anywhere in the app — define once, enforced everywhere. + +→ [Semantic Variables](../06-semantic-variables.html) + +## `src/Exception/` + +```php +#[Message( + en: 'Invalid email: {email}', + ja: '不正なメールアドレス: {email}', +)] +final class InvalidEmailException extends \DomainException {} +``` + +`#[Message]` `en`/`ja` declare the per-language message. Placeholders like `{email}` are assigned from the exception's properties at throw time. + +→ [Error Handling](../09-error-handling.html) + +## `src/Reason/` + +```php +final readonly class ExpressShipping +{ + public function __construct( + private PriorityCarrier $carrier, + private RealTimeTracker $tracker, + ) {} + + public function calculateFee(Weight $weight): Fee + { + return $this->carrier->expressFee($weight); + } +} +``` + +"What does it take to exist as `ExpressDelivery`?" — `ExpressShipping` is the answer. It gathers the capabilities that existence requires into one class, usable as `#[Inject]` to provide capabilities, or as the type of `$being` to decide the next form. + +→ [Reason Layer](../08-reason-layer.html) + +## `src/Module/` + +```php +final class AppModule extends AbstractModule +{ + protected function configure(): void + { + $this->bind(PriorityCarrier::class)->to(FedExPriority::class); + $this->bind(RealTimeTracker::class)->to(FedExTracker::class); + } +} +``` + +DI bindings swapped via an env var like `MODULE=Dev`. A `DevModule` or `TestModule` substitutes implementations without touching the production `AppModule`. + +→ [Ray.Di Manual](https://ray-di.github.io/manuals/1.0/en/index.html) + +## `src/Becoming/` + +```php +final readonly class LoggingBecoming implements BecomingInterface +{ + public function __construct( + private Becoming $inner, + private LoggerInterface $logger, + ) {} + + public function __invoke(object $input): object + { + $this->logger->info('becoming', ['input' => $input::class]); + return ($this->inner)($input); + } +} +``` + +Touched rarely — only when you need to instrument metamorphosis itself (logging, tracing, timing). + +→ [Becoming](../04a-becoming.html) + +## `src/Being/` + +```php +#[Be([Approved::class, Rejected::class])] +final readonly class ApplicationReview +{ + public Approved|Rejected $being; + + public function __construct( + #[Input] LoanApplication $app, + #[Inject] CreditCheck $check, + ) { + $this->being = $check->evaluate($app); + } +} +``` + +Which class comes next is declared by the union-typed `$being` property. In practice, the framework picks the `#[Be([...])]` candidate whose constructor arguments can be satisfied. + +→ [Being Classes](../03-being-classes.html) + +## `src/LogContext/` + +```php +final class EmailFormatAssertedContext extends AbstractContext +{ + public const string TYPE = 'email_format_asserted'; + public const string SCHEMA_URL = '../schemas/email-format-asserted.json'; + + public function __construct( + public readonly string $email, + ) {} +} +``` + +`TYPE` is the event name that lands in the log; `SCHEMA_URL` links to the schema. Inside a Final, `$been->with(new EmailFormatAssertedContext(...))` records the evidence that the object was established. + +→ [Semantic Logging](../10-semantic-logging.html) + +## `src/Moment/` + +```php +final readonly class PaymentCompleted implements MomentInterface +{ + public PaymentCapture $capture; + + public function __construct( + #[Input] string $cardNumber, + #[Input] int $amount, + #[Inject] PaymentGateway $gateway, + ) { + $this->capture = $gateway->authorize($cardNumber, $amount); + } + + public function be(): void + { + $this->capture->be(); + } +} +``` + +The constructor completes `authorize()` (the Potential), but not `capture()`. Only a Final that could construct *every* Moment then calls `be()` on each — so partial commits never happen. + +→ [Metamorphosis Patterns](../05-metamorphosis-patterns.html) + +--- + +Three directories — `Being/`, `LogContext/`, `Moment/` — ship empty. Add classes as the project needs them. diff --git a/manuals/1.0/ja/11-reference-resources.md b/manuals/1.0/ja/11-reference-resources.md index bc80862..9524374 100644 --- a/manuals/1.0/ja/11-reference-resources.md +++ b/manuals/1.0/ja/11-reference-resources.md @@ -20,4 +20,5 @@ permalink: /manuals/1.0/ja/11-reference-resources.html ## 開発リファレンス - [命名規約](./convention/naming-standards.html) — 存在指向の命名原則 +- [ディレクトリ構成](./convention/directory-layout.html) — `src//` 各スロットの役割と置くもの - [背後にある哲学](./12-philosophy-behind.html) — 存在論的プログラミングの思想的ルーツ diff --git a/manuals/1.0/ja/convention/directory-layout.md b/manuals/1.0/ja/convention/directory-layout.md new file mode 100644 index 0000000..7876126 --- /dev/null +++ b/manuals/1.0/ja/convention/directory-layout.md @@ -0,0 +1,225 @@ +--- +layout: docs-ja +title: "Be Framework ディレクトリ構成" +category: Convention +permalink: /manuals/1.0/ja/convention/directory-layout.html +--- + +# ディレクトリ構成 + +> 「論理的空間の中にある諸事実が、世界である」 +> +>   —ルートヴィヒ・ウィトゲンシュタイン(『論理哲学論考』1.13, 1921年) + +## 全体マップ + +| dir | 役割 | マニュアル | +|---|---|---| +| `src/Input/` | パイプラインの起点。`#[Be(...)]` を宣言。 | [Input クラス](../02-input-classes.html) | +| `src/Final/` | 終点。`#[Input]` データと `#[Inject]` サービスを受ける。 | [Final オブジェクト](../04-final-objects.html) | +| `src/Semantic/` | セマンティック変数。クラス名 = パラメータ名。 | [セマンティック変数](../06-semantic-variables.html) | +| `src/Exception/` | セマンティック検証例外。`#[Message]` で多言語化。 | [エラーハンドリング](../09-error-handling.html) | +| `src/Reason/` | 「存在理由」 — その存在に必要な能力を1つに集めたもの。 | [Reason レイヤー](../08-reason-layer.html) | +| `src/Module/` | Ray.Di モジュール。`MODULE=` 環境変数で有効モジュールを切り替える。 | [Ray.Di マニュアル](https://ray-di.github.io/manuals/1.0/ja/index.html) | +| `src/Becoming/` | フレームワーク配線層 — `BecomingInterface` の実装やデコレータ。 | [Becoming](../04a-becoming.html) | +| `src/Being/` | 分岐 — `$being` 判別子 + `#[Be([A, B])]`。 | [Being クラス](../03-being-classes.html) | +| `src/LogContext/` | `Been` に添えるセマンティックログのイベントクラス。 | [セマンティックロギング](../10-semantic-logging.html) | +| `src/Moment/` | Moment — Reason が返した Potential を保持し、`be()` で実現する。 | [メタモルフォーシスパターン](../05-metamorphosis-patterns.html) | + +## `src/Input/` + +```php +#[Be(HelloFinal::class)] +final readonly class HelloInput +{ + public function __construct( + #[Input] public string $name, + ) {} +} +``` + +Input は外部から渡されるデータで作られる最初のドメインクラスです。`#[Be(...)]` 属性で「次に何になるか」を1つ、または複数持ちます。 + +→ [Input クラス](../02-input-classes.html) + +## `src/Final/` + +```php +final readonly class HelloFinal +{ + public string $message; + + public function __construct( + #[Input] string $name, + #[Inject] Greeting $greeting, + ) { + $this->message = $greeting->say($name); + } +} +``` + +変容の終点のクラスです。`#[Be(...)]` は持ちません。`#[Input]` は内在(前段から渡ってきたもの)、`#[Inject]` は超越(外から与えられるサービス)です。完了の証拠は `#[Inject] Been` で記されます。 + +→ [Final オブジェクト](../04-final-objects.html) + +## `src/Semantic/` + +```php +final class Email +{ + #[Validate] + public function validate(string $email): void + { + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + throw new InvalidEmailException(); + } + } +} +``` + +クラス名がそのままパラメータ名になります。`#[Validate]` は `$email` という名の引数すべてに自動で効きます — 一度定義すれば、アプリ全域に適用されます。 + +→ [セマンティック変数](../06-semantic-variables.html) + +## `src/Exception/` + +```php +#[Message( + en: 'Invalid email: {email}', + ja: '不正なメールアドレス: {email}', +)] +final class InvalidEmailException extends \DomainException {} +``` + +`#[Message]` の `en`/`ja` で、言語ごとのメッセージを指定します。`{email}` のようなプレースホルダは、throw 時に例外のプロパティからアサインされます。 + +→ [エラーハンドリング](../09-error-handling.html) + +## `src/Reason/` + +```php +final readonly class ExpressShipping +{ + public function __construct( + private PriorityCarrier $carrier, + private RealTimeTracker $tracker, + ) {} + + public function calculateFee(Weight $weight): Fee + { + return $this->carrier->expressFee($weight); + } +} +``` + +「`ExpressDelivery` として存在するには何が必要か?」 — その答えが `ExpressShipping` です。その存在に必要な能力を1つに集めたクラスで、`#[Inject]` で能力として注入することも、`$being` の型として次段を決めることもできます。 + +→ [Reason レイヤー](../08-reason-layer.html) + +## `src/Module/` + +```php +final class AppModule extends AbstractModule +{ + protected function configure(): void + { + $this->bind(PriorityCarrier::class)->to(FedExPriority::class); + $this->bind(RealTimeTracker::class)->to(FedExTracker::class); + } +} +``` + +`MODULE=Dev` のような環境変数で切り替えるDIの束縛設定です。本番用の `AppModule` を変えずに、`DevModule` や `TestModule` で実装を差し替えられます。 + +→ [Ray.Di マニュアル](https://ray-di.github.io/manuals/1.0/ja/index.html) + +## `src/Becoming/` + +```php +final readonly class LoggingBecoming implements BecomingInterface +{ + public function __construct( + private Becoming $inner, + private LoggerInterface $logger, + ) {} + + public function __invoke(object $input): object + { + $this->logger->info('becoming', ['input' => $input::class]); + return ($this->inner)($input); + } +} +``` + +普段は触りません。メタモルフォーシスの実行そのものに手を入れたいとき(ログ・トレース・計測)だけ使います。 + +→ [Becoming](../04a-becoming.html) + +## `src/Being/` + +```php +#[Be([Approved::class, Rejected::class])] +final readonly class ApplicationReview +{ + public Approved|Rejected $being; + + public function __construct( + #[Input] LoanApplication $app, + #[Inject] CreditCheck $check, + ) { + $this->being = $check->evaluate($app); + } +} +``` + +次にどのクラスに分岐するかは、ユニオン型の `$being` プロパティで明示的に示します。実際には `#[Be([...])]` の候補のうち、引数が用意できるものが次のクラスとして選ばれます。 + +→ [Being クラス](../03-being-classes.html) + +## `src/LogContext/` + +```php +final class EmailFormatAssertedContext extends AbstractContext +{ + public const string TYPE = 'email_format_asserted'; + public const string SCHEMA_URL = '../schemas/email-format-asserted.json'; + + public function __construct( + public readonly string $email, + ) {} +} +``` + +`TYPE` はログに出るイベント名、`SCHEMA_URL` はスキーマへのリンクです。Final 内で `$been->with(new EmailFormatAssertedContext(...))` として、そのオブジェクトが成立したことの証拠を記録します。 + +→ [セマンティックロギング](../10-semantic-logging.html) + +## `src/Moment/` + +```php +final readonly class PaymentCompleted implements MomentInterface +{ + public PaymentCapture $capture; + + public function __construct( + #[Input] string $cardNumber, + #[Input] int $amount, + #[Inject] PaymentGateway $gateway, + ) { + $this->capture = $gateway->authorize($cardNumber, $amount); + } + + public function be(): void + { + $this->capture->be(); + } +} +``` + +コンストラクタで `authorize()` は済みます(Potential)が、`capture()` はまだです。全 Moment を生成できた Final だけが `be()` を一斉に呼びます — 部分的なコミットは起こりません。 + +→ [メタモルフォーシスパターン](../05-metamorphosis-patterns.html) + +--- + +3つのディレクトリ — `Being/`、`LogContext/`、`Moment/` — はデフォルトでは空です。必要に応じて、クラスを追加していきます。 \ No newline at end of file