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 CHANGED
@@ -174,13 +174,25 @@ tags.getRemovedItems() // → [existingTag]
174
174
  ### Domain Events — decouple side effects
175
175
 
176
176
  ```ts
177
- import { DomainEvents } from 'archstone/domain/enterprise'
177
+ import type { EventHandler } from 'archstone/core'
178
+ import { DomainEvents } from 'archstone/core'
178
179
 
179
- // Register handlers anywhere in your infrastructure layer
180
- DomainEvents.register(
181
- (event) => sendWelcomeEmail(event as UserCreatedEvent),
182
- UserCreatedEvent.name,
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`, `EventHandler` |
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.
@@ -1,2 +1,2 @@
1
- import { DomainEvent, DomainEvents, Either, EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList, left, right } from "../shared/chunk-rgs8z093.js";
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-rgs8z093.js";
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-rgs8z093.js";
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-s2r7zghq.js";import{e as a}from"../../shared/chunk-8hx7pxm3.js";export{b as Entity,a as DomainEvents,c as AggregateRoot};
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};
@@ -1,2 +1,2 @@
1
- import { AggregateRoot, Creatable, Deletable, DomainEvent, DomainEvents, Entity, EventHandler, Findable, Repository, Saveable, UseCase, UseCaseError } from "../shared/chunk-rgs8z093.js";
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 };
@@ -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-s2r7zghq.js";import{e as a}from"../shared/chunk-8hx7pxm3.js";export{b as Entity,a as DomainEvents,c as AggregateRoot};
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, EventHandler, Findable, Optional, Repository, Saveable, UniqueEntityId, UseCase, UseCaseError, ValueObject, WatchedList, left, right } from "./shared/chunk-rgs8z093.js";
2
- export { right, left, WatchedList, ValueObject, UseCaseError, UseCase, UniqueEntityId, Saveable, Repository, Optional, Findable, EventHandler, Entity, Either, DomainEvents, DomainEvent, Deletable, Creatable, AggregateRoot };
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-s2r7zghq.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};
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 = (event: DomainEvent) => void;
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 sending emails,
383
- * updating read models, or triggering external integrations.
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
- * All handlers must implement {@link setupSubscriptions} to register
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
- * (event) => this.sendWelcomeEmail(event as UserCreatedEvent),
414
+ * this.handle.bind(this),
396
415
  * UserCreatedEvent.name,
397
416
  * )
398
417
  * }
399
418
  *
400
- * private async sendWelcomeEmail(event: UserCreatedEvent): Promise<void> {
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 EventHandler {
425
+ interface EventHandler2<T extends DomainEvent> {
407
426
  /**
408
427
  * Registers all event subscriptions for this handler.
409
- * Should be called once during application bootstrap.
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, EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList };
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "archstone",
3
3
  "description": "TypeScript architecture foundation for backend services based on Domain-Driven Design and Clean Architecture",
4
- "version": "1.3.0-rc.2",
4
+ "version": "1.3.0-rc.4",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "files": [
@@ -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(): void`
8
- - Register handlers in the infrastructure composition root before the first request
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/domain/enterprise'
32
- import { DomainEvents } from 'archstone/domain/enterprise'
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
- (event) => this.handle(event as UserCreatedEvent),
41
+ this.handle.bind(this),
40
42
  UserCreatedEvent.name,
41
43
  )
42
44
  }
43
45
 
44
- private async handle(event: UserCreatedEvent): Promise<void> {
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
- // Called once at app startupin infrastructure, never in domain
63
- new OnUserCreated(mailer).setupSubscriptions()
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};