@shirudo/ddd-kit 0.9.8 → 0.11.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.
- package/README.md +53 -12
- package/dist/deep-equal-except-C8yoSk4L.d.ts +57 -0
- package/dist/index.d.ts +1516 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/result-jCwPSjFa.d.ts +352 -0
- package/dist/result.d.ts +204 -0
- package/dist/result.js +2 -0
- package/dist/result.js.map +1 -0
- package/dist/utils-array.d.ts +47 -0
- package/dist/utils-array.js +2 -0
- package/dist/utils-array.js.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +2 -0
- package/dist/utils.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Composable TypeScript toolkit for tactical Domain-Driven Design.
|
|
4
4
|
|
|
5
|
+
> **⚠️ BETA WARNING**
|
|
6
|
+
>
|
|
7
|
+
> This library is currently in beta. The API is subject to change until a stable 1.0.0 release. Breaking changes may occur in minor versions during the beta phase. Please pin your dependencies to specific versions.
|
|
8
|
+
|
|
5
9
|
## Badges
|
|
6
10
|
|
|
7
11
|

|
|
@@ -37,9 +41,9 @@ pnpm add @shirudo/ddd-kit
|
|
|
37
41
|
Here's a minimal example showing how to create and use a Value Object:
|
|
38
42
|
|
|
39
43
|
```typescript
|
|
40
|
-
import { vo, type
|
|
44
|
+
import { vo, type VO } from "@shirudo/ddd-kit";
|
|
41
45
|
|
|
42
|
-
type EmailAddress =
|
|
46
|
+
type EmailAddress = VO<{
|
|
43
47
|
value: string;
|
|
44
48
|
}>;
|
|
45
49
|
|
|
@@ -125,10 +129,10 @@ The `Result<T, E>` type provides functional error handling without exceptions. I
|
|
|
125
129
|
### Creating a Value Object
|
|
126
130
|
|
|
127
131
|
```typescript
|
|
128
|
-
import { vo, voEquals, voEqualsExcept, voWithValidation, type
|
|
132
|
+
import { vo, voEquals, voEqualsExcept, voWithValidation, type VO } from "@shirudo/ddd-kit";
|
|
129
133
|
|
|
130
|
-
// Simple value object
|
|
131
|
-
type Money =
|
|
134
|
+
// Simple value object (Functional Style)
|
|
135
|
+
type Money = VO<{
|
|
132
136
|
amount: number;
|
|
133
137
|
currency: string;
|
|
134
138
|
}>;
|
|
@@ -136,6 +140,22 @@ type Money = ValueObject<{
|
|
|
136
140
|
const price = vo({ amount: 99.99, currency: "USD" });
|
|
137
141
|
// price is deeply immutable - nested objects and arrays are also frozen
|
|
138
142
|
|
|
143
|
+
// Class-based Value Object (OOP Style)
|
|
144
|
+
import { ValueObject } from "@shirudo/ddd-kit";
|
|
145
|
+
|
|
146
|
+
class Address extends ValueObject<{ street: string; city: string }> {
|
|
147
|
+
constructor(props: { street: string; city: string }) {
|
|
148
|
+
super(props);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
get street(): string {
|
|
152
|
+
return this.props.street;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const address = new Address({ street: "Main St", city: "New York" });
|
|
157
|
+
// address.props is immutable
|
|
158
|
+
|
|
139
159
|
// Value object with validation (returns Result)
|
|
140
160
|
const result = voWithValidation(
|
|
141
161
|
{ amount: 100, currency: "USD" },
|
|
@@ -458,7 +478,7 @@ import {
|
|
|
458
478
|
type CreateOrderCommand = Command & {
|
|
459
479
|
type: "CreateOrder";
|
|
460
480
|
customerId: string;
|
|
461
|
-
items: Array<{ productId: string; quantity: number }>;
|
|
481
|
+
items: Array<{ productId: string; quantity: number; price: number }>;
|
|
462
482
|
};
|
|
463
483
|
|
|
464
484
|
// Create a command handler
|
|
@@ -471,7 +491,14 @@ const createOrderHandler: CommandHandler<CreateOrderCommand, string> = async (
|
|
|
471
491
|
}
|
|
472
492
|
|
|
473
493
|
// Perform business logic
|
|
474
|
-
const
|
|
494
|
+
const orderId = `order-${Date.now()}` as OrderId;
|
|
495
|
+
const order = Order.create(orderId, cmd.customerId);
|
|
496
|
+
|
|
497
|
+
// Add items to the order
|
|
498
|
+
for (const item of cmd.items) {
|
|
499
|
+
order.addItem(item.productId, item.quantity, item.price);
|
|
500
|
+
}
|
|
501
|
+
|
|
475
502
|
await repository.save(order);
|
|
476
503
|
|
|
477
504
|
return ok(order.id);
|
|
@@ -481,7 +508,7 @@ const createOrderHandler: CommandHandler<CreateOrderCommand, string> = async (
|
|
|
481
508
|
const result = await createOrderHandler({
|
|
482
509
|
type: "CreateOrder",
|
|
483
510
|
customerId: "customer-123",
|
|
484
|
-
items: [{ productId: "product-1", quantity: 2 }],
|
|
511
|
+
items: [{ productId: "product-1", quantity: 2, price: 10.0 }],
|
|
485
512
|
});
|
|
486
513
|
|
|
487
514
|
if (result.ok) {
|
|
@@ -498,7 +525,7 @@ commandBus.register("CreateOrder", createOrderHandler);
|
|
|
498
525
|
const busResult = await commandBus.execute({
|
|
499
526
|
type: "CreateOrder",
|
|
500
527
|
customerId: "customer-123",
|
|
501
|
-
items: [{ productId: "product-1", quantity: 2 }],
|
|
528
|
+
items: [{ productId: "product-1", quantity: 2, price: 10.0 }],
|
|
502
529
|
});
|
|
503
530
|
```
|
|
504
531
|
|
|
@@ -568,7 +595,14 @@ const createOrderHandler: CommandHandler<CreateOrderCommand, string> = async (
|
|
|
568
595
|
return await withCommit(
|
|
569
596
|
{ outbox, bus, uow },
|
|
570
597
|
async () => {
|
|
571
|
-
const
|
|
598
|
+
const orderId = `order-${Date.now()}` as OrderId;
|
|
599
|
+
const order = Order.create(orderId, cmd.customerId);
|
|
600
|
+
|
|
601
|
+
// Add items to the order
|
|
602
|
+
for (const item of cmd.items) {
|
|
603
|
+
order.addItem(item.productId, item.quantity, item.price);
|
|
604
|
+
}
|
|
605
|
+
|
|
572
606
|
await repository.save(order);
|
|
573
607
|
|
|
574
608
|
return {
|
|
@@ -606,7 +640,7 @@ import {
|
|
|
606
640
|
type CreateOrderCommand = Command & {
|
|
607
641
|
type: "CreateOrder";
|
|
608
642
|
customerId: string;
|
|
609
|
-
items:
|
|
643
|
+
items: Array<{ productId: string; quantity: number; price: number }>;
|
|
610
644
|
};
|
|
611
645
|
|
|
612
646
|
type GetOrderQuery = Query & {
|
|
@@ -618,7 +652,14 @@ type GetOrderQuery = Query & {
|
|
|
618
652
|
const createOrderHandler: CommandHandler<CreateOrderCommand, OrderId> = async (
|
|
619
653
|
cmd
|
|
620
654
|
) => {
|
|
621
|
-
const
|
|
655
|
+
const orderId = `order-${Date.now()}` as OrderId;
|
|
656
|
+
const order = Order.create(orderId, cmd.customerId);
|
|
657
|
+
|
|
658
|
+
// Add items to the order
|
|
659
|
+
for (const item of cmd.items) {
|
|
660
|
+
order.addItem(item.productId, item.quantity, item.price);
|
|
661
|
+
}
|
|
662
|
+
|
|
622
663
|
await repository.save(order);
|
|
623
664
|
return ok(order.id);
|
|
624
665
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
type Key = string | symbol;
|
|
2
|
+
type PathSegment = string | number | symbol;
|
|
3
|
+
interface DeepOmitOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Keys to ignore everywhere in the object tree.
|
|
6
|
+
* Only applies to object properties, not Map/Set/TypedArray contents.
|
|
7
|
+
*/
|
|
8
|
+
readonly ignoreKeys?: readonly Key[];
|
|
9
|
+
/**
|
|
10
|
+
* Fine-grained control: Key + path (without current key).
|
|
11
|
+
* Example path: ["user", "meta", 0, "data"]
|
|
12
|
+
*/
|
|
13
|
+
readonly ignoreKeyPredicate?: (key: Key, path: readonly PathSegment[]) => boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a deep copy of `value` with certain keys removed according to the provided rules.
|
|
17
|
+
*
|
|
18
|
+
* This function recursively traverses the object tree and removes keys that match
|
|
19
|
+
* the criteria specified in `options`. Built-in types (Date, Map, Set, TypedArrays, etc.)
|
|
20
|
+
* are treated atomically and not modified.
|
|
21
|
+
*
|
|
22
|
+
* @param value - The value to create a deep copy from
|
|
23
|
+
* @param options - Options specifying which keys to ignore
|
|
24
|
+
* @returns A deep copy of `value` with specified keys removed
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const obj = { a: 1, b: { c: 2, d: 3 } };
|
|
29
|
+
* const result = deepOmit(obj, { ignoreKeys: ['d'] });
|
|
30
|
+
* // result: { a: 1, b: { c: 2 } }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function deepOmit<T>(value: T, options: DeepOmitOptions): T;
|
|
34
|
+
|
|
35
|
+
type DeepEqualExceptOptions = DeepOmitOptions;
|
|
36
|
+
/**
|
|
37
|
+
* Performs a deep equality comparison between two values after omitting specified keys.
|
|
38
|
+
*
|
|
39
|
+
* This function first removes the specified keys from both values using `deepOmit`,
|
|
40
|
+
* then performs a deep equality check using `deepEqual`.
|
|
41
|
+
*
|
|
42
|
+
* @param a - The first value to compare
|
|
43
|
+
* @param b - The second value to compare
|
|
44
|
+
* @param options - Options specifying which keys to omit before comparison
|
|
45
|
+
* @returns `true` if the values are deeply equal after omitting specified keys, `false` otherwise
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const obj1 = { id: 1, name: "Alice", updatedAt: "2024-01-01" };
|
|
50
|
+
* const obj2 = { id: 2, name: "Alice", updatedAt: "2024-01-02" };
|
|
51
|
+
*
|
|
52
|
+
* deepEqualExcept(obj1, obj2, { ignoreKeys: ["id", "updatedAt"] }); // true
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function deepEqualExcept(a: unknown, b: unknown, options: DeepEqualExceptOptions): boolean;
|
|
56
|
+
|
|
57
|
+
export { type DeepOmitOptions as D, type Key as K, type PathSegment as P, type DeepEqualExceptOptions as a, deepEqualExcept as b, deepOmit as d };
|