# @btravstack/entity 中文文档

规范地址：https://npmcn.com/package/%40btravstack/entity/

版本：0\.58\.0
npm 元数据更新时间：2026-11-07T21:12:02.929Z
中文译文更新时间：2026-11-08T17:44:02.076Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40btravstack/entity
原文对照：https://npmcn.com/package/%40btravstack/entity/?lang=original
项目主页：https://btravstack.github.io/btravstack/entity/
源码仓库：https://github.com/btravstack/btravstack

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：267 kB，7 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=22
- 运行依赖：0 个
- peer 依赖：zod ^4\.3\.0，unthrown ^5\.0\.0，@unthrown/standard-schema ^5\.0\.0
- 首次发布：2026-08-07T13:46:44.248Z
- 最新版本发布：2026-11-07T21:12:02.596Z
- 近 30 天下载量：8426（2026-10-09 至 2026-11-07，npm 官方下载统计）
- 已发布版本数：56（近 12 个月 56 个）

## 中文简介

Zod v4 上的域实体构建器: 有品牌的字段, 不可改变的数据, 密封的构造, 以及结果而不是抛出

## 安装

```sh
npm install @btravstack/entity
```

也可以使用：`pnpm add @btravstack/entity`、`yarn add @btravstack/entity`、`bun add @btravstack/entity`

## README

# @btravstack/实体

**一个域实体构建者 [类型脚本](https://www.typescriptlang.org/)打开 [佐德语](https://zod.dev/) v4——有品牌的田地,不可改变的数据,密封的构造,以及 `Result` 而不是抛出.**

一份声明给了你一个类型, 四个衍生的计划 来构建合同从, 行为,以及本身是zod schema的类——所以实体互相筑巢 而不失去他们的力量 无所投出: 每一次都是假的 操作返回一个 [`unthrown`](https://github.com/btravstack/unthrown) `Result`。 。 。 .

```sh
pnpm add @btravstack/entity zod unthrown @unthrown/standard-schema
```

`zod`, (中文(简体) ), `unthrown` 财务报告和审定财务报表 `@unthrown/standard-schema` 是同伴的依赖关系。 这个 zod 范围为 `^4.3.0` ——地表被测出,不是被猜出:地表完整 4.3.0 类型核对、申报和通过运行时间断言. 这里不需要一个后来的未成年人, 和单簧管通常把一Zod钉在了过去 每一个包,所以范围保持了同真实一样宽.

```ts
// vocabulary.ts — each brand declared once, imported by every entity
import { z } from "zod";

export const OrgId = z.uuid().brand("OrgId");
export const Slug = z.string().min(1).brand("Slug");
export const Instant = z.iso.datetime().brand("Instant");
```

一个实体从该模块中进口其品牌; `Entity.field` 只添加旗帜 到一个。 自由文本字段没有什么可混淆的,所以它选择与 `unbranded: true` 而不是给一个没人需要的品牌做点什么.

```ts
import { z } from "zod";
import { Entity } from "@btravstack/entity";
import { Instant, OrgId, Slug } from "./vocabulary.js";

class Organization extends Entity("Organization")({
  id: Entity.field(OrgId, { generated: true, immutable: true }),
  slug: Entity.field(Slug, { immutable: true }),
  name: Entity.field(z.string().min(1), { unbranded: true }), // free text
  createdAt: Entity.field(Instant, { generated: true, immutable: true }),
}) {
  get greeting(): string {
    return `Welcome, ${this.name}`;
  }
}

// Bind the effect sources once, at your composition root.
const createOrganization = Organization.factory({
  id: () => ids.next(),
  createdAt: () => clock.now(),
});

const org = createOrganization({ slug, name }).getOrThrow();
await db.insert(org.toJSON()); // the stored shape — never `_tag`
const loaded = Organization.make(row).getOrThrow(); // rows, imports, event folds
const renamed = loaded.update({ name: next }).getOrThrow(); // a NEW entity
```

| Schema 成员 | 对于 |
| --- | --- |
| `input` | 所有的东西 `make()` 接受 |
| `output` | 存储状态, 内部字段包括: 通过允许列表从中选择响应 |
| `createInput` | 域名允许创建集的内容—— `input` 减去 `generated` 字段 |
| `updateInput` | 域允许改变什么.. `input` 减去 `immutable` 字段, 部分 |
| _类_ | 解析实例; 作为字段有效 |

四是构件,不是公共API. 一条路从他们那里取出 允许列表,所以一个内部域保持内部,一个新的域保持到 有人补充道: [显示 HTTP 合同](https://btravstack.github.io/btravstack/entity/how-to/http-contract)。 。 。 .

`generated`, (中文(简体) ), `immutable`, (中文(简体) ), `identity` 财务报告和审定财务报表 `unbranded` 为: **旗帜 字段**,写为 `Entity.field(schema, flags)`; 一个字段里没有字段是 赤裸的图谋. `computed` 财务报告和审定财务报表 `invariants` 是两个声明选项.

一个实体是: **最终**几个实体共享的领域和行为 根号, `Entity.abstract(name)(fields)`,并在那里延长寿命 实体是您命名的值 :

```ts
abstract class AccountBase extends Entity.abstract("Account")({
  id: AccountId,
  label: DisplayName,
}) {
  abstract describe(): string; // every variant owes this — the compiler checks
}

class Personal extends AccountBase.extend("Personal")({
  kind: z.literal("personal"),
}) {
  override describe(): string {
    return `personal ${this.label}`;
  }
}

// `Business` is declared the same way, on the same root
export const Account = Entity.union("kind", [Personal, Business]);
export type Account = Entity.Instance<typeof Account>;

Account.make(row); // Result<Personal | Business, InvalidEntity>
```

一个变体是它根的真正例子,所以 `instanceof` 狭入了它,和 `Account` 作为类型 `Personal | Business`。没有类表:设置 工会在基层地位是: `TS2507` 在宣言中,因为 类的实例类型完全不能是联合体( c)`TS2509`) (中文(简体) ).

## 合计数

集合根是实体. 它拥有其边界内的实体,并且 每一个变化都要通过一个方法 在根上命名商业意图. 该方法检查其规则并返回一个新的根,当其他部分 系统应该作出反应, 它产生的事件旁边。 事件是可选的 : 命令中没有要宣布的单独返回实体.

```ts
class Invoice extends Entity("Invoice")({
  id: Entity.field(InvoiceId, { identity: true, immutable: true }),
  status: InvoiceStatus, // "DRAFT" | "ISSUED" | "VOID"
  total: Money,
}) {
  void(): Result<
    { readonly entity: Invoice; readonly events: readonly [InvoiceVoided] },
    InvoiceNotVoidable | Entity.InvalidEntity
  > {
    if (this.status !== "ISSUED") {
      return Err(new InvoiceNotVoidable({ invoiceId: this.id }));
    }
    return this.update({ status: "VOID" }).map((entity) => ({
      entity,
      events: [{ type: "InvoiceVoided", invoiceId: entity.id }] as const,
    }));
  }
}

const { entity, events } = invoice.void().getOrThrow();
// one transaction: the new row, and one outbox row per event
```

事件不应用回根;它们告诉其他模块什么 所以会计部门可以对 `InvoiceVoided` 没有 发票知道它的存在. `update()` 是公开的,所以导出命令 域模块而不是要补丁的实体。 见 [型号 A 合计](https://btravstack.github.io/btravstack/entity/how-to/model-an-aggregate) 财务报告和审定财务报表 [写入命令和 事件](https://btravstack.github.io/btravstack/entity/how-to/write-commands)。 。 。 .

### 事件驱动的根源

当根的状态应该改变 仅仅通过它宣布的事件, 声明一下 `Entity.aggregate` 换句话说 它宣布事件和一 每次处理器, 它没有 `update()`,每个成功的命令返回 a 秘密决定。 您仍然选择存储 : 或事件流本身.

```ts
class Subscription extends Entity.aggregate("Subscription")({
  id: Entity.field(SubscriptionId, { identity: true }), // a root needs an identity
  seats: Seats,
  status: z.enum(["ACTIVE", "CANCELLED"]),
})({
  events: SubscriptionEvent, // a zod discriminated union on `type`
  opens: {
    SubscriptionStarted: (e) => ({
      id: e.subscriptionId,
      seats: e.seats,
      status: "ACTIVE",
    }),
  },
  evolve: {
    // one handler per event, or it does not compile
    SeatsChanged: (r, e) => ({ ...r, seats: e.seats }),
    SubscriptionCancelled: (r) => ({ ...r, status: "CANCELLED" }),
  },
}) {
  changeSeats(seats: number) {
    if (this.status === "CANCELLED") return Err(new SubscriptionIsCancelled());
    return this.emit({ type: "SeatsChanged", seats }); // fold, verify once, decide
  }
}

const decision = subscription.changeSeats(5).getOrThrow();
decision.events; // every event since the load
decision.expectedVersion; // the version the store must still be at
repository.save(decision); // a state row and an outbox, or an event stream
```

仅 `emit` 财务报告和审定财务报表 `start` 构建一个决定, 所以一个寄存器只能被移交 事件被折叠 并检查每个变化。 装入 `make(row, { version })` 或者说 `replay(stream)`; 与 或作为事件而不触及其声明。 只有当 您想要这个约束; a `Entity` root 是默认值。 见 [模拟事件驱动 合计](https://btravstack.github.io/btravstack/entity/how-to/model-an-event-driven-aggregate)。 。 。 .

## 文档

**[实体指南](https://btravstack.github.io/btravstack/entity/)**

-   [保证和兼容性](https://btravstack.github.io/btravstack/entity/reference/guarantees) ——执行什么,留给您什么,支持节点/TypeScript/zod版本
-   [比较 Zod 和 效果](https://btravstack.github.io/btravstack/entity/explanation/compared) 一种模式,三种方式
-   [开始](https://btravstack.github.io/btravstack/entity/tutorial/getting-started) 从无到有工作实体
-   [参考文献](https://btravstack.github.io/btravstack/entity/reference/declaration) - 每个成员,选项和类型
-   [解释](https://btravstack.github.io/btravstack/entity/explanation/why-entity) ——为什么它是这样建造的
-   如何进行 : [HTTP合同](https://btravstack.github.io/btravstack/entity/how-to/http-contract) • 妇女 [持久性和还水性](https://btravstack.github.io/btravstack/entity/how-to/persist-and-rehydrate) • 妇女 [模型组合](https://btravstack.github.io/btravstack/entity/how-to/model-an-aggregate) • 妇女 [模拟事件驱动的集合](https://btravstack.github.io/btravstack/entity/how-to/model-an-event-driven-aggregate) • 妇女 [测试域逻辑](https://btravstack.github.io/btravstack/entity/how-to/test-domain-logic)

## 许可证

[麻省理工学院](https://npmcn.com/package/%40btravstack/entity/LICENSE) 贝诺伊特旅行社

## 常见问题

### @btravstack/entity 是什么？

Zod v4 上的域实体构建器: 有品牌的字段, 不可改变的数据, 密封的构造, 以及结果而不是抛出

### 如何安装 @btravstack/entity？

在项目目录运行 npm install @btravstack/entity，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：zod（^4\.3\.0）、unthrown（^5\.0\.0）、@unthrown/standard-schema（^5\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @btravstack/entity 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @btravstack/entity 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @btravstack/entity 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @btravstack/entity 可以商用吗？

@btravstack/entity 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @btravstack/entity 的下载量有多少？

根据 npm 官方下载统计，@btravstack/entity 在 2026/10/09 – 2026/11/07 这 30 天内被下载 8,426 次，日均约 281 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @btravstack/entity 还在维护吗？

最新版本 0\.58\.0 发布于 2026/11/08（今天）；至今共发布 56 个版本，其中过去 12 个月发布了 56 个；首次发布于 2026/08/07。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

