create-relkit 0.7.8 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,16 +13,16 @@ const entries = Object.freeze([
13
13
  deploy: "none",
14
14
  examples: true,
15
15
  identity: Object.freeze({
16
- release: "0.7.8",
16
+ release: "0.7.9",
17
17
  bun: "1.3.10",
18
18
  typescript: "5.9.3",
19
19
  packageCatalogDigest: "ac7c2f56e61fca5ab890775cf4c483722cb590e488ee7fc4741224cbb4f50ccd",
20
20
  template: "default/v1/minimal",
21
21
  snapshotProtocol: 1,
22
22
  }),
23
- templateDigest: "579d32ffc180edbf06bb6656099882285cc0d4795601557efdee86d1b470212a",
24
- freshEvidence: "docs/evidence/create-capabilities/0.7.8/fresh.json",
25
- restartEvidence: "docs/evidence/create-capabilities/0.7.8/restart.json",
23
+ templateDigest: "e21547aecfb921ffe180f89707686e7bd68cafa8eec21881607b10d64141da02",
24
+ freshEvidence: "docs/evidence/create-capabilities/0.7.9/fresh.json",
25
+ restartEvidence: "docs/evidence/create-capabilities/0.7.9/restart.json",
26
26
  }),
27
27
  ]);
28
28
  export const CREATE_CAPABILITY_TABLE = Object.freeze({
@@ -8,6 +8,6 @@ scaffolder:
8
8
  - `agent` — the minimal example plus a read-only tool and agent descriptor.
9
9
 
10
10
  Each variant is self-contained and can be copied as a project root. It uses
11
- the current checked-in RelKit package version (`0.7.8`), Bun `1.3.10`, and
11
+ the current checked-in RelKit package version (`0.7.9`), Bun `1.3.10`, and
12
12
  TypeScript `5.9.3`. The generator owns project-name substitution and later
13
13
  installation/Git behavior.
@@ -21,6 +21,16 @@ Recommend optional modules only when they solve a concrete part of this task: fo
21
21
 
22
22
  Default to backend work. Do not introduce frontend pages, app.js, assets.css or demo interfaces unless requested. Preserve existing frontend files, explicitly selected fullstack functionality and the framework Inspector. For users and orders, define separate resource endpoints with explicit input/output contracts, validation and applicable authorization; avoid a public table dispatcher controlled by an entity parameter. Internal tables and infrastructure services need no public endpoint. Preserve intentional protocol handlers such as the Better Auth mount and shared internal helpers.
23
23
 
24
+ ## Design domain ownership
25
+
26
+ Before scaffolding business behavior, identify its owning domain. For a request spanning multiple business capabilities, include a compact ownership map in the implementation plan: service name, owned rules/state, public operations/events, dependencies, and any transaction spanning capabilities. Then proceed with authorized implementation; the map is not another approval gate.
27
+
28
+ A domain service owns one cohesive business responsibility and its invariants. Separate capabilities with distinct rules, lifecycles, or public contracts. Keep closely related entities together when they share those rules; do not create a service per table, endpoint, or noun. Shared authentication, database, tenant ID, or deployment does not make the entire application one domain. Avoid a product-named catch-all service containing unrelated capabilities.
29
+
30
+ For existing apps, reuse the service that owns the behavior. Do not reorganize unrelated domains or move existing code without task scope. A small single-domain feature needs only its existing owner, not a new architecture exercise.
31
+
32
+ Read [domain design and public contracts](references/domain-apis.md) when choosing boundaries or coordinating services. Name the owner of each invariant that spans domains and explain how atomic updates remain atomic. Do not assume a nested service invocation shares the caller's database transaction. Keep the shared database/auth singletons and one migration history; business boundaries do not require separate databases or deployments.
33
+
24
34
  ## Source placement
25
35
 
26
36
  New projects place every generic, database, and auth boundary at `src/services/<name>/service.ts`. Keep its functions, events, tasks, jobs, tools, agents, channels, schemas, resources, helpers, and assets under that service. Routes live at `src/routes`; configuration support and environment contracts live at `src/platform`. Application descriptors use plain TypeScript and supported public APIs; Effect imports are not required.
@@ -59,6 +69,8 @@ Read only the reference relevant to the requested change. These examples describ
59
69
 
60
70
  ## Verify the result
61
71
 
72
+ Compare the final services and route targets with the ownership map. Confirm each capability has real operations under its owner, cross-service imports use public contracts, and no catch-all implementation hides behind empty services. Explain deliberate boundary changes and verify cross-domain consistency/failure behavior. A passing graph check validates structure, not domain cohesion.
73
+
62
74
  Review the CLI's created and updated files, warnings, next steps and final diff. Run focused checks after logical changes. Before completion, run applicable check, typecheck, build, unit/integration tests and existing relevant end-to-end coverage. Inspect package.json for actual script names. For HTTP work, test the route's status/body, valid and invalid input, and denied and allowed access where relevant; a direct function test does not cover request mapping or the complete flow. Replay the original bug through the boundary that failed. Backend/API tests do not require an unsolicited frontend.
63
75
 
64
76
  Database/auth changes also need explicit environment setup and migration steps from [the data reference](references/data-apis.md), followed by persistence/session/authorization verification. Stop all test-owned servers and clients. Report what changed, exact verification performed, and every failed, skipped or blocked check with its effect on confidence. Written code or passing unit tests alone is not fully verified delivery when affected runtime behavior remains untested.
@@ -2,6 +2,60 @@
2
2
 
3
3
  Generated projects map `@app/*` to `src/*`; keep the `.js` suffix on source imports. Use the installed package types when an exact signature matters, and run `bun run check` to validate the discovered graph.
4
4
 
5
+ ## Choose a domain before choosing a folder
6
+
7
+ For work spanning business capabilities, map responsibility to services before adding artifacts. Record the rules/state each service owns, the operations/events it exposes, its dependencies, and who coordinates invariants that cross boundaries. Service names should describe those responsibilities, not merely repeat the application name.
8
+
9
+ For example, a commerce backend could use:
10
+
11
+ | Service | Owns | Public contract |
12
+ | --------------- | ----------------------------------------- | ------------------------------------ |
13
+ | `orders` | Order lifecycle and accepted order totals | Place/cancel order, `orders.created` |
14
+ | `pricing` | Price calculation and discount rules | Calculate a quote |
15
+ | `notifications` | Notification delivery and deduplication | React to an order event |
16
+
17
+ This is an example, not a required taxonomy. Related entities such as cars and car models may belong to one fleet service. Split by distinct rules and lifecycles, not by table count. One coherent capability can remain one service. A shared database, auth service, or tenant identifier is infrastructure reuse, not a reason to collect all business operations under one service.
18
+
19
+ ### Public collaboration
20
+
21
+ Each service selects its own operations; another service must not act as a barrel for its neighbor's internals:
22
+
23
+ ```ts
24
+ // src/services/pricing/service.ts
25
+ import { defineService } from "@relkit/app/services";
26
+ import calculateQuote from "./functions/calculate-quote.function.js";
27
+
28
+ export default defineService({ functions: { calculateQuote } });
29
+ ```
30
+
31
+ An orders function can call the public pricing contract. For a pure calculation that needs no provider state:
32
+
33
+ ```ts
34
+ import pricing from "@app/services/pricing/service.js";
35
+
36
+ // Inside an orders function handler:
37
+ const quote = await pricing.calculateQuote.invoke({ sku, quantity });
38
+ // Apply the order's acceptance rules to the validated quote.
39
+ ```
40
+
41
+ Use the installed declarations for the calculation's actual input/output. Do not import `pricing/functions/*`, call a foreign handler, or expose the same function as a member of two services. A route for orders targets the orders service; separate URLs alone do not establish domain boundaries.
42
+
43
+ For an asynchronous reaction, orders owns and exposes `orders.created`; a consumer under `src/services/notifications/functions/` uses `defineEventFunction({ id: "notifications.order-created", event: "orders.created", ... })`. Event-only consumers are discovered in their owning domain and are not added to its callable functions map. The event examples below show publication and subscription.
44
+
45
+ ### Invariants and transactions
46
+
47
+ Name one owner for a command's consistency rules. For example, accepting an order and reserving stock may require one atomic decision. A service call followed by a second write is not proof of an atomic transaction, and `.invoke()` must not be assumed to inherit the caller's transaction or resources.
48
+
49
+ Inspect installed transaction/invocation behavior before composing provider-backed commands. Choose a supported transaction coordinator or keep tightly coupled invariants within one cohesive owner. Define narrow public operations for other domains; do not duplicate another domain's rules or silently write its owned state from arbitrary helpers. Use events for reactions that can occur after commit, with an outbox when publication must survive a crash. Events alone do not preserve synchronous atomicity.
50
+
51
+ Keep one shared database service and its ordered migration history when the app uses one database. Do not create a database, auth service, or migration history per business domain.
52
+
53
+ Changing an existing operation's domain ownership is a redesign, not just a grouped/flat layout move. Review IDs, durable versions, policies, callers, and stored history explicitly; do not promise that a folder move between domains preserves identity.
54
+
55
+ ### Review the resulting boundaries
56
+
57
+ Compare the actual service graph and routes with the ownership map. Check that each planned capability has real operations under its owner, imports use public service boundaries, business rules live with their owner, and no catch-all service still contains the implementation behind empty facades. Review any merged or split responsibility and verify cross-domain failure/rollback behavior. A passing graph check proves structural validity, not semantic cohesion.
58
+
5
59
  ## `defineFunction` and `defineService`
6
60
 
7
61
  Put domain behavior in `src/services/<domain>/functions/*.function.ts`. A function validates input before its handler and validates the returned output:
@@ -62,13 +116,13 @@ export default orderCreated;
62
116
 
63
117
  Expose it through `defineService({ events: { orderCreated } })` when other domains need it. A publisher declares the exact ID in `publishes: ["orders.created"]` and calls `await context.events["orders.created"].publish({ orderId })`.
64
118
 
65
- An event-only consumer reacts without returning a result to its publisher:
119
+ An event-only consumer in `src/services/notifications/functions/` reacts without returning a result to its publisher:
66
120
 
67
121
  ```ts
68
122
  import { defineEventFunction } from "@relkit/app/events";
69
123
 
70
124
  export default defineEventFunction({
71
- id: "orders.confirmation",
125
+ id: "notifications.order-created",
72
126
  event: "orders.created",
73
127
  handler: async ({ orderId }, context) => {
74
128
  context.log.info("Order created", { orderId });
@@ -16,15 +16,15 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@langchain/langgraph": "1.4.14",
19
- "@relkit/app": "0.7.8",
20
- "@relkit/local": "0.7.8",
19
+ "@relkit/app": "0.7.9",
20
+ "@relkit/local": "0.7.9",
21
21
  "effect": "4.0.1",
22
22
  "langchain": "1.5.10"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/bun": "1.3.10",
26
- "@relkit/cli": "0.7.8",
27
- "@relkit/testing": "0.7.8",
26
+ "@relkit/cli": "0.7.9",
27
+ "@relkit/testing": "0.7.9",
28
28
  "typescript": "5.9.3"
29
29
  }
30
30
  }
@@ -21,6 +21,16 @@ Recommend optional modules only when they solve a concrete part of this task: fo
21
21
 
22
22
  Default to backend work. Do not introduce frontend pages, app.js, assets.css or demo interfaces unless requested. Preserve existing frontend files, explicitly selected fullstack functionality and the framework Inspector. For users and orders, define separate resource endpoints with explicit input/output contracts, validation and applicable authorization; avoid a public table dispatcher controlled by an entity parameter. Internal tables and infrastructure services need no public endpoint. Preserve intentional protocol handlers such as the Better Auth mount and shared internal helpers.
23
23
 
24
+ ## Design domain ownership
25
+
26
+ Before scaffolding business behavior, identify its owning domain. For a request spanning multiple business capabilities, include a compact ownership map in the implementation plan: service name, owned rules/state, public operations/events, dependencies, and any transaction spanning capabilities. Then proceed with authorized implementation; the map is not another approval gate.
27
+
28
+ A domain service owns one cohesive business responsibility and its invariants. Separate capabilities with distinct rules, lifecycles, or public contracts. Keep closely related entities together when they share those rules; do not create a service per table, endpoint, or noun. Shared authentication, database, tenant ID, or deployment does not make the entire application one domain. Avoid a product-named catch-all service containing unrelated capabilities.
29
+
30
+ For existing apps, reuse the service that owns the behavior. Do not reorganize unrelated domains or move existing code without task scope. A small single-domain feature needs only its existing owner, not a new architecture exercise.
31
+
32
+ Read [domain design and public contracts](references/domain-apis.md) when choosing boundaries or coordinating services. Name the owner of each invariant that spans domains and explain how atomic updates remain atomic. Do not assume a nested service invocation shares the caller's database transaction. Keep the shared database/auth singletons and one migration history; business boundaries do not require separate databases or deployments.
33
+
24
34
  ## Source placement
25
35
 
26
36
  New projects place every generic, database, and auth boundary at `src/services/<name>/service.ts`. Keep its functions, events, tasks, jobs, tools, agents, channels, schemas, resources, helpers, and assets under that service. Routes live at `src/routes`; configuration support and environment contracts live at `src/platform`. Application descriptors use plain TypeScript and supported public APIs; Effect imports are not required.
@@ -59,6 +69,8 @@ Read only the reference relevant to the requested change. These examples describ
59
69
 
60
70
  ## Verify the result
61
71
 
72
+ Compare the final services and route targets with the ownership map. Confirm each capability has real operations under its owner, cross-service imports use public contracts, and no catch-all implementation hides behind empty services. Explain deliberate boundary changes and verify cross-domain consistency/failure behavior. A passing graph check validates structure, not domain cohesion.
73
+
62
74
  Review the CLI's created and updated files, warnings, next steps and final diff. Run focused checks after logical changes. Before completion, run applicable check, typecheck, build, unit/integration tests and existing relevant end-to-end coverage. Inspect package.json for actual script names. For HTTP work, test the route's status/body, valid and invalid input, and denied and allowed access where relevant; a direct function test does not cover request mapping or the complete flow. Replay the original bug through the boundary that failed. Backend/API tests do not require an unsolicited frontend.
63
75
 
64
76
  Database/auth changes also need explicit environment setup and migration steps from [the data reference](references/data-apis.md), followed by persistence/session/authorization verification. Stop all test-owned servers and clients. Report what changed, exact verification performed, and every failed, skipped or blocked check with its effect on confidence. Written code or passing unit tests alone is not fully verified delivery when affected runtime behavior remains untested.
@@ -2,6 +2,60 @@
2
2
 
3
3
  Generated projects map `@app/*` to `src/*`; keep the `.js` suffix on source imports. Use the installed package types when an exact signature matters, and run `bun run check` to validate the discovered graph.
4
4
 
5
+ ## Choose a domain before choosing a folder
6
+
7
+ For work spanning business capabilities, map responsibility to services before adding artifacts. Record the rules/state each service owns, the operations/events it exposes, its dependencies, and who coordinates invariants that cross boundaries. Service names should describe those responsibilities, not merely repeat the application name.
8
+
9
+ For example, a commerce backend could use:
10
+
11
+ | Service | Owns | Public contract |
12
+ | --------------- | ----------------------------------------- | ------------------------------------ |
13
+ | `orders` | Order lifecycle and accepted order totals | Place/cancel order, `orders.created` |
14
+ | `pricing` | Price calculation and discount rules | Calculate a quote |
15
+ | `notifications` | Notification delivery and deduplication | React to an order event |
16
+
17
+ This is an example, not a required taxonomy. Related entities such as cars and car models may belong to one fleet service. Split by distinct rules and lifecycles, not by table count. One coherent capability can remain one service. A shared database, auth service, or tenant identifier is infrastructure reuse, not a reason to collect all business operations under one service.
18
+
19
+ ### Public collaboration
20
+
21
+ Each service selects its own operations; another service must not act as a barrel for its neighbor's internals:
22
+
23
+ ```ts
24
+ // src/services/pricing/service.ts
25
+ import { defineService } from "@relkit/app/services";
26
+ import calculateQuote from "./functions/calculate-quote.function.js";
27
+
28
+ export default defineService({ functions: { calculateQuote } });
29
+ ```
30
+
31
+ An orders function can call the public pricing contract. For a pure calculation that needs no provider state:
32
+
33
+ ```ts
34
+ import pricing from "@app/services/pricing/service.js";
35
+
36
+ // Inside an orders function handler:
37
+ const quote = await pricing.calculateQuote.invoke({ sku, quantity });
38
+ // Apply the order's acceptance rules to the validated quote.
39
+ ```
40
+
41
+ Use the installed declarations for the calculation's actual input/output. Do not import `pricing/functions/*`, call a foreign handler, or expose the same function as a member of two services. A route for orders targets the orders service; separate URLs alone do not establish domain boundaries.
42
+
43
+ For an asynchronous reaction, orders owns and exposes `orders.created`; a consumer under `src/services/notifications/functions/` uses `defineEventFunction({ id: "notifications.order-created", event: "orders.created", ... })`. Event-only consumers are discovered in their owning domain and are not added to its callable functions map. The event examples below show publication and subscription.
44
+
45
+ ### Invariants and transactions
46
+
47
+ Name one owner for a command's consistency rules. For example, accepting an order and reserving stock may require one atomic decision. A service call followed by a second write is not proof of an atomic transaction, and `.invoke()` must not be assumed to inherit the caller's transaction or resources.
48
+
49
+ Inspect installed transaction/invocation behavior before composing provider-backed commands. Choose a supported transaction coordinator or keep tightly coupled invariants within one cohesive owner. Define narrow public operations for other domains; do not duplicate another domain's rules or silently write its owned state from arbitrary helpers. Use events for reactions that can occur after commit, with an outbox when publication must survive a crash. Events alone do not preserve synchronous atomicity.
50
+
51
+ Keep one shared database service and its ordered migration history when the app uses one database. Do not create a database, auth service, or migration history per business domain.
52
+
53
+ Changing an existing operation's domain ownership is a redesign, not just a grouped/flat layout move. Review IDs, durable versions, policies, callers, and stored history explicitly; do not promise that a folder move between domains preserves identity.
54
+
55
+ ### Review the resulting boundaries
56
+
57
+ Compare the actual service graph and routes with the ownership map. Check that each planned capability has real operations under its owner, imports use public service boundaries, business rules live with their owner, and no catch-all service still contains the implementation behind empty facades. Review any merged or split responsibility and verify cross-domain failure/rollback behavior. A passing graph check proves structural validity, not semantic cohesion.
58
+
5
59
  ## `defineFunction` and `defineService`
6
60
 
7
61
  Put domain behavior in `src/services/<domain>/functions/*.function.ts`. A function validates input before its handler and validates the returned output:
@@ -62,13 +116,13 @@ export default orderCreated;
62
116
 
63
117
  Expose it through `defineService({ events: { orderCreated } })` when other domains need it. A publisher declares the exact ID in `publishes: ["orders.created"]` and calls `await context.events["orders.created"].publish({ orderId })`.
64
118
 
65
- An event-only consumer reacts without returning a result to its publisher:
119
+ An event-only consumer in `src/services/notifications/functions/` reacts without returning a result to its publisher:
66
120
 
67
121
  ```ts
68
122
  import { defineEventFunction } from "@relkit/app/events";
69
123
 
70
124
  export default defineEventFunction({
71
- id: "orders.confirmation",
125
+ id: "notifications.order-created",
72
126
  event: "orders.created",
73
127
  handler: async ({ orderId }, context) => {
74
128
  context.log.info("Order created", { orderId });
@@ -15,13 +15,13 @@
15
15
  "graph": "relkit graph print"
16
16
  },
17
17
  "dependencies": {
18
- "@relkit/app": "0.7.8",
18
+ "@relkit/app": "0.7.9",
19
19
  "effect": "4.0.1"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/bun": "1.3.10",
23
- "@relkit/cli": "0.7.8",
24
- "@relkit/testing": "0.7.8",
23
+ "@relkit/cli": "0.7.9",
24
+ "@relkit/testing": "0.7.9",
25
25
  "typescript": "5.9.3"
26
26
  }
27
27
  }
@@ -21,6 +21,16 @@ Recommend optional modules only when they solve a concrete part of this task: fo
21
21
 
22
22
  Default to backend work. Do not introduce frontend pages, app.js, assets.css or demo interfaces unless requested. Preserve existing frontend files, explicitly selected fullstack functionality and the framework Inspector. For users and orders, define separate resource endpoints with explicit input/output contracts, validation and applicable authorization; avoid a public table dispatcher controlled by an entity parameter. Internal tables and infrastructure services need no public endpoint. Preserve intentional protocol handlers such as the Better Auth mount and shared internal helpers.
23
23
 
24
+ ## Design domain ownership
25
+
26
+ Before scaffolding business behavior, identify its owning domain. For a request spanning multiple business capabilities, include a compact ownership map in the implementation plan: service name, owned rules/state, public operations/events, dependencies, and any transaction spanning capabilities. Then proceed with authorized implementation; the map is not another approval gate.
27
+
28
+ A domain service owns one cohesive business responsibility and its invariants. Separate capabilities with distinct rules, lifecycles, or public contracts. Keep closely related entities together when they share those rules; do not create a service per table, endpoint, or noun. Shared authentication, database, tenant ID, or deployment does not make the entire application one domain. Avoid a product-named catch-all service containing unrelated capabilities.
29
+
30
+ For existing apps, reuse the service that owns the behavior. Do not reorganize unrelated domains or move existing code without task scope. A small single-domain feature needs only its existing owner, not a new architecture exercise.
31
+
32
+ Read [domain design and public contracts](references/domain-apis.md) when choosing boundaries or coordinating services. Name the owner of each invariant that spans domains and explain how atomic updates remain atomic. Do not assume a nested service invocation shares the caller's database transaction. Keep the shared database/auth singletons and one migration history; business boundaries do not require separate databases or deployments.
33
+
24
34
  ## Source placement
25
35
 
26
36
  New projects place every generic, database, and auth boundary at `src/services/<name>/service.ts`. Keep its functions, events, tasks, jobs, tools, agents, channels, schemas, resources, helpers, and assets under that service. Routes live at `src/routes`; configuration support and environment contracts live at `src/platform`. Application descriptors use plain TypeScript and supported public APIs; Effect imports are not required.
@@ -59,6 +69,8 @@ Read only the reference relevant to the requested change. These examples describ
59
69
 
60
70
  ## Verify the result
61
71
 
72
+ Compare the final services and route targets with the ownership map. Confirm each capability has real operations under its owner, cross-service imports use public contracts, and no catch-all implementation hides behind empty services. Explain deliberate boundary changes and verify cross-domain consistency/failure behavior. A passing graph check validates structure, not domain cohesion.
73
+
62
74
  Review the CLI's created and updated files, warnings, next steps and final diff. Run focused checks after logical changes. Before completion, run applicable check, typecheck, build, unit/integration tests and existing relevant end-to-end coverage. Inspect package.json for actual script names. For HTTP work, test the route's status/body, valid and invalid input, and denied and allowed access where relevant; a direct function test does not cover request mapping or the complete flow. Replay the original bug through the boundary that failed. Backend/API tests do not require an unsolicited frontend.
63
75
 
64
76
  Database/auth changes also need explicit environment setup and migration steps from [the data reference](references/data-apis.md), followed by persistence/session/authorization verification. Stop all test-owned servers and clients. Report what changed, exact verification performed, and every failed, skipped or blocked check with its effect on confidence. Written code or passing unit tests alone is not fully verified delivery when affected runtime behavior remains untested.
@@ -2,6 +2,60 @@
2
2
 
3
3
  Generated projects map `@app/*` to `src/*`; keep the `.js` suffix on source imports. Use the installed package types when an exact signature matters, and run `bun run check` to validate the discovered graph.
4
4
 
5
+ ## Choose a domain before choosing a folder
6
+
7
+ For work spanning business capabilities, map responsibility to services before adding artifacts. Record the rules/state each service owns, the operations/events it exposes, its dependencies, and who coordinates invariants that cross boundaries. Service names should describe those responsibilities, not merely repeat the application name.
8
+
9
+ For example, a commerce backend could use:
10
+
11
+ | Service | Owns | Public contract |
12
+ | --------------- | ----------------------------------------- | ------------------------------------ |
13
+ | `orders` | Order lifecycle and accepted order totals | Place/cancel order, `orders.created` |
14
+ | `pricing` | Price calculation and discount rules | Calculate a quote |
15
+ | `notifications` | Notification delivery and deduplication | React to an order event |
16
+
17
+ This is an example, not a required taxonomy. Related entities such as cars and car models may belong to one fleet service. Split by distinct rules and lifecycles, not by table count. One coherent capability can remain one service. A shared database, auth service, or tenant identifier is infrastructure reuse, not a reason to collect all business operations under one service.
18
+
19
+ ### Public collaboration
20
+
21
+ Each service selects its own operations; another service must not act as a barrel for its neighbor's internals:
22
+
23
+ ```ts
24
+ // src/services/pricing/service.ts
25
+ import { defineService } from "@relkit/app/services";
26
+ import calculateQuote from "./functions/calculate-quote.function.js";
27
+
28
+ export default defineService({ functions: { calculateQuote } });
29
+ ```
30
+
31
+ An orders function can call the public pricing contract. For a pure calculation that needs no provider state:
32
+
33
+ ```ts
34
+ import pricing from "@app/services/pricing/service.js";
35
+
36
+ // Inside an orders function handler:
37
+ const quote = await pricing.calculateQuote.invoke({ sku, quantity });
38
+ // Apply the order's acceptance rules to the validated quote.
39
+ ```
40
+
41
+ Use the installed declarations for the calculation's actual input/output. Do not import `pricing/functions/*`, call a foreign handler, or expose the same function as a member of two services. A route for orders targets the orders service; separate URLs alone do not establish domain boundaries.
42
+
43
+ For an asynchronous reaction, orders owns and exposes `orders.created`; a consumer under `src/services/notifications/functions/` uses `defineEventFunction({ id: "notifications.order-created", event: "orders.created", ... })`. Event-only consumers are discovered in their owning domain and are not added to its callable functions map. The event examples below show publication and subscription.
44
+
45
+ ### Invariants and transactions
46
+
47
+ Name one owner for a command's consistency rules. For example, accepting an order and reserving stock may require one atomic decision. A service call followed by a second write is not proof of an atomic transaction, and `.invoke()` must not be assumed to inherit the caller's transaction or resources.
48
+
49
+ Inspect installed transaction/invocation behavior before composing provider-backed commands. Choose a supported transaction coordinator or keep tightly coupled invariants within one cohesive owner. Define narrow public operations for other domains; do not duplicate another domain's rules or silently write its owned state from arbitrary helpers. Use events for reactions that can occur after commit, with an outbox when publication must survive a crash. Events alone do not preserve synchronous atomicity.
50
+
51
+ Keep one shared database service and its ordered migration history when the app uses one database. Do not create a database, auth service, or migration history per business domain.
52
+
53
+ Changing an existing operation's domain ownership is a redesign, not just a grouped/flat layout move. Review IDs, durable versions, policies, callers, and stored history explicitly; do not promise that a folder move between domains preserves identity.
54
+
55
+ ### Review the resulting boundaries
56
+
57
+ Compare the actual service graph and routes with the ownership map. Check that each planned capability has real operations under its owner, imports use public service boundaries, business rules live with their owner, and no catch-all service still contains the implementation behind empty facades. Review any merged or split responsibility and verify cross-domain failure/rollback behavior. A passing graph check proves structural validity, not semantic cohesion.
58
+
5
59
  ## `defineFunction` and `defineService`
6
60
 
7
61
  Put domain behavior in `src/services/<domain>/functions/*.function.ts`. A function validates input before its handler and validates the returned output:
@@ -62,13 +116,13 @@ export default orderCreated;
62
116
 
63
117
  Expose it through `defineService({ events: { orderCreated } })` when other domains need it. A publisher declares the exact ID in `publishes: ["orders.created"]` and calls `await context.events["orders.created"].publish({ orderId })`.
64
118
 
65
- An event-only consumer reacts without returning a result to its publisher:
119
+ An event-only consumer in `src/services/notifications/functions/` reacts without returning a result to its publisher:
66
120
 
67
121
  ```ts
68
122
  import { defineEventFunction } from "@relkit/app/events";
69
123
 
70
124
  export default defineEventFunction({
71
- id: "orders.confirmation",
125
+ id: "notifications.order-created",
72
126
  event: "orders.created",
73
127
  handler: async ({ orderId }, context) => {
74
128
  context.log.info("Order created", { orderId });
@@ -15,9 +15,9 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@langchain/langgraph": "1.4.14",
18
- "@relkit/app": "0.7.8",
19
- "@relkit/client": "0.7.8",
20
- "@relkit/local": "0.7.8",
18
+ "@relkit/app": "0.7.9",
19
+ "@relkit/client": "0.7.9",
20
+ "@relkit/local": "0.7.9",
21
21
  "@tanstack/react-query": "5.102.3",
22
22
  "effect": "4.0.1",
23
23
  "langchain": "1.5.10",
@@ -26,8 +26,8 @@
26
26
  "react-dom": "19.2.8"
27
27
  },
28
28
  "devDependencies": {
29
- "@relkit/cli": "0.7.8",
30
- "@relkit/testing": "0.7.8",
29
+ "@relkit/cli": "0.7.9",
30
+ "@relkit/testing": "0.7.9",
31
31
  "@types/bun": "1.3.10",
32
32
  "@types/react": "19.2.18",
33
33
  "@types/react-dom": "19.2.7",
@@ -21,6 +21,16 @@ Recommend optional modules only when they solve a concrete part of this task: fo
21
21
 
22
22
  Default to backend work. Do not introduce frontend pages, app.js, assets.css or demo interfaces unless requested. Preserve existing frontend files, explicitly selected fullstack functionality and the framework Inspector. For users and orders, define separate resource endpoints with explicit input/output contracts, validation and applicable authorization; avoid a public table dispatcher controlled by an entity parameter. Internal tables and infrastructure services need no public endpoint. Preserve intentional protocol handlers such as the Better Auth mount and shared internal helpers.
23
23
 
24
+ ## Design domain ownership
25
+
26
+ Before scaffolding business behavior, identify its owning domain. For a request spanning multiple business capabilities, include a compact ownership map in the implementation plan: service name, owned rules/state, public operations/events, dependencies, and any transaction spanning capabilities. Then proceed with authorized implementation; the map is not another approval gate.
27
+
28
+ A domain service owns one cohesive business responsibility and its invariants. Separate capabilities with distinct rules, lifecycles, or public contracts. Keep closely related entities together when they share those rules; do not create a service per table, endpoint, or noun. Shared authentication, database, tenant ID, or deployment does not make the entire application one domain. Avoid a product-named catch-all service containing unrelated capabilities.
29
+
30
+ For existing apps, reuse the service that owns the behavior. Do not reorganize unrelated domains or move existing code without task scope. A small single-domain feature needs only its existing owner, not a new architecture exercise.
31
+
32
+ Read [domain design and public contracts](references/domain-apis.md) when choosing boundaries or coordinating services. Name the owner of each invariant that spans domains and explain how atomic updates remain atomic. Do not assume a nested service invocation shares the caller's database transaction. Keep the shared database/auth singletons and one migration history; business boundaries do not require separate databases or deployments.
33
+
24
34
  ## Source placement
25
35
 
26
36
  New projects place every generic, database, and auth boundary at `src/services/<name>/service.ts`. Keep its functions, events, tasks, jobs, tools, agents, channels, schemas, resources, helpers, and assets under that service. Routes live at `src/routes`; configuration support and environment contracts live at `src/platform`. Application descriptors use plain TypeScript and supported public APIs; Effect imports are not required.
@@ -59,6 +69,8 @@ Read only the reference relevant to the requested change. These examples describ
59
69
 
60
70
  ## Verify the result
61
71
 
72
+ Compare the final services and route targets with the ownership map. Confirm each capability has real operations under its owner, cross-service imports use public contracts, and no catch-all implementation hides behind empty services. Explain deliberate boundary changes and verify cross-domain consistency/failure behavior. A passing graph check validates structure, not domain cohesion.
73
+
62
74
  Review the CLI's created and updated files, warnings, next steps and final diff. Run focused checks after logical changes. Before completion, run applicable check, typecheck, build, unit/integration tests and existing relevant end-to-end coverage. Inspect package.json for actual script names. For HTTP work, test the route's status/body, valid and invalid input, and denied and allowed access where relevant; a direct function test does not cover request mapping or the complete flow. Replay the original bug through the boundary that failed. Backend/API tests do not require an unsolicited frontend.
63
75
 
64
76
  Database/auth changes also need explicit environment setup and migration steps from [the data reference](references/data-apis.md), followed by persistence/session/authorization verification. Stop all test-owned servers and clients. Report what changed, exact verification performed, and every failed, skipped or blocked check with its effect on confidence. Written code or passing unit tests alone is not fully verified delivery when affected runtime behavior remains untested.
@@ -2,6 +2,60 @@
2
2
 
3
3
  Generated projects map `@app/*` to `src/*`; keep the `.js` suffix on source imports. Use the installed package types when an exact signature matters, and run `bun run check` to validate the discovered graph.
4
4
 
5
+ ## Choose a domain before choosing a folder
6
+
7
+ For work spanning business capabilities, map responsibility to services before adding artifacts. Record the rules/state each service owns, the operations/events it exposes, its dependencies, and who coordinates invariants that cross boundaries. Service names should describe those responsibilities, not merely repeat the application name.
8
+
9
+ For example, a commerce backend could use:
10
+
11
+ | Service | Owns | Public contract |
12
+ | --------------- | ----------------------------------------- | ------------------------------------ |
13
+ | `orders` | Order lifecycle and accepted order totals | Place/cancel order, `orders.created` |
14
+ | `pricing` | Price calculation and discount rules | Calculate a quote |
15
+ | `notifications` | Notification delivery and deduplication | React to an order event |
16
+
17
+ This is an example, not a required taxonomy. Related entities such as cars and car models may belong to one fleet service. Split by distinct rules and lifecycles, not by table count. One coherent capability can remain one service. A shared database, auth service, or tenant identifier is infrastructure reuse, not a reason to collect all business operations under one service.
18
+
19
+ ### Public collaboration
20
+
21
+ Each service selects its own operations; another service must not act as a barrel for its neighbor's internals:
22
+
23
+ ```ts
24
+ // src/services/pricing/service.ts
25
+ import { defineService } from "@relkit/app/services";
26
+ import calculateQuote from "./functions/calculate-quote.function.js";
27
+
28
+ export default defineService({ functions: { calculateQuote } });
29
+ ```
30
+
31
+ An orders function can call the public pricing contract. For a pure calculation that needs no provider state:
32
+
33
+ ```ts
34
+ import pricing from "@app/services/pricing/service.js";
35
+
36
+ // Inside an orders function handler:
37
+ const quote = await pricing.calculateQuote.invoke({ sku, quantity });
38
+ // Apply the order's acceptance rules to the validated quote.
39
+ ```
40
+
41
+ Use the installed declarations for the calculation's actual input/output. Do not import `pricing/functions/*`, call a foreign handler, or expose the same function as a member of two services. A route for orders targets the orders service; separate URLs alone do not establish domain boundaries.
42
+
43
+ For an asynchronous reaction, orders owns and exposes `orders.created`; a consumer under `src/services/notifications/functions/` uses `defineEventFunction({ id: "notifications.order-created", event: "orders.created", ... })`. Event-only consumers are discovered in their owning domain and are not added to its callable functions map. The event examples below show publication and subscription.
44
+
45
+ ### Invariants and transactions
46
+
47
+ Name one owner for a command's consistency rules. For example, accepting an order and reserving stock may require one atomic decision. A service call followed by a second write is not proof of an atomic transaction, and `.invoke()` must not be assumed to inherit the caller's transaction or resources.
48
+
49
+ Inspect installed transaction/invocation behavior before composing provider-backed commands. Choose a supported transaction coordinator or keep tightly coupled invariants within one cohesive owner. Define narrow public operations for other domains; do not duplicate another domain's rules or silently write its owned state from arbitrary helpers. Use events for reactions that can occur after commit, with an outbox when publication must survive a crash. Events alone do not preserve synchronous atomicity.
50
+
51
+ Keep one shared database service and its ordered migration history when the app uses one database. Do not create a database, auth service, or migration history per business domain.
52
+
53
+ Changing an existing operation's domain ownership is a redesign, not just a grouped/flat layout move. Review IDs, durable versions, policies, callers, and stored history explicitly; do not promise that a folder move between domains preserves identity.
54
+
55
+ ### Review the resulting boundaries
56
+
57
+ Compare the actual service graph and routes with the ownership map. Check that each planned capability has real operations under its owner, imports use public service boundaries, business rules live with their owner, and no catch-all service still contains the implementation behind empty facades. Review any merged or split responsibility and verify cross-domain failure/rollback behavior. A passing graph check proves structural validity, not semantic cohesion.
58
+
5
59
  ## `defineFunction` and `defineService`
6
60
 
7
61
  Put domain behavior in `src/services/<domain>/functions/*.function.ts`. A function validates input before its handler and validates the returned output:
@@ -62,13 +116,13 @@ export default orderCreated;
62
116
 
63
117
  Expose it through `defineService({ events: { orderCreated } })` when other domains need it. A publisher declares the exact ID in `publishes: ["orders.created"]` and calls `await context.events["orders.created"].publish({ orderId })`.
64
118
 
65
- An event-only consumer reacts without returning a result to its publisher:
119
+ An event-only consumer in `src/services/notifications/functions/` reacts without returning a result to its publisher:
66
120
 
67
121
  ```ts
68
122
  import { defineEventFunction } from "@relkit/app/events";
69
123
 
70
124
  export default defineEventFunction({
71
- id: "orders.confirmation",
125
+ id: "notifications.order-created",
72
126
  event: "orders.created",
73
127
  handler: async ({ orderId }, context) => {
74
128
  context.log.info("Order created", { orderId });
@@ -15,13 +15,13 @@
15
15
  "graph": "relkit graph print"
16
16
  },
17
17
  "dependencies": {
18
- "@relkit/app": "0.7.8",
18
+ "@relkit/app": "0.7.9",
19
19
  "effect": "4.0.1"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/bun": "1.3.10",
23
- "@relkit/cli": "0.7.8",
24
- "@relkit/testing": "0.7.8",
23
+ "@relkit/cli": "0.7.9",
24
+ "@relkit/testing": "0.7.9",
25
25
  "typescript": "5.9.3"
26
26
  }
27
27
  }
@@ -21,6 +21,16 @@ Recommend optional modules only when they solve a concrete part of this task: fo
21
21
 
22
22
  Default to backend work. Do not introduce frontend pages, app.js, assets.css or demo interfaces unless requested. Preserve existing frontend files, explicitly selected fullstack functionality and the framework Inspector. For users and orders, define separate resource endpoints with explicit input/output contracts, validation and applicable authorization; avoid a public table dispatcher controlled by an entity parameter. Internal tables and infrastructure services need no public endpoint. Preserve intentional protocol handlers such as the Better Auth mount and shared internal helpers.
23
23
 
24
+ ## Design domain ownership
25
+
26
+ Before scaffolding business behavior, identify its owning domain. For a request spanning multiple business capabilities, include a compact ownership map in the implementation plan: service name, owned rules/state, public operations/events, dependencies, and any transaction spanning capabilities. Then proceed with authorized implementation; the map is not another approval gate.
27
+
28
+ A domain service owns one cohesive business responsibility and its invariants. Separate capabilities with distinct rules, lifecycles, or public contracts. Keep closely related entities together when they share those rules; do not create a service per table, endpoint, or noun. Shared authentication, database, tenant ID, or deployment does not make the entire application one domain. Avoid a product-named catch-all service containing unrelated capabilities.
29
+
30
+ For existing apps, reuse the service that owns the behavior. Do not reorganize unrelated domains or move existing code without task scope. A small single-domain feature needs only its existing owner, not a new architecture exercise.
31
+
32
+ Read [domain design and public contracts](references/domain-apis.md) when choosing boundaries or coordinating services. Name the owner of each invariant that spans domains and explain how atomic updates remain atomic. Do not assume a nested service invocation shares the caller's database transaction. Keep the shared database/auth singletons and one migration history; business boundaries do not require separate databases or deployments.
33
+
24
34
  ## Source placement
25
35
 
26
36
  New projects place every generic, database, and auth boundary at `src/services/<name>/service.ts`. Keep its functions, events, tasks, jobs, tools, agents, channels, schemas, resources, helpers, and assets under that service. Routes live at `src/routes`; configuration support and environment contracts live at `src/platform`. Application descriptors use plain TypeScript and supported public APIs; Effect imports are not required.
@@ -59,6 +69,8 @@ Read only the reference relevant to the requested change. These examples describ
59
69
 
60
70
  ## Verify the result
61
71
 
72
+ Compare the final services and route targets with the ownership map. Confirm each capability has real operations under its owner, cross-service imports use public contracts, and no catch-all implementation hides behind empty services. Explain deliberate boundary changes and verify cross-domain consistency/failure behavior. A passing graph check validates structure, not domain cohesion.
73
+
62
74
  Review the CLI's created and updated files, warnings, next steps and final diff. Run focused checks after logical changes. Before completion, run applicable check, typecheck, build, unit/integration tests and existing relevant end-to-end coverage. Inspect package.json for actual script names. For HTTP work, test the route's status/body, valid and invalid input, and denied and allowed access where relevant; a direct function test does not cover request mapping or the complete flow. Replay the original bug through the boundary that failed. Backend/API tests do not require an unsolicited frontend.
63
75
 
64
76
  Database/auth changes also need explicit environment setup and migration steps from [the data reference](references/data-apis.md), followed by persistence/session/authorization verification. Stop all test-owned servers and clients. Report what changed, exact verification performed, and every failed, skipped or blocked check with its effect on confidence. Written code or passing unit tests alone is not fully verified delivery when affected runtime behavior remains untested.
@@ -2,6 +2,60 @@
2
2
 
3
3
  Generated projects map `@app/*` to `src/*`; keep the `.js` suffix on source imports. Use the installed package types when an exact signature matters, and run `bun run check` to validate the discovered graph.
4
4
 
5
+ ## Choose a domain before choosing a folder
6
+
7
+ For work spanning business capabilities, map responsibility to services before adding artifacts. Record the rules/state each service owns, the operations/events it exposes, its dependencies, and who coordinates invariants that cross boundaries. Service names should describe those responsibilities, not merely repeat the application name.
8
+
9
+ For example, a commerce backend could use:
10
+
11
+ | Service | Owns | Public contract |
12
+ | --------------- | ----------------------------------------- | ------------------------------------ |
13
+ | `orders` | Order lifecycle and accepted order totals | Place/cancel order, `orders.created` |
14
+ | `pricing` | Price calculation and discount rules | Calculate a quote |
15
+ | `notifications` | Notification delivery and deduplication | React to an order event |
16
+
17
+ This is an example, not a required taxonomy. Related entities such as cars and car models may belong to one fleet service. Split by distinct rules and lifecycles, not by table count. One coherent capability can remain one service. A shared database, auth service, or tenant identifier is infrastructure reuse, not a reason to collect all business operations under one service.
18
+
19
+ ### Public collaboration
20
+
21
+ Each service selects its own operations; another service must not act as a barrel for its neighbor's internals:
22
+
23
+ ```ts
24
+ // src/services/pricing/service.ts
25
+ import { defineService } from "@relkit/app/services";
26
+ import calculateQuote from "./functions/calculate-quote.function.js";
27
+
28
+ export default defineService({ functions: { calculateQuote } });
29
+ ```
30
+
31
+ An orders function can call the public pricing contract. For a pure calculation that needs no provider state:
32
+
33
+ ```ts
34
+ import pricing from "@app/services/pricing/service.js";
35
+
36
+ // Inside an orders function handler:
37
+ const quote = await pricing.calculateQuote.invoke({ sku, quantity });
38
+ // Apply the order's acceptance rules to the validated quote.
39
+ ```
40
+
41
+ Use the installed declarations for the calculation's actual input/output. Do not import `pricing/functions/*`, call a foreign handler, or expose the same function as a member of two services. A route for orders targets the orders service; separate URLs alone do not establish domain boundaries.
42
+
43
+ For an asynchronous reaction, orders owns and exposes `orders.created`; a consumer under `src/services/notifications/functions/` uses `defineEventFunction({ id: "notifications.order-created", event: "orders.created", ... })`. Event-only consumers are discovered in their owning domain and are not added to its callable functions map. The event examples below show publication and subscription.
44
+
45
+ ### Invariants and transactions
46
+
47
+ Name one owner for a command's consistency rules. For example, accepting an order and reserving stock may require one atomic decision. A service call followed by a second write is not proof of an atomic transaction, and `.invoke()` must not be assumed to inherit the caller's transaction or resources.
48
+
49
+ Inspect installed transaction/invocation behavior before composing provider-backed commands. Choose a supported transaction coordinator or keep tightly coupled invariants within one cohesive owner. Define narrow public operations for other domains; do not duplicate another domain's rules or silently write its owned state from arbitrary helpers. Use events for reactions that can occur after commit, with an outbox when publication must survive a crash. Events alone do not preserve synchronous atomicity.
50
+
51
+ Keep one shared database service and its ordered migration history when the app uses one database. Do not create a database, auth service, or migration history per business domain.
52
+
53
+ Changing an existing operation's domain ownership is a redesign, not just a grouped/flat layout move. Review IDs, durable versions, policies, callers, and stored history explicitly; do not promise that a folder move between domains preserves identity.
54
+
55
+ ### Review the resulting boundaries
56
+
57
+ Compare the actual service graph and routes with the ownership map. Check that each planned capability has real operations under its owner, imports use public service boundaries, business rules live with their owner, and no catch-all service still contains the implementation behind empty facades. Review any merged or split responsibility and verify cross-domain failure/rollback behavior. A passing graph check proves structural validity, not semantic cohesion.
58
+
5
59
  ## `defineFunction` and `defineService`
6
60
 
7
61
  Put domain behavior in `src/services/<domain>/functions/*.function.ts`. A function validates input before its handler and validates the returned output:
@@ -62,13 +116,13 @@ export default orderCreated;
62
116
 
63
117
  Expose it through `defineService({ events: { orderCreated } })` when other domains need it. A publisher declares the exact ID in `publishes: ["orders.created"]` and calls `await context.events["orders.created"].publish({ orderId })`.
64
118
 
65
- An event-only consumer reacts without returning a result to its publisher:
119
+ An event-only consumer in `src/services/notifications/functions/` reacts without returning a result to its publisher:
66
120
 
67
121
  ```ts
68
122
  import { defineEventFunction } from "@relkit/app/events";
69
123
 
70
124
  export default defineEventFunction({
71
- id: "orders.confirmation",
125
+ id: "notifications.order-created",
72
126
  event: "orders.created",
73
127
  handler: async ({ orderId }, context) => {
74
128
  context.log.info("Order created", { orderId });
@@ -13,13 +13,13 @@
13
13
  "jobs:trigger": "relkit jobs trigger --job exportOrders --input-file examples/export-orders.json --operation-id example-trigger"
14
14
  },
15
15
  "dependencies": {
16
- "@relkit/app": "0.7.8",
16
+ "@relkit/app": "0.7.9",
17
17
  "effect": "4.0.1"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@types/bun": "1.3.10",
21
- "@relkit/cli": "0.7.8",
22
- "@relkit/testing": "0.7.8",
21
+ "@relkit/cli": "0.7.9",
22
+ "@relkit/testing": "0.7.9",
23
23
  "typescript": "5.9.3"
24
24
  }
25
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-relkit",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "Create a RELKIT application from a supported project template.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -44,10 +44,10 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@clack/prompts": "1.7.0",
47
- "@relkit/compiler": "0.7.8",
47
+ "@relkit/compiler": "0.7.9",
48
48
  "typescript": "5.9.3",
49
49
  "effect": "4.0.1",
50
- "@relkit/contracts": "0.7.8"
50
+ "@relkit/contracts": "0.7.9"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@effect/vitest": "4.0.1",