@vytches/ddd-aggregates 0.31.0 → 0.31.1
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 +58 -0
- 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.
|
|
3
|
+
"version": "0.31.1",
|
|
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.
|
|
38
|
-
"@vytches/ddd-
|
|
39
|
-
"@vytches/ddd-value-objects": "0.31.
|
|
40
|
-
"@vytches/ddd-
|
|
37
|
+
"@vytches/ddd-contracts": "0.31.1",
|
|
38
|
+
"@vytches/ddd-domain-primitives": "0.31.1",
|
|
39
|
+
"@vytches/ddd-value-objects": "0.31.1",
|
|
40
|
+
"@vytches/ddd-utils": "0.31.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"typescript": "^5.0.0",
|