archstone 1.3.0-rc.2 → 1.3.0-rc.3

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-v5h13a5p.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-v5h13a5p.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-v5h13a5p.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-v5h13a5p.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-v5h13a5p.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,6 +305,13 @@ 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.
309
316
  */
310
317
  type DomainEventCallback = (event: DomainEvent) => void;
@@ -376,39 +383,54 @@ declare class DomainEventsImplementation {
376
383
  */
377
384
  declare const DomainEvents: DomainEventsImplementation;
378
385
  /**
379
- * Base contract for all event handlers.
386
+ * Base contract for all domain event handlers.
380
387
  *
381
388
  * 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.
389
+ * and executing side effects in response — such as persisting records,
390
+ * broadcasting WebSocket messages, sending emails, or triggering
391
+ * external integrations.
392
+ *
393
+ * Implement {@link setupSubscriptions} to register callbacks in the
394
+ * {@link DomainEvents} registry, and {@link handle} to define the
395
+ * reaction logic for the subscribed event.
384
396
  *
385
- * All handlers must implement {@link setupSubscriptions} to register
386
- * their callbacks in the {@link DomainEvents} registry.
397
+ * @typeParam T - The specific {@link DomainEvent} this handler reacts to.
387
398
  *
388
399
  * @example
389
400
  * ```ts
390
- * class OnUserCreated implements EventHandler {
391
- * constructor(private readonly mailer: Mailer) {}
401
+ * class OnUserCreated implements EventHandler<UserCreatedEvent> {
402
+ * constructor(private readonly mailer: Mailer) {
403
+ * this.setupSubscriptions()
404
+ * }
392
405
  *
393
406
  * setupSubscriptions(): void {
394
407
  * DomainEvents.register(
395
- * (event) => this.sendWelcomeEmail(event as UserCreatedEvent),
408
+ * this.handle.bind(this),
396
409
  * UserCreatedEvent.name,
397
410
  * )
398
411
  * }
399
412
  *
400
- * private async sendWelcomeEmail(event: UserCreatedEvent): Promise<void> {
413
+ * async handle(event: UserCreatedEvent): Promise<void> {
401
414
  * await this.mailer.send(event.user.email)
402
415
  * }
403
416
  * }
404
417
  * ```
405
418
  */
406
- interface EventHandler {
419
+ interface EventHandler2<T extends DomainEvent> {
407
420
  /**
408
421
  * Registers all event subscriptions for this handler.
409
- * Should be called once during application bootstrap.
422
+ *
423
+ * Should be called once during instantiation — typically
424
+ * in the constructor — to ensure the handler is active
425
+ * before any events are dispatched.
410
426
  */
411
427
  setupSubscriptions(): void;
428
+ /**
429
+ * Executes the handler logic in response to a dispatched event.
430
+ *
431
+ * @param event - The domain event instance that was dispatched.
432
+ */
433
+ handle(event: T): Promise<void>;
412
434
  }
413
435
  /**
414
436
  * Make some property optional on type
@@ -634,4 +656,4 @@ interface UseCase<
634
656
  > {
635
657
  execute(input: Input): Promise<Output>;
636
658
  }
637
- export { Either, left, right, DomainEvent, Creatable, Deletable, Findable, Saveable, Repository, UseCaseError, UseCase, Entity, AggregateRoot, DomainEvents, EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList };
659
+ 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.3",
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};