agentsclimarketplace

Workflow domain entity

Skill marzun9620/agent_skills/workflow/skills/workflow-domain-entity

Domain Entity implementation guide. Define entities with Schema.Class + Brand, plus Value Objects, Domain Errors (Schema.TaggedError), and barrel exports. Triggers: domain entity creation, Schema.Class, Brand types, domain model, Value Object, domain error, domain layer implementation.From its SKILL.md

Install
npx -y skills add marzun9620/agent_skills --skill workflow-domain-entity

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

4.7 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it

Domain Entity 実装手順

ADR-0001 / ADR-0004 準拠。domain 層は純粋なデータ定義のみ。I/O 禁止。

ファイル構成(必ずこの構造に従う)

apps/datahub/src/domain/{module}/
├── {entity}Id.ts         # Brand 型 ID
├── {entity}Status.ts     # Enum(必要な場合)
├── {entity}.ts           # Entity 本体(Schema.Class)
├── errors.ts             # Domain Error(Schema.TaggedError)
└── index.ts              # Barrel export(全 public 型)

ファイル名は camelCase。1ファイル1責務。

Import ルール(重要)

  • 同一ディレクトリ内: ./ で直接 import OK
  • cross-directory(他の domain module 等): barrel(index.js)経由のみ
// ✅ 同一ディレクトリ内
import { EntityId } from "./entityId.js";

// ✅ 他の domain module
import { UserId } from "~/domain/user/index.js";

// ❌ 禁止
import { UserId } from "~/domain/user/userId.js";

1. Entity 定義

ファイル: apps/datahub/src/domain/{module}/{entity}.ts

import { Schema } from "effect";
import { EntityId } from "./entityId.js";

export class Entity extends Schema.Class<Entity>("Entity")({
  id: EntityId,
  name: Schema.String,
  status: EntityStatus,
  // nested value objects OK
}) {}
  • Schema.Class<T>(name)({fields}) で定義
  • メソッドなし、純粋なデータコンテナ
  • ネストされた Value Object / Enum を参照可能

2. Value Object(ID)

ファイル: apps/datahub/src/domain/{module}/{entity}Id.ts

import { Schema } from "effect";

export const EntityId = Schema.String.pipe(Schema.brand("EntityId"));
export type EntityId = typeof EntityId.Type;

3. Enum(Status / Role / Kind)

ファイル: apps/datahub/src/domain/{module}/{entity}Status.ts

import { Schema } from "effect";

export const EntityStatus = Schema.Literal("ACTIVE", "ARCHIVED");
export type EntityStatus = typeof EntityStatus.Type;

命名: *Status, *Role, *Kind を使う。*Type は避ける。

4. Domain Error

ファイル: apps/datahub/src/domain/{module}/errors.ts

import { Schema } from "effect";
import { EntityId } from "./entityId.js";

export class EntityNotFoundError extends Schema.TaggedError<EntityNotFoundError>()(
  "EntityNotFoundError",
  { entityId: EntityId },
) {}
  • Schema.TaggedError を使う(Data.TaggedError は domain 層で禁止)
  • Brand にしない

5. Domain Service(必要な場合のみ)

ファイル: apps/datahub/src/domain/services/{serviceName}.ts

import { Match, Option } from "effect";

// 純粋関数のみ。Effect を返す場合も I/O なし
export const checkPermission = (role: SystemRole) =>
  Match.value(role).pipe(
    Match.when("SYSTEM_ADMIN", () => Effect.void),
    Match.when("USER", () => Effect.fail(new InsufficientPermissionError({}))),
    Match.exhaustive,
  );
  • nullable は Option.fromNullable + Option.match で処理
  • Match.value() + Match.exhaustive でパターンマッチ

6. Barrel Export

ファイル: apps/datahub/src/domain/{module}/index.ts

export { Entity } from "./entity.js";
export { EntityId } from "./entityId.js";
export type { EntityId as EntityIdType } from "./entityId.js";
export { EntityStatus } from "./entityStatus.js";
export { EntityNotFoundError } from "./errors.js";

cross-directory import はこの barrel 経由のみ。同一ディレクトリ内は ./ OK。

テスト踏襲ルール

実装前に、plan の Ref test に指定された既存テストファイルを読むこと:

  • 同モジュールのテストがあればそれを踏襲
  • なければ tests/domain/ 内の別モジュールのテストを参考にする
  • 構造を合わせる: type 定義(XxxCase)、table-driven(.each())、AAA パターン
  • expect(result).toEqual(expected) を使う(toBe ではなく toEqual)

チェックリスト

  • type を使っている(interface 禁止 — ADR-0001)
  • Schema.TaggedError を使っている(Data.TaggedError 禁止)
  • I/O なし(postgres, drizzle, hono, node:fs, node:net の import なし)
  • _tag への直接アクセスなし(Schema.is() / Match.tag() を使用)
  • barrel export が存在する
  • any を使っていない
  • named export のみ(default export 禁止)

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 619,450. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.