@vytches/ddd-aggregates 0.31.0 → 0.32.0

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.
Files changed (2) hide show
  1. package/README.md +58 -0
  2. package/package.json +5 -5
package/README.md CHANGED
@@ -137,6 +137,64 @@ class OrderLine extends Entity<string> {
137
137
  }
138
138
  ```
139
139
 
140
+ ## Late-bound event data at the persistence boundary
141
+
142
+ `getDomainEvents()` returns deep-frozen events — payload and nested objects
143
+ included. That is deliberate: an event is a record of something that already
144
+ happened, so nothing downstream may rewrite it. But a repository sometimes has
145
+ to attach data only the persistence boundary knows: an encryption key id
146
+ resolved at save time, an encrypted payload, a correlation id assigned at
147
+ dispatch.
148
+
149
+ `transformDomainEvents()` is the sanctioned way in. It replaces `payload` and
150
+ `metadata` on the aggregate's own events — identity, prototype and `instanceof`
151
+ survive, and the event constructor is never called. It has to land on the
152
+ aggregate rather than on a local copy, because the dispatcher reads the
153
+ aggregate's events again after `save()`; a local copy would leave the in-process
154
+ event bus publishing the untransformed originals.
155
+
156
+ The callback is synchronous, so anything asynchronous (a key lookup, an encrypt
157
+ call) runs first and the results are applied by index:
158
+
159
+ ```typescript
160
+ protected async encryptEventPii(aggregate: TAggregate): Promise<void> {
161
+ const events = aggregate.getDomainEvents();
162
+ const replacements = new Map<number, { payload: unknown }>();
163
+
164
+ for (let i = 0; i < events.length; i++) {
165
+ const event = events[i];
166
+ if (!isPiiCarrying(event)) continue;
167
+
168
+ // async: resolve the per-subject key, then encrypt
169
+ const keyId = await this.keys.resolve(event.payload.crypto.subjectKeyRef);
170
+ const payload = event.payload as Record<string, unknown>;
171
+
172
+ replacements.set(i, {
173
+ payload: {
174
+ ...payload,
175
+ piiData: await this.crypto.encrypt(payload.piiData),
176
+ crypto: { ...(payload.crypto as object), keyId },
177
+ },
178
+ });
179
+ }
180
+
181
+ if (replacements.size > 0) {
182
+ aggregate.transformDomainEvents((_event, index) => replacements.get(index));
183
+ }
184
+ }
185
+ ```
186
+
187
+ Return nothing for an event to leave it untouched. `eventName`, identity, and
188
+ the number and order of events are fixed — changing those would desync handlers
189
+ on replay or break the version invariant.
190
+
191
+ **Reaching into `_domainEvents` instead is not a supported substitute.** It is
192
+ private, it is not part of the public contract, and code that does it silently
193
+ depends on an internal field name. The one thing it can do that
194
+ `transformDomainEvents()` deliberately cannot is swap a whole event _instance_
195
+ (for example to restore a prototype lost by an older serialization path) — if
196
+ you need that, treat it as a migration step, not as a pattern.
197
+
140
198
  ## API Reference
141
199
 
142
200
  ### Core Classes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vytches/ddd-aggregates",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "Aggregate root implementations with capabilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -34,10 +34,10 @@
34
34
  "directory": "packages/aggregates"
35
35
  },
36
36
  "dependencies": {
37
- "@vytches/ddd-contracts": "0.31.0",
38
- "@vytches/ddd-utils": "0.31.0",
39
- "@vytches/ddd-value-objects": "0.31.0",
40
- "@vytches/ddd-domain-primitives": "0.31.0"
37
+ "@vytches/ddd-domain-primitives": "0.32.0",
38
+ "@vytches/ddd-contracts": "0.32.0",
39
+ "@vytches/ddd-utils": "0.32.0",
40
+ "@vytches/ddd-value-objects": "0.32.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "typescript": "^5.0.0",