archstone 1.3.0-rc.2 → 1.3.0-rc.4
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.
- package/README.md +20 -8
- package/dist/core/index.d.ts +1 -1
- package/dist/domain/application/index.d.ts +1 -1
- package/dist/domain/enterprise/index.d.ts +1 -1
- package/dist/domain/enterprise/index.js +1 -1
- package/dist/domain/index.d.ts +1 -1
- package/dist/domain/index.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/shared/chunk-k39ksk62.js +2 -0
- package/dist/shared/{chunk-rgs8z093.d.ts → chunk-z9br464b.d.ts} +41 -13
- package/package.json +1 -1
- package/skills/use-archstone/references/domain-events.md +12 -10
- package/dist/shared/chunk-s2r7zghq.js +0 -2
package/README.md
CHANGED
|
@@ -174,13 +174,25 @@ tags.getRemovedItems() // → [existingTag]
|
|
|
174
174
|
### Domain Events — decouple side effects
|
|
175
175
|
|
|
176
176
|
```ts
|
|
177
|
-
import {
|
|
177
|
+
import type { EventHandler } from 'archstone/core'
|
|
178
|
+
import { DomainEvents } from 'archstone/core'
|
|
178
179
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
class OnUserCreated implements EventHandler<UserCreatedEvent> {
|
|
181
|
+
constructor(private readonly mailer: Mailer) {
|
|
182
|
+
this.setupSubscriptions()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
setupSubscriptions(): void {
|
|
186
|
+
DomainEvents.register(this.handle.bind(this), UserCreatedEvent.name)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async handle(event: UserCreatedEvent): Promise<void> {
|
|
190
|
+
await this.mailer.send(event.user.email.value)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Instantiate in infrastructure — handler self-registers via constructor
|
|
195
|
+
new OnUserCreated(mailer)
|
|
184
196
|
|
|
185
197
|
// Dispatch after persistence — events stay inside the aggregate until then
|
|
186
198
|
await userRepository.create(user)
|
|
@@ -212,9 +224,9 @@ export interface AuditRepository extends Creatable<AuditLog> {}
|
|
|
212
224
|
| Import | Contents |
|
|
213
225
|
|---|---|
|
|
214
226
|
| `archstone` | Everything |
|
|
215
|
-
| `archstone/core` | `Either`, `ValueObject`, `UniqueEntityId`, `WatchedList`, `Optional` |
|
|
227
|
+
| `archstone/core` | `Either`, `ValueObject`, `UniqueEntityId`, `WatchedList`, `Optional`, `EventHandler` |
|
|
216
228
|
| `archstone/domain` | All domain exports |
|
|
217
|
-
| `archstone/domain/enterprise` | `Entity`, `AggregateRoot`, `DomainEvent`, `DomainEvents
|
|
229
|
+
| `archstone/domain/enterprise` | `Entity`, `AggregateRoot`, `DomainEvent`, `DomainEvents` |
|
|
218
230
|
| `archstone/domain/application` | `UseCase`, `UseCaseError`, repository contracts |
|
|
219
231
|
|
|
220
232
|
All sub-paths share type declarations via a common chunk — mixing imports from multiple sub-paths is fully type-safe with no duplicate declaration conflicts.
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DomainEvent, DomainEvents, Either, EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList, left, right } from "../shared/chunk-
|
|
1
|
+
import { DomainEvent, DomainEvents, Either, EventHandler1 as EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList, left, right } from "../shared/chunk-z9br464b.js";
|
|
2
2
|
export { right, left, WatchedList, ValueObject, UniqueEntityId, Optional, EventHandler, Either, DomainEvents, DomainEvent };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "../../shared/chunk-
|
|
1
|
+
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "../../shared/chunk-z9br464b.js";
|
|
2
2
|
export { UseCaseError, UseCase, Saveable, Repository, Findable, Deletable, Creatable };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AggregateRoot, DomainEvent, DomainEvents, Entity, EventHandler } from "../../shared/chunk-
|
|
1
|
+
import { AggregateRoot, DomainEvent, DomainEvents, Entity, EventHandler } from "../../shared/chunk-z9br464b.js";
|
|
2
2
|
export { EventHandler, Entity, DomainEvents, DomainEvent, AggregateRoot };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{a as b,b as c}from"../../shared/chunk-
|
|
2
|
+
import{a as b,b as c}from"../../shared/chunk-k39ksk62.js";import{e as a}from"../../shared/chunk-8hx7pxm3.js";export{b as Entity,a as DomainEvents,c as AggregateRoot};
|
package/dist/domain/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AggregateRoot, Creatable, Deletable, DomainEvent, DomainEvents, Entity, EventHandler, Findable, Repository, Saveable, UseCase, UseCaseError } from "../shared/chunk-
|
|
1
|
+
import { AggregateRoot, Creatable, Deletable, DomainEvent, DomainEvents, Entity, EventHandler, Findable, Repository, Saveable, UseCase, UseCaseError } from "../shared/chunk-z9br464b.js";
|
|
2
2
|
export { UseCaseError, UseCase, Saveable, Repository, Findable, EventHandler, Entity, DomainEvents, DomainEvent, Deletable, Creatable, AggregateRoot };
|
package/dist/domain/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import"../shared/chunk-x296z7h1.js";import"../shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"../shared/chunk-
|
|
2
|
+
import"../shared/chunk-x296z7h1.js";import"../shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"../shared/chunk-k39ksk62.js";import{e as a}from"../shared/chunk-8hx7pxm3.js";export{b as Entity,a as DomainEvents,c as AggregateRoot};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AggregateRoot, Creatable, Deletable, DomainEvent, DomainEvents, Either, Entity,
|
|
2
|
-
export { right, left, WatchedList, ValueObject, UseCaseError, UseCase, UniqueEntityId, Saveable, Repository, Optional, Findable,
|
|
1
|
+
import { AggregateRoot, Creatable, Deletable, DomainEvent, DomainEvents, Either, Entity, Findable, Optional, Repository, Saveable, UniqueEntityId, UseCase, UseCaseError, ValueObject, WatchedList, left, right } from "./shared/chunk-z9br464b.js";
|
|
2
|
+
export { right, left, WatchedList, ValueObject, UseCaseError, UseCase, UniqueEntityId, Saveable, Repository, Optional, Findable, Entity, Either, DomainEvents, DomainEvent, Deletable, Creatable, AggregateRoot };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import"./shared/chunk-x296z7h1.js";import"./shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"./shared/chunk-
|
|
2
|
+
import"./shared/chunk-x296z7h1.js";import"./shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"./shared/chunk-k39ksk62.js";import{c as f,d as m,e as p,f as t,g as x,h as a}from"./shared/chunk-8hx7pxm3.js";export{m as right,f as left,a as WatchedList,x as ValueObject,t as UniqueEntityId,b as Entity,p as DomainEvents,c as AggregateRoot};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{e as j,f as k}from"./chunk-8hx7pxm3.js";class R{_id;props;get id(){return this._id}constructor(A,B){this._id=B??new k,this.props=A}equals(A){if(A===this)return!0;return A.id.equals(this._id)}}class z extends R{_domainEvents=new Set;get domainEvents(){return Array.from(this._domainEvents)}addDomainEvent(A){this._domainEvents.add(A),j.markAggregateForDispatch(this)}clearEvents(){this._domainEvents.clear()}}export{R as a,z as b};
|
|
@@ -305,9 +305,22 @@ declare abstract class AggregateRoot<Props> extends Entity<Props> {
|
|
|
305
305
|
clearEvents(): void;
|
|
306
306
|
}
|
|
307
307
|
/**
|
|
308
|
+
* @deprecated use `EventHandler` from `archstone/core` instead.
|
|
309
|
+
* The new version supports generic typing via `EventHandler<T>`.
|
|
310
|
+
*/
|
|
311
|
+
interface EventHandler {
|
|
312
|
+
setupSubscriptions(): void;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
308
315
|
* Callback function invoked when a domain event is dispatched.
|
|
316
|
+
*
|
|
317
|
+
* The bivariant method signature allows handlers typed to a specific
|
|
318
|
+
* `DomainEvent` subtype to be registered — TypeScript enforces strict
|
|
319
|
+
* contravariance on function types but not on method signatures.
|
|
309
320
|
*/
|
|
310
|
-
type DomainEventCallback =
|
|
321
|
+
type DomainEventCallback = {
|
|
322
|
+
bivarianceHack(event: DomainEvent): Promise<void>;
|
|
323
|
+
}["bivarianceHack"];
|
|
311
324
|
/**
|
|
312
325
|
* Central registry and dispatcher for domain events.
|
|
313
326
|
*
|
|
@@ -376,39 +389,54 @@ declare class DomainEventsImplementation {
|
|
|
376
389
|
*/
|
|
377
390
|
declare const DomainEvents: DomainEventsImplementation;
|
|
378
391
|
/**
|
|
379
|
-
* Base contract for all event handlers.
|
|
392
|
+
* Base contract for all domain event handlers.
|
|
380
393
|
*
|
|
381
394
|
* An event handler is responsible for subscribing to domain events
|
|
382
|
-
* and executing side effects in response — such as
|
|
383
|
-
*
|
|
395
|
+
* and executing side effects in response — such as persisting records,
|
|
396
|
+
* broadcasting WebSocket messages, sending emails, or triggering
|
|
397
|
+
* external integrations.
|
|
398
|
+
*
|
|
399
|
+
* Implement {@link setupSubscriptions} to register callbacks in the
|
|
400
|
+
* {@link DomainEvents} registry, and {@link handle} to define the
|
|
401
|
+
* reaction logic for the subscribed event.
|
|
384
402
|
*
|
|
385
|
-
*
|
|
386
|
-
* their callbacks in the {@link DomainEvents} registry.
|
|
403
|
+
* @typeParam T - The specific {@link DomainEvent} this handler reacts to.
|
|
387
404
|
*
|
|
388
405
|
* @example
|
|
389
406
|
* ```ts
|
|
390
|
-
* class OnUserCreated implements EventHandler {
|
|
391
|
-
* constructor(private readonly mailer: Mailer) {
|
|
407
|
+
* class OnUserCreated implements EventHandler<UserCreatedEvent> {
|
|
408
|
+
* constructor(private readonly mailer: Mailer) {
|
|
409
|
+
* this.setupSubscriptions()
|
|
410
|
+
* }
|
|
392
411
|
*
|
|
393
412
|
* setupSubscriptions(): void {
|
|
394
413
|
* DomainEvents.register(
|
|
395
|
-
*
|
|
414
|
+
* this.handle.bind(this),
|
|
396
415
|
* UserCreatedEvent.name,
|
|
397
416
|
* )
|
|
398
417
|
* }
|
|
399
418
|
*
|
|
400
|
-
*
|
|
419
|
+
* async handle(event: UserCreatedEvent): Promise<void> {
|
|
401
420
|
* await this.mailer.send(event.user.email)
|
|
402
421
|
* }
|
|
403
422
|
* }
|
|
404
423
|
* ```
|
|
405
424
|
*/
|
|
406
|
-
interface
|
|
425
|
+
interface EventHandler2<T extends DomainEvent> {
|
|
407
426
|
/**
|
|
408
427
|
* Registers all event subscriptions for this handler.
|
|
409
|
-
*
|
|
428
|
+
*
|
|
429
|
+
* Should be called once during instantiation — typically
|
|
430
|
+
* in the constructor — to ensure the handler is active
|
|
431
|
+
* before any events are dispatched.
|
|
410
432
|
*/
|
|
411
433
|
setupSubscriptions(): void;
|
|
434
|
+
/**
|
|
435
|
+
* Executes the handler logic in response to a dispatched event.
|
|
436
|
+
*
|
|
437
|
+
* @param event - The domain event instance that was dispatched.
|
|
438
|
+
*/
|
|
439
|
+
handle(event: T): Promise<void>;
|
|
412
440
|
}
|
|
413
441
|
/**
|
|
414
442
|
* Make some property optional on type
|
|
@@ -634,4 +662,4 @@ interface UseCase<
|
|
|
634
662
|
> {
|
|
635
663
|
execute(input: Input): Promise<Output>;
|
|
636
664
|
}
|
|
637
|
-
export { Either, left, right, DomainEvent, Creatable, Deletable, Findable, Saveable, Repository, UseCaseError, UseCase, Entity, AggregateRoot, DomainEvents,
|
|
665
|
+
export { Either, left, right, DomainEvent, Creatable, Deletable, Findable, Saveable, Repository, UseCaseError, UseCase, Entity, AggregateRoot, EventHandler, DomainEvents, EventHandler2 as EventHandler1, Optional, UniqueEntityId, ValueObject, WatchedList };
|
package/package.json
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
- Raise events inside the aggregate via `this.addDomainEvent()`
|
|
6
6
|
- Dispatch **after** successful persistence — never before
|
|
7
|
-
- Define handlers as classes implementing `EventHandler` with `setupSubscriptions():
|
|
8
|
-
-
|
|
7
|
+
- Define handlers as classes implementing `EventHandler<T>` from `archstone/core` with `setupSubscriptions()` and `handle(event: T)`
|
|
8
|
+
- Call `setupSubscriptions()` in the constructor — just instantiate the handler in the composition root
|
|
9
9
|
- Dispatch via `DomainEvents.dispatchEventsForAggregate(aggregate.id)` — argument is `UniqueEntityId`
|
|
10
10
|
- `clearEvents()` is called internally by `dispatchEventsForAggregate` — do not call manually
|
|
11
11
|
- Test isolation: call `DomainEvents.clearHandlers()` and `DomainEvents.clearMarkedAggregates()` in `beforeEach`
|
|
@@ -28,20 +28,22 @@ class User extends AggregateRoot<UserProps> {
|
|
|
28
28
|
## Defining a Handler
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
|
-
import type { EventHandler } from 'archstone/
|
|
32
|
-
import { DomainEvents } from 'archstone/
|
|
31
|
+
import type { EventHandler } from 'archstone/core'
|
|
32
|
+
import { DomainEvents } from 'archstone/core'
|
|
33
33
|
|
|
34
|
-
class OnUserCreated implements EventHandler {
|
|
35
|
-
constructor(private readonly mailer: Mailer) {
|
|
34
|
+
class OnUserCreated implements EventHandler<UserCreatedEvent> {
|
|
35
|
+
constructor(private readonly mailer: Mailer) {
|
|
36
|
+
this.setupSubscriptions()
|
|
37
|
+
}
|
|
36
38
|
|
|
37
39
|
setupSubscriptions(): void {
|
|
38
40
|
DomainEvents.register(
|
|
39
|
-
|
|
41
|
+
this.handle.bind(this),
|
|
40
42
|
UserCreatedEvent.name,
|
|
41
43
|
)
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
async handle(event: UserCreatedEvent): Promise<void> {
|
|
45
47
|
await this.mailer.send(event.user.email.value)
|
|
46
48
|
}
|
|
47
49
|
}
|
|
@@ -59,8 +61,8 @@ async create(user: User): Promise<void> {
|
|
|
59
61
|
## Composition Root Registration
|
|
60
62
|
|
|
61
63
|
```ts
|
|
62
|
-
//
|
|
63
|
-
new OnUserCreated(mailer)
|
|
64
|
+
// setupSubscriptions() is called in the constructor — just instantiate
|
|
65
|
+
new OnUserCreated(mailer)
|
|
64
66
|
```
|
|
65
67
|
|
|
66
68
|
## Common Mistakes
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{e as D,f as H}from"./chunk-8hx7pxm3.js";class A{_id;props;get id(){return this._id}constructor(x,f){this._id=f??new H,this.props=x}equals(x){if(x===this)return!0;return x.id.equals(this._id)}}class R extends A{_domainEvents=new Set;get domainEvents(){return Array.from(this._domainEvents)}addDomainEvent(x){this._domainEvents.add(x),D.markAggregateForDispatch(this)}clearEvents(){this._domainEvents.clear()}}export{A as a,R as b};
|