Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions manuals/1.0/en/01-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,12 @@ You can acquire the following new programming methods:
3. Express natural transformation (self-metamorphosis) instead of forcing objects to change
4. Trust the correct state instead of preventing errors

## So Why "DeletedUser"?

Now back to the opening question. `new DeletedUser($activeUser)` is not an action—it is a transformation. The user is not "being deleted"; a new existence called `DeletedUser` is born from `$activeUser`. The type itself proves that deletion has occurred. There is no need to check a `$status` flag, no risk of calling methods on an already-deleted user. This is the essence of Be Framework: **express state transitions as new types, not as actions on existing objects.**

## Let's Get Started

Let's learn from the foundation. Everything starts from [Input Classes]({{ '/manuals/1.0/en/02-input-classes.html' | relative_url }}) →
**Want to try it first?** Jump to the [Getting Started]({{ '/manuals/1.0/en/getting-started.html' | relative_url }}) guide for a hands-on Hello World, or the [Tutorial]({{ '/manuals/1.0/en/tutorial.html' | relative_url }}) for a real-world example.

Create your first object and experience why it is "DeletedUser".
**Want to understand the concepts?** Continue to [Input Classes]({{ '/manuals/1.0/en/02-input-classes.html' | relative_url }}) to learn the foundations step by step →
4 changes: 3 additions & 1 deletion manuals/1.0/en/03-being-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ final readonly class ValidatedUser
}
```

`#[Input]` parameters automatically receive values from the previous class's public properties by matching names. `UserInput`'s `public string $name` maps to `ValidatedUser`'s `#[Input] string $name`. `#[Inject]` parameters receive external dependencies from the DI container. The detailed rules of this automatic matching are explained in [Chapter 5: Metamorphosis](./05-metamorphosis-patterns.html).

## Objects as Temporal Beings

In Be Framework, objects are not treated as static data structures, but as temporal beings that exist only within a specific moment in time.
Expand All @@ -51,7 +53,7 @@ Immanence meets Transcendence, the logic of transformation takes effect, and new

### Life (Being)

The object exposes its "form as it should be" to the world as `public readonly` properties. But no one touches those properties—the object vanishes shortly after birth, making way for the next.
The object exposes its "form as it should be" to the world as `public readonly` properties. The framework reads these properties and passes them as `#[Input]` to the next class in the chain. The object then vanishes, making way for the next.

### Becoming the Self You Want to Be

Expand Down
10 changes: 6 additions & 4 deletions manuals/1.0/en/04-final-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ final readonly class SuccessfulOrder
public string $confirmationCode;
public DateTimeImmutable $timestamp;
public string $message;
public BeenProcessed $been; // Evidence of completion
public BeenConfirmed $been; // Evidence of completion

public function __construct(
#[Input] Money $total, // Immanence
Expand All @@ -37,7 +37,7 @@ final readonly class SuccessfulOrder
$this->timestamp = new DateTimeImmutable();
$this->message = "Order Confirmed: {$this->orderId}";

$this->been = new BeenProcessed(
$this->been = new BeenConfirmed(
actor: $card->getHolderName(),
timestamp: $this->timestamp,
evidence: [
Expand All @@ -50,7 +50,9 @@ final readonly class SuccessfulOrder
}
```

In contrast to Input Classes, Final Objects fully express the richness of the domain. Immanence has met Transcendence, undergone transformation, and reached a complete state that requires no further change. Success and failure share the same structure. A `FailedOrder` also has a `$been`—`BeenRejected`.
The `$been` property uses an application-defined, domain-specific type. `SuccessfulOrder` has `BeenConfirmed`, `FailedOrder` has `BeenRejected`, a `DeletedUser` might have `BeenDeleted`. What counts as "evidence of completion" depends on the domain—you design the class to capture whatever your domain requires as proof.

In contrast to Input Classes, Final Objects fully express the richness of the domain. Immanence has met Transcendence, undergone transformation, and reached a complete state that requires no further change.

## Completeness of Temporal Being

Expand All @@ -75,7 +77,7 @@ In traditional programming, external tests judge whether an object has been proc

## Multiple Final Destinies

Objects can have multiple possible final forms determined by their nature:
Objects can have multiple possible final forms determined by their nature. Here `$becoming` triggers the metamorphosis chain—this mechanism is explained in the [next chapter](./04a-becoming.html):

```php
$order = $becoming(new OrderInput($items, $card));
Expand Down
25 changes: 14 additions & 11 deletions manuals/1.0/en/05-metamorphosis-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,39 +43,42 @@ Each moment never returns, and new existence preserves previous forms as memory
Like living beings in reality, objects determine their own destiny through the interaction between immanence and transcendence. This is not following a predetermined route, but natural metamorphosis responding to the circumstances of that moment:

```php
#[Be([ApprovedApplication::class, RejectedApplication::class])]
#[Be([ApprovalNotification::class, RejectionNotification::class])]
final readonly class ApplicationReview
{
public ApprovedApplication|RejectedApplication $being;
public Approved|Rejected $being;

public function __construct(
#[Input] array $documents, // Immanence
#[Inject] ReviewService $reviewer // Transcendence
#[Input] string $email, // Immanence
#[Input] array $documents, // Immanence
#[Inject] ReviewService $reviewer // Transcendence
) {
$result = $reviewer->evaluate($documents);

// Destiny is decided at this very moment
$this->being = $result->isApproved()
? new ApprovedApplication($documents, $result->getScore())
: new RejectedApplication($result->getReasons());
? new Approved($email, $result->getScore())
: new Rejected($email, $result->getReasons());
}
}
```

`Approved` and `Rejected` are Reason objects—like `Emergency` and `Observation` in the triage tutorial. The Reason determines the destiny: it carries the decision and its basis, and when assigned to `$being`, its type decides which class comes next. Decision logic that completes within the constructor belongs in the Reason layer. When deferred behavior is needed—methods to be called after construction, like `assignER()` in the triage example—those capabilities belong on the Final class.

## Type-Based Continuation

Among the candidate classes specified in `#[Be()]`, the framework automatically selects the one whose constructor `#[Input]` parameter matches the type of a public property on the current object:
Among the candidate classes specified in `#[Be()]`, the framework automatically selects the one whose constructor `#[Input]` parameter can be satisfied by the current object's public properties:

```php
// If ApplicationReview's property is of type ApprovedApplication,
// this class is automatically selected because #[Input] type matches
// ApplicationReview's $being is of type Approved,
// so this class is selected because #[Input] Approved matches
final readonly class ApprovalNotification
{
public function __construct(
#[Input] ApprovedApplication $application,
#[Input] Approved $approval,
#[Inject] Mailer $mailer
) {
$mailer->send($application->getEmail(), 'Approved!');
$mailer->send($approval->email, 'Approved! Score: ' . $approval->score);
}
}
```
Expand Down
98 changes: 98 additions & 0 deletions manuals/1.0/en/08-reason-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ final readonly class StandardDelivery

The type `ExpressShipping $being` itself is the reason why it becomes `ExpressDelivery`. The framework reads this type and automatically selects the corresponding transformation destination.

Any Reason object can serve as either `#[Inject]` (providing transcendent capabilities) or `$being` (determining destiny). The difference is not in the object itself, but in how it is used. A `JTASProtocol` that evaluates patients as `#[Inject]` in one context could determine destiny as `$being` in another.

## Defining Reason Classes

Reason classes bundle the services necessary to realize a specific mode of existence:
Expand Down Expand Up @@ -145,6 +147,102 @@ public function __construct(

"What is needed to become ExpressDelivery?" — a single reason object answers that question. Objects themselves declare "what to become", while reason objects realize "how to achieve that state".

## Reason Returning Potential

So far, Reason has returned immediate values like `Fee`. But consider order processing: inventory must be reserved, payment captured, and shipping arranged—and all three must succeed before any of them commit. If payment fails after inventory is reserved, that reservation must not persist.

For this kind of all-or-nothing coordination, Reason returns a **Potential**—an object that holds a deferred operation, realized later via `be()`. This pattern is only needed when multiple external operations must all commit atomically.

### Potential: Prepared but Uncommitted

A Reason method prepares the external operation and returns a Potential:

```php
final class PaymentGateway
{
public function authorize(string $cardNumber, int $amount): PaymentCapture
{
$authCode = $this->api->authorize($cardNumber, $amount);

return new PaymentCapture(
$authCode,
$amount,
fn () => $this->api->capture($authCode, $amount),
);
}
}
```

`PaymentCapture` is a Potential—it holds the authorization code and a deferred capture operation. The payment is authorized but not yet captured. Calling `be()` commits it:

```php
$capture = $gateway->authorize($cardNumber, $amount);
$capture->authorizationCode; // Available immediately
$capture->be(); // Commits the capture
```
Comment on lines +161 to +182

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Define the PaymentCapture class structure.

The example shows PaymentCapture being constructed (lines 167-171) and used (lines 180-181), but never defines the class itself. Readers need to see how PaymentCapture holds the authorization code and implements be() to understand the Potential pattern.

📖 Suggested addition after line 173

Add the PaymentCapture class definition before the usage example:

final readonly class PaymentCapture
{
    public function __construct(
        public string $authorizationCode,
        public int $amount,
        private \Closure $captureOperation,
    ) {}

    public function be(): void
    {
        ($this->captureOperation)();
    }
}

Then the usage example at lines 178-182 becomes clear: the authorization code is immediately available as a public property, and calling be() executes the deferred capture closure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@manuals/1.0/en/08-reason-layer.md` around lines 161 - 182, The docs reference
a PaymentCapture type but never defines it; add a small immutable class named
PaymentCapture that stores the authorizationCode and amount and accepts a
deferred capture operation (e.g. a Closure) in the constructor, expose the
authorizationCode (and amount) as accessible properties, and implement a be()
method that invokes the stored capture operation to perform the deferred
capture; ensure the constructor parameter names match those used when new
PaymentCapture(...) is called in PaymentGateway::authorize so the example
compiles and the behavior is clear.


### Moment: Holding Potential

A class that holds a Potential from Reason is called a **Moment** (Hegel's 契機—an essential aspect that only makes sense as part of a whole). A Moment implements `MomentInterface`, provided by the framework:

```php
interface MomentInterface
{
public function be(): void;
}
```

```php
final readonly class PaymentCompleted implements MomentInterface
{
public PaymentCapture $capture;

public function __construct(
#[Input] public string $cardNumber,
#[Input] public int $amount,
#[Inject] PaymentGateway $gateway,
) {
$this->capture = $gateway->authorize($cardNumber, $amount);
}

public function be(): void
{
$this->capture->be();
}
}
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### Convergence: Final Realizes Moments

When multiple Moments must all succeed together, a Final Object receives them and calls `be()` on each. This is not an external command—it is self-completion:

```php
final readonly class OrderConfirmed
{
public string $orderId;
public string $status;

public function __construct(
public InventoryReserved $inventory,
public PaymentCompleted $payment,
public ShippingArranged $shipping,
) {
$this->inventory->be();
$this->payment->be();
$this->shipping->be();

$this->orderId = 'ORD-' . date('Ymd') . '-' . bin2hex(random_bytes(4));
$this->status = 'confirmed';
}
}
```

If any Moment cannot be created (because its Reason failed), the Final Object is never constructed. If all Moments exist, `be()` commits every deferred operation. No manual rollback flags, no nested try-catch.

### When to Use This Pattern

Use Potential-returning Reason when multiple external operations must succeed atomically—all commit together, or none at all. For simple cases where Reason returns an immediate value, Potential is unnecessary.

---

The inability to exist is itself an existence. Learn how to handle this in [Validation and Error Handling](./09-error-handling.html) ➡️
12 changes: 6 additions & 6 deletions manuals/1.0/en/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,9 @@ final readonly class PatientArrival

The `#[Be]` attribute declares destiny: this arrival WILL BECOME a TriageAssessment.

## Step 5: Create Destiny Markers
## Step 5: Create Reason Objects

These types represent the two possible destinies:
These Reason objects represent the two possible destinies. The Reason determines the destiny—"this is it":

```php
// src/Reason/Emergency.php
Expand All @@ -155,7 +155,7 @@ final readonly class Emergency {}
final readonly class Observation {}
```

These aren't empty classes—they ARE the distinction. An `Emergency` is fundamentally different from an `Observation`. The type itself carries meaning.
These aren't empty classes—they ARE the distinction. An `Emergency` is fundamentally different from an `Observation`. The type itself carries meaning. When assigned to `$being`, the Reason's type determines which Final class comes next.

## Step 6: Create Being Class

Expand Down Expand Up @@ -367,9 +367,9 @@ src/
│ ├── EmergencyCase.php # Final form: emergency
│ └── ObservationCase.php # Final form: observation
├── Reason/
│ ├── Emergency.php # Destiny marker
│ ├── JTASProtocol.php # Transcendent wisdom
│ └── Observation.php # Destiny marker
│ ├── Emergency.php # Reason: determines destiny
│ ├── JTASProtocol.php # Reason: transcendent wisdom
│ └── Observation.php # Reason: determines destiny
└── Semantic/
├── BodyTemperature.php # What CAN exist
└── HeartRate.php
Expand Down
8 changes: 6 additions & 2 deletions manuals/1.0/ja/01-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,12 @@ Be Frameworkは異なるアプローチを採ります。操作が目的のデ
3. オブジェクトを無理に変更するのではなく、自然な変容(自己変容)を表現する
4. エラーを防ぐのではなく、正しい状態を信頼する

## なぜ「DeletedUser」なのか?

冒頭の問いに戻りましょう。`new DeletedUser($activeUser)` は操作ではなく、変容です。ユーザーが「削除される」のではなく、`$activeUser`から`DeletedUser`という新しい存在が生まれるのです。型そのものが削除済みであることを証明しています。`$status`フラグを確認する必要も、削除済みのユーザーに誤ってメソッドを呼ぶ危険もありません。これがBe Frameworkの本質です:**状態遷移を既存オブジェクトへの操作ではなく、新しい型として表現する。**

## さあ、始めましょう

基礎から学んでいきましょう。全ては[入力クラス]({{ '/manuals/1.0/ja/02-input-classes.html' | relative_url }})から始まります →
**まず動かしてみたい方は?** [Getting Started]({{ '/manuals/1.0/ja/getting-started.html' | relative_url }})でHello Worldを体験するか、[チュートリアル]({{ '/manuals/1.0/ja/tutorial.html' | relative_url }})で実践的な例に挑戦できます。

最初のオブジェクトを作りながら、なぜ『`DeletedUser`』なのかを体感してください。
**概念から理解したい方は?** [入力クラス]({{ '/manuals/1.0/ja/02-input-classes.html' | relative_url }})に進んで、基礎からステップバイステップで学びましょう →
4 changes: 3 additions & 1 deletion manuals/1.0/ja/03-being-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ final readonly class ValidatedUser
}
```

`#[Input]`パラメータには、前のクラスのpublicプロパティが名前の一致により自動的に渡されます。`UserInput`の`public string $name`は`ValidatedUser`の`#[Input] string $name`に対応します。`#[Inject]`パラメータにはDIコンテナから外部の依存が注入されます。この自動マッチングの詳細なルールは[5章 変容](./05-metamorphosis-patterns.html)で説明します。

## 時間的存在としてのオブジェクト

Be Frameworkでは、オブジェクトを静的なデータ構造ではなく、特定の時間の中でのみ存在する時間的な存在として捉えます。
Expand All @@ -53,7 +55,7 @@ Be Frameworkでは、オブジェクトを静的なデータ構造ではなく

### 生

オブジェクトは `public readonly` プロパティとして、その「あるべき姿」を世界に晒します。しかし、そのプロパティが参照されることはありません。生まれた直後に、次のオブジェクトへ引き継がれて消滅するからです。
オブジェクトは `public readonly` プロパティとして、その「あるべき姿」を世界に晒します。フレームワークがこのプロパティを読み取り、次のクラスの `#[Input]` として引き渡します。その後、オブジェクトは消滅し、次の存在に道を譲ります。

### なりたい自分になる

Expand Down
10 changes: 6 additions & 4 deletions manuals/1.0/ja/04-final-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ final readonly class SuccessfulOrder
public string $confirmationCode;
public DateTimeImmutable $timestamp;
public string $message;
public BeenProcessed $been; // 完了の証跡
public BeenConfirmed $been; // 完了の証跡

public function __construct(
#[Input] Money $total, // 内在
Expand All @@ -37,7 +37,7 @@ final readonly class SuccessfulOrder
$this->timestamp = new DateTimeImmutable();
$this->message = "注文確認: {$this->orderId}";

$this->been = new BeenProcessed(
$this->been = new BeenConfirmed(
actor: $card->getHolderName(),
timestamp: $this->timestamp,
evidence: [
Expand All @@ -50,7 +50,9 @@ final readonly class SuccessfulOrder
}
```

入力クラスとは対照的に、最終オブジェクトはドメインの豊かさを完全に表現した存在です。内在が超越と出会い、変容を経て、これ以上変わる必要のない完全な状態に達しています。成功も失敗も同じ構造です。たとえば`FailedOrder`も、拒否の証跡として`$been`を持ちます。
`$been`プロパティはアプリケーションが定義する、ドメイン固有の型を持ちます。`SuccessfulOrder`には`BeenConfirmed`、`FailedOrder`には`BeenRejected`、`DeletedUser`なら`BeenDeleted`というように命名します。何を「完了の証跡」とするかはドメインによって異なり、必要な証跡に応じてクラスを設計します。

入力クラスとは対照的に、最終オブジェクトはドメインの豊かさを完全に表現した存在です。内在が超越と出会い、変容を経て、これ以上変わる必要のない完全な状態に達しています。

## 時間的存在の完全性

Expand All @@ -75,7 +77,7 @@ Be Frameworkでは、オブジェクトの時間的存在を二つの軸で捉

## 複数の最終的運命

オブジェクトはその性質によって複数の最終形態を持つことができます:
オブジェクトはその性質によって複数の最終形態を持つことができます。ここで使っている`$becoming`は変容チェーンを起動する仕組みで、[次章](./04a-becoming.html)で説明します:

```php
$order = $becoming(new OrderInput($items, $card));
Expand Down
Loading