tshex-cli 1.0.29 → 1.0.31
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/docs/context-ports.md +66 -28
- package/docs/generated-file-reference.md +4 -11
- package/docs/library-structure.md +23 -6
- package/docs/shared/application/data.md +188 -117
- package/docs/shared/application/services.md +90 -87
- package/docs/shared/domain/aggregates.md +22 -131
- package/docs/shared/domain/entities.md +98 -51
- package/package.json +1 -1
- package/readme.md +2 -9
- package/templates/lib/shared/application/data/capabilities.ts +18 -14
- package/templates/lib/shared/application/data/drivers.ts +2 -2
- package/templates/lib/shared/application/data/managers.ts +5 -5
- package/templates/lib/shared/application/data/repositories.ts +3 -4
- package/templates/lib/shared/application/events.ts +1 -1
- package/templates/lib/shared/application/http/errors.ts +2 -0
- package/docs/shared/application/http/handlers.md +0 -102
- package/docs/shared/application/http/json-api.md +0 -235
- package/docs/shared/application/http/json-web-token.md +0 -209
- package/docs/shared/application/http/opengraph.md +0 -161
package/docs/context-ports.md
CHANGED
|
@@ -21,7 +21,7 @@ surface that another actor can call or observe.
|
|
|
21
21
|
|
|
22
22
|
The generated context starts with a single root file for ports.
|
|
23
23
|
|
|
24
|
-
```ts title="
|
|
24
|
+
```ts title="enrollment/example-ports.ts"
|
|
25
25
|
export class ExamplePort {
|
|
26
26
|
public doSomething(): void {
|
|
27
27
|
// ...
|
|
@@ -39,32 +39,56 @@ expected to be a module that defines one or more context ports.
|
|
|
39
39
|
#### First Port
|
|
40
40
|
|
|
41
41
|
In the following example we replace the placeholder with a concrete port for
|
|
42
|
-
|
|
42
|
+
managing course enrollments.
|
|
43
43
|
|
|
44
|
-
```ts title="
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
44
|
+
```ts title="enrollment/example-ports.ts"
|
|
45
|
+
import { InMemoryDatabaseDriver } from './application/database.ts'
|
|
46
|
+
import { Course } from './domain/courses.ts'
|
|
47
|
+
import { Student } from './domain/students.ts'
|
|
48
|
+
import { InscriptionAggregate } from './domain/inscriptions.ts'
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const database: Record<string, Record<string, unknown>[]> = {}
|
|
51
|
+
|
|
52
|
+
export class Example {
|
|
53
|
+
[property: string]: unknown
|
|
54
|
+
|
|
55
|
+
private readonly driver: InMemoryDatabaseDriver
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
this.driver = new InMemoryDatabaseDriver(database)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public createStudent(student: Student) {
|
|
62
|
+
const result = this.driver.connect('students').create(student.toJSON())
|
|
63
|
+
this.driver.disconnect()
|
|
64
|
+
return result
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public createCourse(course: Course) {
|
|
68
|
+
const result = this.driver.connect('courses').create(course.toJSON())
|
|
69
|
+
this.driver.disconnect()
|
|
70
|
+
return result
|
|
54
71
|
}
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
73
|
+
public listInscriptions() {
|
|
74
|
+
const result = this.driver.connect('inscriptions').all()
|
|
75
|
+
this.driver.disconnect()
|
|
76
|
+
return result
|
|
77
|
+
}
|
|
61
78
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
public createInscription(student: Student, course: Course) {
|
|
80
|
+
const inscription = InscriptionAggregate.enroll(student, course)
|
|
81
|
+
const result = this.driver.connect('inscriptions').create(inscription.toJSON())
|
|
82
|
+
this.driver.disconnect()
|
|
83
|
+
return result
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
65
87
|
|
|
66
|
-
|
|
67
|
-
|
|
88
|
+
`Example` is a boundary object of the context. Each static method is one
|
|
89
|
+
concrete capability it exposes. The port connects the boundary to a driver,
|
|
90
|
+
uses domain concepts internally, and keeps infrastructure details out of the
|
|
91
|
+
caller.
|
|
68
92
|
|
|
69
93
|
This is the normal flow inside the context boundary:
|
|
70
94
|
|
|
@@ -83,10 +107,23 @@ capability and uses domain capabilities.
|
|
|
83
107
|
|
|
84
108
|
As the context grows, you can keep several port modules at the context root.
|
|
85
109
|
|
|
86
|
-
```ts title="
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
110
|
+
```ts title="enrollment/courses.ts"
|
|
111
|
+
import { CoursesService } from './application/services.ts'
|
|
112
|
+
import { Course } from './domain/courses.ts'
|
|
113
|
+
|
|
114
|
+
export class CoursesPort {
|
|
115
|
+
constructor(private readonly service: CoursesService) {}
|
|
116
|
+
|
|
117
|
+
public all(): Record<string, unknown>[] {
|
|
118
|
+
return this.service.all()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public create(name: string, description: string, hours: number): boolean {
|
|
122
|
+
return this.service.create(new Course(name, description, hours))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public delete(course: Course): boolean {
|
|
126
|
+
return this.service.delete(course)
|
|
90
127
|
}
|
|
91
128
|
}
|
|
92
129
|
```
|
|
@@ -110,9 +147,10 @@ in the generated folders.
|
|
|
110
147
|
|
|
111
148
|
```mermaid
|
|
112
149
|
flowchart TD
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
150
|
+
enrollment["enrollment/"] --> examplePort["example-ports.ts"]
|
|
151
|
+
enrollment --> courses["courses.ts"]
|
|
152
|
+
enrollment --> application["application/"]
|
|
153
|
+
enrollment --> domain["domain/"]
|
|
116
154
|
```
|
|
117
155
|
|
|
118
156
|
This layout keeps the context boundary visible from the top level. It also
|
|
@@ -63,21 +63,13 @@ contracts that adapters and services can share.
|
|
|
63
63
|
#### Shared HTTP Files
|
|
64
64
|
|
|
65
65
|
The `shared/application/http` directory groups the framework-agnostic HTTP
|
|
66
|
-
boundary
|
|
66
|
+
boundary contracts.
|
|
67
67
|
|
|
68
68
|
| File | Responsibility |
|
|
69
69
|
| --- | --- |
|
|
70
|
-
| `shared/application/http/handlers.ts` | Declares `HttpRequestHandler` and `HttpMiddleware`. |
|
|
71
70
|
| `shared/application/http/errors.ts` | Declares `HttpError`. |
|
|
72
|
-
| `shared/application/http/json-api.ts` | Type-only JSON:API v1.1 document, resource, and Atomic Operations declarations. |
|
|
73
|
-
| `shared/application/http/json-web-token.ts` | Type-only JOSE/JWT declarations (JWK, JWS, JWE, JWT claims). |
|
|
74
|
-
| `shared/application/http/opengraph.ts` | Type-only Open Graph, Twitter Card, and social metadata declarations. |
|
|
75
71
|
|
|
76
|
-
`
|
|
77
|
-
runtime code. `json-api.ts`, `json-web-token.ts`, and `opengraph.ts` contain
|
|
78
|
-
compile-time structure only; they describe the shape of external formats
|
|
79
|
-
without implementing parsing, validation, or serialization. Each file is
|
|
80
|
-
documented in its own page under `shared/application/http/*.md`.
|
|
72
|
+
`errors.ts` is documented in its own page under `shared/application/http/errors.md`.
|
|
81
73
|
|
|
82
74
|
#### Shared Data Files
|
|
83
75
|
|
|
@@ -85,8 +77,9 @@ The generated template also includes a small set of data-access abstractions.
|
|
|
85
77
|
|
|
86
78
|
| File | Responsibility |
|
|
87
79
|
| --- | --- |
|
|
80
|
+
| `shared/application/data/capabilities.ts` | Declares the operation capability interfaces: `Listable`, `Filterable`, `Sortable`, `Creatable`, `Updatable`, `Deletable`, `Aggregatable`, and `Relatable`. |
|
|
88
81
|
| `shared/application/data/drivers.ts` | Declares `DriverAdapter`, the connection contract with a data source. |
|
|
89
|
-
| `shared/application/data/managers.ts` | Declares `DataManager
|
|
82
|
+
| `shared/application/data/managers.ts` | Declares `DataManager` and `DatasetManager`. |
|
|
90
83
|
| `shared/application/data/repositories.ts` | Declares `Repository`, which transforms raw records into domain representations. |
|
|
91
84
|
|
|
92
85
|
These contracts belong to the application layer because they define how the
|
|
@@ -18,7 +18,7 @@ flowchart TD
|
|
|
18
18
|
root["Library root"] --> types["types/"]
|
|
19
19
|
root --> main["main.ts"]
|
|
20
20
|
root --> shared["shared/"]
|
|
21
|
-
root -->
|
|
21
|
+
root --> enrollment["enrollment/"]
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
`types/` groups the root-level type declarations. `main.ts` starts as a
|
|
@@ -69,7 +69,7 @@ capability.
|
|
|
69
69
|
|
|
70
70
|
```mermaid
|
|
71
71
|
flowchart TD
|
|
72
|
-
contexts["Contexts"] -->
|
|
72
|
+
contexts["Contexts"] --> enrollment["enrollment/"]
|
|
73
73
|
contexts --> billing["billing/"]
|
|
74
74
|
contexts --> inventory["inventory/"]
|
|
75
75
|
contexts --> sales["sales/"]
|
|
@@ -85,10 +85,10 @@ Every generated context starts with the same internal structure.
|
|
|
85
85
|
|
|
86
86
|
```mermaid
|
|
87
87
|
flowchart TD
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
enrollment["enrollment/"] --> ports["example-ports.ts"]
|
|
89
|
+
enrollment --> adapters["adapters/"]
|
|
90
|
+
enrollment --> application["application/"]
|
|
91
|
+
enrollment --> domain["domain/"]
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
`example-ports.ts` is an example module in the root communication surface of
|
|
@@ -189,6 +189,23 @@ application services use domain capabilities, while adapters can depend on
|
|
|
189
189
|
ports and third-party libraries. The port branch stops at the boundary because
|
|
190
190
|
what exists beyond that port depends on the system that implements it.
|
|
191
191
|
|
|
192
|
+
```ts title="main.ts"
|
|
193
|
+
import { Example } from './enrollment/example-ports.ts'
|
|
194
|
+
import { Student } from './enrollment/domain/students.ts'
|
|
195
|
+
import { Course } from './enrollment/domain/courses.ts'
|
|
196
|
+
import { Email } from './shared/domain/value-objects.ts'
|
|
197
|
+
|
|
198
|
+
const example = new Example()
|
|
199
|
+
const student = new Student('Ada Lovelace', Email.from('ada@example.com'))
|
|
200
|
+
const course = new Course('Mathematics', 'Fundamentals of algebra and calculus', 40)
|
|
201
|
+
|
|
202
|
+
example.createStudent(student)
|
|
203
|
+
example.createCourse(course)
|
|
204
|
+
example.createInscription(student, course)
|
|
205
|
+
|
|
206
|
+
console.log(example.listInscriptions())
|
|
207
|
+
```
|
|
208
|
+
|
|
192
209
|
#### Next Step
|
|
193
210
|
|
|
194
211
|
Use this structure as the default layout for new code. When you need to inspect
|
|
@@ -1,188 +1,260 @@
|
|
|
1
1
|
### Data
|
|
2
2
|
|
|
3
3
|
The data contracts define how the application layer interacts with plain source
|
|
4
|
-
records.
|
|
5
|
-
They separate connection management, raw record access, and domain
|
|
4
|
+
records. They separate connection management, raw record access, and domain
|
|
6
5
|
transformation so that a context can change drivers without rewriting its use
|
|
7
6
|
cases.
|
|
8
7
|
|
|
9
|
-
The generated structure splits this concern into
|
|
8
|
+
The generated structure splits this concern into four files:
|
|
10
9
|
|
|
11
|
-
1. `
|
|
12
|
-
2. `
|
|
13
|
-
3. `
|
|
10
|
+
1. `capabilities.ts` for operation capability interfaces;
|
|
11
|
+
2. `drivers.ts` for connection adapters;
|
|
12
|
+
3. `managers.ts` for plain-record operations;
|
|
13
|
+
4. `repositories.ts` for record-to-domain transformation.
|
|
14
14
|
|
|
15
|
-
####
|
|
15
|
+
#### Capabilities
|
|
16
16
|
|
|
17
|
-
`
|
|
18
|
-
|
|
17
|
+
`capabilities.ts` declares the operation interfaces that a manager implements
|
|
18
|
+
to advertise what it supports.
|
|
19
19
|
|
|
20
|
-
```ts title="shared/application/data/
|
|
21
|
-
|
|
20
|
+
```ts title="shared/application/data/capabilities.ts"
|
|
21
|
+
export interface Listable<DataShape extends Generic = Generic> {
|
|
22
|
+
all(): DataShape[]
|
|
23
|
+
}
|
|
22
24
|
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
+
export interface Creatable<DataShape extends Generic = Generic, Feedback = unknown> {
|
|
26
|
+
create(data: DataShape): Feedback
|
|
27
|
+
}
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
export interface Deletable<Selector = unknown, Feedback = unknown> {
|
|
30
|
+
delete(selector: Selector): Feedback
|
|
27
31
|
}
|
|
32
|
+
|
|
33
|
+
// Also available: Filterable, Sortable, Updatable, Aggregatable, Relatable
|
|
28
34
|
```
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
Implement only the interfaces that the manager actually supports. Adding
|
|
37
|
+
`Creatable` to a class makes the creation capability explicit and discoverable
|
|
38
|
+
without adding it to the base class.
|
|
39
|
+
|
|
40
|
+
```ts title="enrollment/application/managers.ts"
|
|
41
|
+
import { DataManager } from '../shared/application/data/managers.ts'
|
|
42
|
+
import type {
|
|
43
|
+
Listable,
|
|
44
|
+
Creatable,
|
|
45
|
+
Deletable
|
|
46
|
+
} from '../shared/application/data/capabilities.ts'
|
|
47
|
+
import type { Course } from '../domain/courses.ts'
|
|
48
|
+
import type { Student } from '../domain/students.ts'
|
|
49
|
+
import type { Inscription } from '../domain/inscriptions.ts'
|
|
50
|
+
|
|
51
|
+
type Generic = Record<string, unknown>
|
|
52
|
+
|
|
53
|
+
export type CourseData = ReturnType<Course['toJSON']>
|
|
54
|
+
export type StudentData = ReturnType<Student['toJSON']>
|
|
55
|
+
export type InscriptionData = ReturnType<Inscription['toJSON']>
|
|
56
|
+
|
|
57
|
+
export class InMemoryDatabaseManager
|
|
58
|
+
extends DataManager
|
|
59
|
+
implements Listable, Creatable, Deletable
|
|
60
|
+
{
|
|
61
|
+
[property: string]: unknown
|
|
62
|
+
|
|
63
|
+
constructor(private records: Generic[]) {
|
|
64
|
+
super()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public all(): Generic[] {
|
|
68
|
+
return this.records
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public create(data: Generic): boolean {
|
|
72
|
+
this.records.push(data)
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public delete(selector: Partial<Generic>): boolean {
|
|
77
|
+
const before = this.records.length
|
|
78
|
+
this.records = this.records.filter((record) =>
|
|
79
|
+
Object.entries(selector).every(([key, value]) => record[key] !== value)
|
|
80
|
+
)
|
|
81
|
+
return this.records.length < before
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
32
85
|
|
|
33
86
|
#### Data Manager
|
|
34
87
|
|
|
35
|
-
`DataManager` is
|
|
88
|
+
`DataManager` is the base contract for any data source. `DatasetManager`
|
|
89
|
+
extends it with set operations for contexts that need to combine collections.
|
|
36
90
|
|
|
37
91
|
```ts title="shared/application/data/managers.ts"
|
|
38
92
|
export abstract class DataManager<T = Record<string, unknown>> {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
93
|
+
[property: string]: unknown
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export abstract class DatasetManager<T = Record<string, unknown>> extends DataManager<T> {
|
|
97
|
+
[property: string]: unknown
|
|
42
98
|
|
|
43
|
-
public abstract
|
|
99
|
+
public abstract union(other: Array<T>): Array<T>
|
|
100
|
+
public abstract intersection(other: Array<T>): Array<T>
|
|
101
|
+
public abstract difference(other: Array<T>): Array<T>
|
|
102
|
+
public abstract symmetricDifference(other: Array<T>): Array<T>
|
|
103
|
+
public abstract complement(other: Array<T>): Array<T>
|
|
44
104
|
}
|
|
45
105
|
```
|
|
46
106
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`DatasetManager` extension for set operations.
|
|
107
|
+
Extend `DataManager` to implement a concrete data source. The manager exposes
|
|
108
|
+
raw data without domain transformation. The `InMemoryDatabaseManager` above
|
|
109
|
+
extends `DataManager` and implements `Listable`, `Creatable`, and `Deletable`.
|
|
51
110
|
|
|
52
|
-
####
|
|
111
|
+
#### Driver Adapter
|
|
112
|
+
|
|
113
|
+
`DriverAdapter` is responsible for connecting to a data source and returning an
|
|
114
|
+
enabled `DataManager`.
|
|
53
115
|
|
|
54
|
-
|
|
116
|
+
```ts title="shared/application/data/drivers.ts"
|
|
117
|
+
export abstract class DriverAdapter<M extends DataManager = DataManager> {
|
|
118
|
+
[property: string]: unknown
|
|
55
119
|
|
|
56
|
-
|
|
57
|
-
import { DriverAdapter } from '../../shared/application/data/drivers.js'
|
|
58
|
-
import { DataManager } from '../../shared/application/data/managers.js'
|
|
120
|
+
public abstract connect(...args: unknown[]): M
|
|
59
121
|
|
|
60
|
-
|
|
61
|
-
id: string
|
|
62
|
-
email: string
|
|
63
|
-
active: boolean | null
|
|
122
|
+
public abstract disconnect(): unknown
|
|
64
123
|
}
|
|
124
|
+
```
|
|
65
125
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
super()
|
|
69
|
-
}
|
|
126
|
+
Extend `DriverAdapter` to wrap a concrete data source. The driver connects to
|
|
127
|
+
the source, returns an enabled manager, and disconnects when the work is done.
|
|
70
128
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
129
|
+
```ts title="enrollment/application/database.ts"
|
|
130
|
+
import { DriverAdapter } from '../shared/application/data/drivers.ts'
|
|
131
|
+
import { InMemoryDatabaseManager } from './managers.ts'
|
|
132
|
+
|
|
133
|
+
type Generic = Record<string, unknown>
|
|
75
134
|
|
|
76
|
-
export
|
|
77
|
-
|
|
135
|
+
export type Database = Record<string, Generic[]>
|
|
136
|
+
|
|
137
|
+
export class InMemoryDatabaseDriver extends DriverAdapter<InMemoryDatabaseManager> {
|
|
138
|
+
[property: string]: unknown
|
|
139
|
+
|
|
140
|
+
private manager: InMemoryDatabaseManager | null = null
|
|
141
|
+
|
|
142
|
+
constructor(private readonly database: Database) {
|
|
78
143
|
super()
|
|
79
144
|
}
|
|
80
145
|
|
|
81
|
-
public
|
|
82
|
-
|
|
146
|
+
public connect(collectionKey: string): InMemoryDatabaseManager {
|
|
147
|
+
if (this.database[collectionKey] === undefined) {
|
|
148
|
+
this.database[collectionKey] = []
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.manager = new InMemoryDatabaseManager(this.database[collectionKey])
|
|
152
|
+
return this.manager
|
|
83
153
|
}
|
|
84
154
|
|
|
85
|
-
public
|
|
86
|
-
|
|
155
|
+
public disconnect(): void {
|
|
156
|
+
this.manager = null
|
|
87
157
|
}
|
|
88
158
|
}
|
|
89
159
|
```
|
|
90
160
|
|
|
91
|
-
`
|
|
92
|
-
raw records. The application layer can use both without knowing whether
|
|
93
|
-
source is memory, SQL, or an HTTP-backed adapter.
|
|
161
|
+
`InMemoryDatabaseDriver` owns the connection contract. `InMemoryDatabaseManager`
|
|
162
|
+
owns the raw records. The application layer can use both without knowing whether
|
|
163
|
+
the source is memory, SQL, or an HTTP-backed adapter.
|
|
94
164
|
|
|
95
|
-
|
|
165
|
+
#### Repository
|
|
96
166
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
167
|
+
`Repository` is responsible for transforming raw records into domain-oriented
|
|
168
|
+
representations. It holds a reference to the manager and requires `transform()`
|
|
169
|
+
to map a raw record into a domain entity.
|
|
100
170
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
171
|
+
```ts title="shared/application/data/repositories.ts"
|
|
172
|
+
export abstract class Repository<
|
|
173
|
+
RawDataShape = Generic,
|
|
174
|
+
EntityShape = Generic,
|
|
175
|
+
M extends DataManager<RawDataShape> = DataManager<RawDataShape>
|
|
176
|
+
> {
|
|
177
|
+
[property: string]: unknown
|
|
178
|
+
|
|
179
|
+
public constructor(public readonly manager: M) {}
|
|
180
|
+
|
|
181
|
+
protected abstract transform(data: RawDataShape, ...args: unknown[]): EntityShape
|
|
104
182
|
}
|
|
105
183
|
```
|
|
106
184
|
|
|
107
|
-
|
|
185
|
+
Data retrieval operations are not defined in the base class — add them
|
|
186
|
+
explicitly in the concrete class using the capability interfaces from
|
|
187
|
+
`capabilities.ts`.
|
|
108
188
|
|
|
109
|
-
|
|
110
|
-
|
|
189
|
+
```ts title="enrollment/application/repositories.ts"
|
|
190
|
+
import { Repository } from '../shared/application/data/repositories.ts'
|
|
191
|
+
import { InMemoryDatabaseManager } from './managers.ts'
|
|
192
|
+
import type { CourseData, StudentData, InscriptionData } from './managers.ts'
|
|
193
|
+
import { Course } from '../domain/courses.ts'
|
|
194
|
+
import { Student } from '../domain/students.ts'
|
|
195
|
+
import { Inscription } from '../domain/inscriptions.ts'
|
|
196
|
+
import { Email } from '../shared/domain/value-objects.ts'
|
|
111
197
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
import { type DriverAdapter } from './drivers.js'
|
|
198
|
+
export class CoursesRepository extends Repository<CourseData, Course, InMemoryDatabaseManager> {
|
|
199
|
+
[property: string]: unknown
|
|
115
200
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
> {
|
|
120
|
-
public constructor(public readonly driver: DriverAdapter<DataManager<DataShape>>) {}
|
|
201
|
+
constructor(manager: InMemoryDatabaseManager) {
|
|
202
|
+
super(manager)
|
|
203
|
+
}
|
|
121
204
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const raw = await connection.all()
|
|
125
|
-
const entities = this.transformList(raw)
|
|
126
|
-
await this.driver.disconnect()
|
|
127
|
-
return entities
|
|
205
|
+
protected transform(data: CourseData): Course {
|
|
206
|
+
return new Course(data.name, data.description, data.duration_hours)
|
|
128
207
|
}
|
|
129
208
|
|
|
130
|
-
|
|
131
|
-
return
|
|
209
|
+
public create(course: Course): boolean {
|
|
210
|
+
return this.manager.create(course.toJSON())
|
|
132
211
|
}
|
|
133
212
|
|
|
134
|
-
|
|
213
|
+
public delete(course: Course): boolean {
|
|
214
|
+
return this.manager.delete(course.toJSON())
|
|
215
|
+
}
|
|
135
216
|
}
|
|
136
|
-
```
|
|
137
217
|
|
|
138
|
-
|
|
139
|
-
|
|
218
|
+
export class StudentsRepository extends Repository<StudentData, Student, InMemoryDatabaseManager> {
|
|
219
|
+
[property: string]: unknown
|
|
140
220
|
|
|
141
|
-
|
|
221
|
+
constructor(manager: InMemoryDatabaseManager) {
|
|
222
|
+
super(manager)
|
|
223
|
+
}
|
|
142
224
|
|
|
143
|
-
|
|
144
|
-
|
|
225
|
+
protected transform(data: StudentData): Student {
|
|
226
|
+
return new Student(data.name, Email.from(data.email))
|
|
227
|
+
}
|
|
145
228
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
import { DataManager } from '../../shared/application/data/managers.js'
|
|
229
|
+
public create(student: Student): boolean {
|
|
230
|
+
return this.manager.create(student.toJSON())
|
|
231
|
+
}
|
|
150
232
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
active: boolean | null
|
|
233
|
+
public delete(student: Student): boolean {
|
|
234
|
+
return this.manager.delete(student.toJSON())
|
|
235
|
+
}
|
|
155
236
|
}
|
|
156
237
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
email: string
|
|
160
|
-
active: boolean | null
|
|
161
|
-
}
|
|
238
|
+
export class InscriptionsRepository extends Repository<InscriptionData, Inscription, InMemoryDatabaseManager> {
|
|
239
|
+
[property: string]: unknown
|
|
162
240
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
super(driver)
|
|
241
|
+
constructor(manager: InMemoryDatabaseManager) {
|
|
242
|
+
super(manager)
|
|
166
243
|
}
|
|
167
244
|
|
|
168
|
-
protected transform(data:
|
|
169
|
-
return
|
|
170
|
-
id: data.id,
|
|
171
|
-
email: data.email,
|
|
172
|
-
active: data.active,
|
|
173
|
-
}
|
|
245
|
+
protected transform(data: InscriptionData, student: Student, course: Course): Inscription {
|
|
246
|
+
return new Inscription(student, course, data.enrolled_at)
|
|
174
247
|
}
|
|
175
|
-
}
|
|
176
|
-
```
|
|
177
248
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
249
|
+
public create(inscription: Inscription): boolean {
|
|
250
|
+
return this.manager.create(inscription.toJSON())
|
|
251
|
+
}
|
|
181
252
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
253
|
+
public delete(inscription: Inscription): boolean {
|
|
254
|
+
return this.manager.delete(inscription.toJSON())
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
186
258
|
|
|
187
259
|
#### Example Flow
|
|
188
260
|
|
|
@@ -191,8 +263,7 @@ The normal flow of the data abstractions is the following:
|
|
|
191
263
|
```mermaid
|
|
192
264
|
flowchart LR
|
|
193
265
|
service[Service] --> repository[Repository]
|
|
194
|
-
repository -->
|
|
195
|
-
driver --> manager["Data manager"]
|
|
266
|
+
repository --> manager["Data manager"]
|
|
196
267
|
manager --> raw["Raw records"]
|
|
197
268
|
repository --> transformed["Transformed records"]
|
|
198
269
|
transformed --> service
|