tshex-cli 1.0.20 → 1.0.21
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 +14 -14
- package/docs/context-ports.md +165 -0
- package/docs/generated-file-reference.md +88 -0
- package/docs/library-structure.md +163 -0
- package/docs/library-types.md +70 -0
- package/docs/shared/application/data.md +185 -0
- package/docs/shared/application/events.md +144 -0
- package/docs/shared/application/http.md +131 -0
- package/docs/shared/application/loggers.md +127 -0
- package/docs/shared/application/services.md +120 -0
- package/docs/shared/application/validations.md +94 -0
- package/docs/shared/domain/aggregates.md +79 -0
- package/docs/shared/domain/entities.md +91 -0
- package/docs/shared/domain/errors.md +103 -0
- package/docs/shared/domain/value-objects.md +148 -0
- package/package.json +8 -3
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
### Validations
|
|
2
|
+
|
|
3
|
+
Validation objects are responsible for checking whether application input is
|
|
4
|
+
ready for the next step of a process.
|
|
5
|
+
They are used to keep validation explicit before the application constructs
|
|
6
|
+
domain objects or invokes external collaborators.
|
|
7
|
+
|
|
8
|
+
The generated template provides a single contract for this purpose:
|
|
9
|
+
`Validatable`.
|
|
10
|
+
|
|
11
|
+
#### Contract
|
|
12
|
+
|
|
13
|
+
`Validatable` is responsible for exposing a Boolean validation result.
|
|
14
|
+
|
|
15
|
+
```ts title="shared/application/validations.ts"
|
|
16
|
+
export interface Validatable {
|
|
17
|
+
isValid(): boolean
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This contract stays deliberately small. It does not prescribe how errors are
|
|
22
|
+
stored, how messages are formatted, or whether validation is synchronous or
|
|
23
|
+
composed from several objects.
|
|
24
|
+
|
|
25
|
+
#### First Validation Object
|
|
26
|
+
|
|
27
|
+
In the following example we validate a registration payload before the service
|
|
28
|
+
turns it into domain values.
|
|
29
|
+
|
|
30
|
+
```ts title="users/application/create-user-input.ts"
|
|
31
|
+
import { Validatable } from '../../shared/application/validations.js'
|
|
32
|
+
import { Email } from '../../shared/domain/value-objects.js'
|
|
33
|
+
|
|
34
|
+
export class CreateUserInput implements Validatable {
|
|
35
|
+
public constructor(
|
|
36
|
+
public readonly id: string,
|
|
37
|
+
public readonly email: string,
|
|
38
|
+
) {}
|
|
39
|
+
|
|
40
|
+
public isValid(): boolean {
|
|
41
|
+
return this.id.length > 0 && Email.isValid(this.email)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`CreateUserInput` performs application-level checks without constructing an
|
|
47
|
+
`Email` instance yet. This is useful when the process wants to reject invalid
|
|
48
|
+
input before attempting a full domain conversion.
|
|
49
|
+
|
|
50
|
+
#### Integration With A Service
|
|
51
|
+
|
|
52
|
+
Now that the validation object exists, a service can decide what to do when the
|
|
53
|
+
input does not satisfy the contract.
|
|
54
|
+
|
|
55
|
+
```ts title="users/application/register-user.ts"
|
|
56
|
+
import { Service } from '../../shared/application/services.js'
|
|
57
|
+
import { Validatable } from '../../shared/application/validations.js'
|
|
58
|
+
|
|
59
|
+
type CreateUserInput = Validatable & {
|
|
60
|
+
readonly id: string
|
|
61
|
+
readonly email: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type RegistrationResult =
|
|
65
|
+
| { ok: true }
|
|
66
|
+
| { ok: false; errors: string[] }
|
|
67
|
+
|
|
68
|
+
export class RegisterUser extends Service {
|
|
69
|
+
public execute(input: CreateUserInput): RegistrationResult {
|
|
70
|
+
if (input.isValid() === false) {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
errors: ['The registration input is invalid.'],
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
ok: true,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The service decides the process outcome, while the validation object owns the
|
|
85
|
+
question of whether the input is acceptable.
|
|
86
|
+
|
|
87
|
+
> **Hint**
|
|
88
|
+
> `Validatable` does not replace domain rules. Use it for application-level
|
|
89
|
+
> checks. Keep domain invariants inside value objects, entities, or aggregates.
|
|
90
|
+
|
|
91
|
+
#### Next Step
|
|
92
|
+
|
|
93
|
+
After validation succeeds, the next step is usually to construct domain objects
|
|
94
|
+
or call a repository through an application service.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
### Aggregates
|
|
2
|
+
|
|
3
|
+
An aggregate is responsible for grouping multiple entities into one logical
|
|
4
|
+
domain unit.
|
|
5
|
+
It is used when the behavior of the concept depends on the collaboration of
|
|
6
|
+
several entities instead of a single entity in isolation.
|
|
7
|
+
|
|
8
|
+
The generated template provides `Aggregate` as a semantic base class.
|
|
9
|
+
|
|
10
|
+
#### Base Class
|
|
11
|
+
|
|
12
|
+
`Aggregate` is an abstract class with no concrete methods.
|
|
13
|
+
|
|
14
|
+
```ts title="shared/domain/aggregates.ts"
|
|
15
|
+
export abstract class Aggregate {}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This means the generated base class does not impose persistence rules, event
|
|
19
|
+
publication, or identity behavior. Those concerns remain in the concrete domain
|
|
20
|
+
model of the project.
|
|
21
|
+
|
|
22
|
+
#### First Aggregate
|
|
23
|
+
|
|
24
|
+
In the following example we model a shopping cart as a group of line items.
|
|
25
|
+
|
|
26
|
+
```ts title="users/domain/cart.ts"
|
|
27
|
+
import { Aggregate } from '../../shared/domain/aggregates.js'
|
|
28
|
+
import { Entity } from '../../shared/domain/entities.js'
|
|
29
|
+
|
|
30
|
+
class LineItem extends Entity {
|
|
31
|
+
public constructor(
|
|
32
|
+
public readonly id: string,
|
|
33
|
+
public readonly quantity: number,
|
|
34
|
+
) {
|
|
35
|
+
super()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public equals(other: Entity): boolean {
|
|
39
|
+
return other instanceof LineItem && this.id === other.id
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class Cart extends Aggregate {
|
|
44
|
+
public constructor(public readonly items: Array<LineItem>) {
|
|
45
|
+
super()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public totalItems(): number {
|
|
49
|
+
return this.items.reduce((sum, item) => sum + item.quantity, 0)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`Cart` is an aggregate because its behavior depends on the collection of
|
|
55
|
+
entities that compose it. `LineItem` remains an entity because its identity is
|
|
56
|
+
independent inside the aggregate.
|
|
57
|
+
|
|
58
|
+
#### Responsibility Boundary
|
|
59
|
+
|
|
60
|
+
An aggregate should own rules that require several internal parts to work
|
|
61
|
+
together.
|
|
62
|
+
|
|
63
|
+
Examples include:
|
|
64
|
+
|
|
65
|
+
1. checking whether the aggregate can change status;
|
|
66
|
+
2. keeping related entities in a consistent state;
|
|
67
|
+
3. exposing operations that depend on the collaboration of those entities.
|
|
68
|
+
|
|
69
|
+
The generated `Aggregate` base class does not implement these rules for you. It
|
|
70
|
+
only provides the semantic place where those rules belong.
|
|
71
|
+
|
|
72
|
+
> **Warning**
|
|
73
|
+
> Do not extend `Aggregate` only to create a container for unrelated values. Use
|
|
74
|
+
> it when the concept represents one domain unit composed of several parts.
|
|
75
|
+
|
|
76
|
+
#### Next Step
|
|
77
|
+
|
|
78
|
+
Aggregates usually collaborate with entities and value objects. The base entity
|
|
79
|
+
behavior is documented in `entities.md`.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
### Entities
|
|
2
|
+
|
|
3
|
+
An entity is responsible for representing a domain concept with its own
|
|
4
|
+
identity.
|
|
5
|
+
Two entity instances refer to the same conceptual element when they share that
|
|
6
|
+
identity, even if other attributes change over time.
|
|
7
|
+
|
|
8
|
+
The generated template provides `Entity` as a base class for this pattern.
|
|
9
|
+
|
|
10
|
+
#### Base Class
|
|
11
|
+
|
|
12
|
+
`Entity` requires one operation and already provides two helper methods.
|
|
13
|
+
|
|
14
|
+
```ts title="shared/domain/entities.ts"
|
|
15
|
+
export abstract class Entity {
|
|
16
|
+
public abstract equals(other: Entity): boolean
|
|
17
|
+
|
|
18
|
+
public toJSON(): Record<string, unknown> {
|
|
19
|
+
return this
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public toString(): string {
|
|
23
|
+
return String(this.constructor.name)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Every concrete entity must implement `equals()`. The generated base class also
|
|
29
|
+
provides `toJSON()` and `toString()`.
|
|
30
|
+
|
|
31
|
+
#### First Entity
|
|
32
|
+
|
|
33
|
+
In the following example we model a user entity.
|
|
34
|
+
|
|
35
|
+
```ts title="users/domain/user.ts"
|
|
36
|
+
import { Entity } from '../../shared/domain/entities.js'
|
|
37
|
+
import { Email } from '../../shared/domain/value-objects.js'
|
|
38
|
+
|
|
39
|
+
export class User extends Entity {
|
|
40
|
+
public constructor(
|
|
41
|
+
public readonly id: string,
|
|
42
|
+
public readonly email: Email,
|
|
43
|
+
) {
|
|
44
|
+
super()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public equals(other: Entity): boolean {
|
|
48
|
+
return other instanceof User && this.id === other.id
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The identity rule lives in `equals()`. The entity compares the `id`, not the
|
|
54
|
+
email address, because the email can change while the entity still represents
|
|
55
|
+
the same user.
|
|
56
|
+
|
|
57
|
+
#### Included Behavior
|
|
58
|
+
|
|
59
|
+
Now that the entity exists, the inherited helpers become useful.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const user = new User('usr_1', Email.from('ada@example.com'))
|
|
63
|
+
|
|
64
|
+
user.toString()
|
|
65
|
+
user.toJSON()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`toString()` returns the constructor name. `toJSON()` returns the instance as a
|
|
69
|
+
plain record, which is convenient for simple serialization or inspection.
|
|
70
|
+
|
|
71
|
+
#### Equality Rules
|
|
72
|
+
|
|
73
|
+
The most important design decision in an entity is the identity comparison.
|
|
74
|
+
|
|
75
|
+
Correct identity candidates usually include:
|
|
76
|
+
|
|
77
|
+
1. a stable identifier;
|
|
78
|
+
2. a natural domain key that does not drift over time;
|
|
79
|
+
3. a combination of fields that the domain treats as unique.
|
|
80
|
+
|
|
81
|
+
Changing fields that are not part of the identity should not make `equals()`
|
|
82
|
+
return `false` for the same conceptual entity.
|
|
83
|
+
|
|
84
|
+
> **Hint**
|
|
85
|
+
> Keep `equals()` explicit and small. If the comparison starts depending on many
|
|
86
|
+
> mutable fields, the model may be closer to a value object than to an entity.
|
|
87
|
+
|
|
88
|
+
#### Next Step
|
|
89
|
+
|
|
90
|
+
When the identity of a concept is determined entirely by its value, use a value
|
|
91
|
+
object instead. The generated abstraction is documented in `value-objects.md`.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
### Errors
|
|
2
|
+
|
|
3
|
+
Domain errors are responsible for making invalid domain values explicit.
|
|
4
|
+
They are used when a domain concept rejects an input because that input does not
|
|
5
|
+
satisfy the rule required by the concept.
|
|
6
|
+
|
|
7
|
+
The generated template provides `ValueError` for this purpose.
|
|
8
|
+
|
|
9
|
+
#### ValueError
|
|
10
|
+
|
|
11
|
+
`ValueError` is responsible for describing that a received value does not match
|
|
12
|
+
the expected domain concept.
|
|
13
|
+
|
|
14
|
+
```ts title="shared/domain/errors.ts"
|
|
15
|
+
export class ValueError extends Error {
|
|
16
|
+
public constructor(received: string, expected: string) {
|
|
17
|
+
super(`Invalid value ${received} for ${expected}.`)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The constructor receives the invalid value and the expected concept name. The
|
|
23
|
+
message format is generated automatically.
|
|
24
|
+
|
|
25
|
+
#### First Usage
|
|
26
|
+
|
|
27
|
+
In the following example we define a local value object that rejects numbers
|
|
28
|
+
outside the accepted range.
|
|
29
|
+
|
|
30
|
+
```ts title="users/domain/percentage.ts"
|
|
31
|
+
import { ValueError } from '../../shared/domain/errors.js'
|
|
32
|
+
|
|
33
|
+
export class Percentage {
|
|
34
|
+
public constructor(public readonly value: number) {}
|
|
35
|
+
|
|
36
|
+
public static from(value: number): Percentage {
|
|
37
|
+
if (value < 0 || value > 100) {
|
|
38
|
+
throw new ValueError(String(value), Percentage.name)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return new Percentage(value)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`Percentage.from()` throws `ValueError` when the received value does not satisfy
|
|
47
|
+
the rule of the concept.
|
|
48
|
+
|
|
49
|
+
#### Handling The Error
|
|
50
|
+
|
|
51
|
+
Now consider a process that wants to translate a domain error into an
|
|
52
|
+
application-level result.
|
|
53
|
+
|
|
54
|
+
```ts title="users/application/apply-discount.ts"
|
|
55
|
+
import { ValueError } from '../../shared/domain/errors.js'
|
|
56
|
+
|
|
57
|
+
type DiscountResult =
|
|
58
|
+
| { ok: true; value: number }
|
|
59
|
+
| { ok: false; error: string }
|
|
60
|
+
|
|
61
|
+
class Percentage {
|
|
62
|
+
public constructor(public readonly value: number) {}
|
|
63
|
+
|
|
64
|
+
public static from(value: number): Percentage {
|
|
65
|
+
if (value < 0 || value > 100) {
|
|
66
|
+
throw new ValueError(String(value), Percentage.name)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return new Percentage(value)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function applyDiscount(value: number): DiscountResult {
|
|
74
|
+
try {
|
|
75
|
+
return {
|
|
76
|
+
ok: true,
|
|
77
|
+
value: Percentage.from(value).value,
|
|
78
|
+
}
|
|
79
|
+
} catch (error: unknown) {
|
|
80
|
+
if (error instanceof ValueError) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
error: error.message,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
throw error
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This pattern keeps the domain rule strict while allowing the application layer
|
|
93
|
+
to decide how that failure is exposed.
|
|
94
|
+
|
|
95
|
+
> **Hint**
|
|
96
|
+
> `Email.from()` in the generated value objects uses this same error type.
|
|
97
|
+
> Reusing `ValueError` keeps invalid-value failures recognizable across the
|
|
98
|
+
> domain layer.
|
|
99
|
+
|
|
100
|
+
#### Next Step
|
|
101
|
+
|
|
102
|
+
The generated value objects that already throw `ValueError` are documented in
|
|
103
|
+
`value-objects.md`.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
### Value Objects
|
|
2
|
+
|
|
3
|
+
Value objects are responsible for modeling concepts whose identity is
|
|
4
|
+
determined entirely by their value.
|
|
5
|
+
They are used to keep domain rules, comparisons, and semantics close to the
|
|
6
|
+
data that those rules describe.
|
|
7
|
+
|
|
8
|
+
The generated template provides the abstract `ValueObject<T>` base class and two
|
|
9
|
+
concrete implementations: `NullableBoolean` and `Email`.
|
|
10
|
+
|
|
11
|
+
#### Base Class
|
|
12
|
+
|
|
13
|
+
`ValueObject<T>` is responsible for storing a value and defining its comparison
|
|
14
|
+
rules.
|
|
15
|
+
|
|
16
|
+
```ts title="shared/domain/value-objects.ts"
|
|
17
|
+
export abstract class ValueObject<T = unknown> {
|
|
18
|
+
public abstract readonly value: T
|
|
19
|
+
|
|
20
|
+
public toString(): string {
|
|
21
|
+
return String(this.value)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public toJSON(): T {
|
|
25
|
+
return this.value
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public abstract equals(other: ValueObject<T> | null | undefined): boolean
|
|
29
|
+
|
|
30
|
+
public static isValid(value: unknown): boolean {
|
|
31
|
+
return (
|
|
32
|
+
value !== null &&
|
|
33
|
+
value !== undefined &&
|
|
34
|
+
Object.is(value, NaN) === false
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The base class provides serialization helpers and a generic validation check.
|
|
41
|
+
Concrete value objects must define `value` and `equals()`.
|
|
42
|
+
|
|
43
|
+
#### Custom Value Object
|
|
44
|
+
|
|
45
|
+
In the following example we create a local value object for percentages.
|
|
46
|
+
|
|
47
|
+
```ts title="users/domain/percentage.ts"
|
|
48
|
+
import { ValueObject } from '../../shared/domain/value-objects.js'
|
|
49
|
+
import { ValueError } from '../../shared/domain/errors.js'
|
|
50
|
+
|
|
51
|
+
export class Percentage extends ValueObject<number> {
|
|
52
|
+
public override readonly value: number
|
|
53
|
+
|
|
54
|
+
protected constructor(value: number) {
|
|
55
|
+
super()
|
|
56
|
+
this.value = value
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public override equals(
|
|
60
|
+
other: Percentage | null | undefined,
|
|
61
|
+
): boolean {
|
|
62
|
+
if (other === null || other === undefined) {
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return this.value === other.value
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public static from(value: number): Percentage {
|
|
70
|
+
if (value < 0 || value > 100) {
|
|
71
|
+
throw new ValueError(String(value), Percentage.name)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return new Percentage(value)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`Percentage` owns the validation rule of the concept and the equality rule of
|
|
80
|
+
the value. This is the normal responsibility split for a value object.
|
|
81
|
+
|
|
82
|
+
#### NullableBoolean
|
|
83
|
+
|
|
84
|
+
`NullableBoolean` is responsible for representing a tri-state Boolean.
|
|
85
|
+
|
|
86
|
+
```ts title="shared/domain/value-objects.ts"
|
|
87
|
+
import { NullableBoolean } from '../../shared/domain/value-objects.js'
|
|
88
|
+
|
|
89
|
+
const active = NullableBoolean.from(true)
|
|
90
|
+
const unknown = NullableBoolean.from(null)
|
|
91
|
+
|
|
92
|
+
active.equals(NullableBoolean.from(true))
|
|
93
|
+
unknown.isIndeterminate()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`from(value)` accepts `true`, `false`, or `null`. `equals()` compares the wrapped
|
|
97
|
+
value. `isIndeterminate()` returns `true` when the state is `null`.
|
|
98
|
+
|
|
99
|
+
#### Email
|
|
100
|
+
|
|
101
|
+
`Email` is responsible for validating and describing an email address.
|
|
102
|
+
|
|
103
|
+
```ts title="shared/domain/value-objects.ts"
|
|
104
|
+
import { Email } from '../../shared/domain/value-objects.js'
|
|
105
|
+
|
|
106
|
+
const email = Email.from('ada@example.com')
|
|
107
|
+
|
|
108
|
+
email.username
|
|
109
|
+
email.domain
|
|
110
|
+
email.tld
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`Email.from()` validates the string and throws `ValueError` when the input does
|
|
114
|
+
not match the generated email rules. The getters expose common derived parts of
|
|
115
|
+
the address without repeating parsing logic in the rest of the domain.
|
|
116
|
+
|
|
117
|
+
#### Full Example
|
|
118
|
+
|
|
119
|
+
The following example combines both generated value objects in one small domain
|
|
120
|
+
shape.
|
|
121
|
+
|
|
122
|
+
```ts title="users/domain/user-profile.ts"
|
|
123
|
+
import { Email, NullableBoolean } from '../../shared/domain/value-objects.js'
|
|
124
|
+
|
|
125
|
+
type UserProfile = {
|
|
126
|
+
email: Email
|
|
127
|
+
active: NullableBoolean
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const profile: UserProfile = {
|
|
131
|
+
email: Email.from('ada@example.com'),
|
|
132
|
+
active: NullableBoolean.from(null),
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This example keeps the domain data explicit. The email carries its own
|
|
137
|
+
validation and derived fields. The active flag carries its own tri-state
|
|
138
|
+
semantics.
|
|
139
|
+
|
|
140
|
+
> **Warning**
|
|
141
|
+
> Use a value object only when equality depends on the value itself. If the
|
|
142
|
+
> concept must preserve identity independently from changing attributes, model it
|
|
143
|
+
> as an entity instead.
|
|
144
|
+
|
|
145
|
+
#### Next Step
|
|
146
|
+
|
|
147
|
+
After modeling values, the next step is usually to compose them inside entities
|
|
148
|
+
or aggregates.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tshex-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
4
4
|
"author": "https://github.com/virtualitems/",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Typescript Hexagonal Architecture CLI",
|
|
@@ -23,7 +23,11 @@
|
|
|
23
23
|
"typescript",
|
|
24
24
|
"virtualitems"
|
|
25
25
|
],
|
|
26
|
-
"
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/virtualitems/tshex-cli.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/virtualitems/tshex-cli#readme",
|
|
27
31
|
"bugs": {
|
|
28
32
|
"url": "https://github.com/virtualitems/tshex-cli/issues"
|
|
29
33
|
},
|
|
@@ -31,7 +35,8 @@
|
|
|
31
35
|
"files": [
|
|
32
36
|
"./build",
|
|
33
37
|
"./source",
|
|
34
|
-
"./templates"
|
|
38
|
+
"./templates",
|
|
39
|
+
"./docs"
|
|
35
40
|
],
|
|
36
41
|
"bin": {
|
|
37
42
|
"tshex": "./build/main.js"
|