tshex-cli 1.0.18 → 1.0.19

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `tshex-cli` creates the base structure of a library organized by contexts. The structure groups shared contracts, domain concepts, use cases, and adapters into directories with defined responsibilities.
4
4
 
5
- In this guide, we will build a library named `core` with a context named `users`. The walkthrough starts with the CLI and continues with the implementation of each generated component.
5
+ In this guide, we will build a library named `core` with a context named `users`. The walkthrough starts with the CLI and then explains the purpose of the generated components.
6
6
 
7
7
  The examples in this guide are intentionally simple. They are designed to show the responsibility of each component, not to cover real infrastructure or production scenarios.
8
8
 
@@ -74,7 +74,10 @@ core/
74
74
  ├── main.ts
75
75
  ├── shared/
76
76
  │ ├── application/
77
- │ │ ├── data-sources.ts
77
+ │ │ ├── data/
78
+ │ │ │ ├── drivers.ts
79
+ │ │ │ ├── managers.ts
80
+ │ │ │ └── repositories.ts
78
81
  │ │ ├── events.ts
79
82
  │ │ ├── http.ts
80
83
  │ │ ├── loggers.ts
@@ -89,8 +92,7 @@ core/
89
92
  ├── adapters/
90
93
  ├── application/
91
94
  ├── domain/
92
- ├── example-ports.ts
93
- └── index.ts
95
+ └── example-ports.ts
94
96
  ```
95
97
 
96
98
  ### Choose the destination directory
@@ -146,7 +148,7 @@ The library is divided into a root, a shared directory, and one or more contexts
146
148
 
147
149
  The root contains `index.d.ts` and `main.ts`.
148
150
 
149
- `index.d.ts` declares the types available to the library. `main.ts` contains the main implementation and the public components that belong directly to that entry point.
151
+ `index.d.ts` declares the types available to the library. `main.ts` starts as a placeholder for the main implementation and the public components that belong directly to that entry point.
150
152
 
151
153
  ### Shared code
152
154
 
@@ -195,7 +197,7 @@ An application service combines capabilities to complete an operation. For examp
195
197
 
196
198
  #### Context root
197
199
 
198
- The root contains `index.ts` and other `.ts` files intended for the context's ports.
200
+ The generated context root contains `example-ports.ts` as a starting point for the context's ports. You can keep that file, replace it, or add `index.ts` and other `.ts` files at the context root as the communication surface grows.
199
201
 
200
202
  A port describes a form of communication between the context and another system. It defines the received data, the returned data, and the operation available at that boundary.
201
203
 
@@ -225,7 +227,7 @@ The application occupies the middle layer and uses those capabilities to build p
225
227
 
226
228
  Ports and adapters occupy the outer layer and handle communication between systems.
227
229
 
228
- A port declares the communication available at the context boundary: what data enters, what data leaves, and which operation is exposed. Ports are declared in `index.ts` or in `.ts` modules located at the context root.
230
+ A port declares the communication available at the context boundary: what data enters, what data leaves, and which operation is exposed. Ports are declared in `.ts` modules located at the context root. Many projects centralize them in `index.ts`, but the generated template starts with `example-ports.ts`.
229
231
 
230
232
  An adapter implements that communication. It imports the corresponding port, translates external input or output into the application process format, and delegates the work to the application service.
231
233
 
@@ -237,7 +239,7 @@ adapter → application
237
239
  application → domain
238
240
  ```
239
241
 
240
- For example, `users/index.ts` declares the `CreateUserPort` port. `users/adapters/create-user-adapter.ts` imports that port and connects an external request to `users/application/create-user-service.ts`. The service applies the capabilities of `Email` and `User` to complete the registration.
242
+ One possible arrangement is to keep `users/example-ports.ts` at the context root and replace the starter export with your own contracts as the context grows. In this guide, the examples stay close to the generated files: value objects extend `ValueObject`, entities extend `Entity`, services extend `Service`, repositories extend `Repository`, and ports live at the context root.
241
243
 
242
244
  > **Tip:** start the implementation inside the context and move a component to `shared` when its meaning and use belong to multiple contexts.
243
245
 
@@ -286,16 +288,17 @@ The `shared/domain/value-objects.ts` file generates the `ValueObject<T>` base cl
286
288
  ```ts
287
289
  import { Email } from './core/shared/domain/value-objects.js'
288
290
 
289
- const email = Email.from('alejandro@example.com')
291
+ const email = Email.from('ana@example.com')
290
292
 
291
293
  email.value
294
+ email.username
292
295
  email.domain
293
296
  ```
294
297
 
295
298
  Creation is performed with `Email.from()`. This method runs `Email.isValid()` before constructing the instance.
296
299
 
297
300
  ```ts
298
- Email.isValid('alejandro@example.com')
301
+ Email.isValid('ana@example.com')
299
302
  ```
300
303
 
301
304
  Every creation follows the same validity rule:
@@ -321,53 +324,53 @@ When validation fails, `from()` throws `ValueError`.
321
324
  ```ts
322
325
  import { NullableBoolean } from './core/shared/domain/value-objects.js'
323
326
 
324
- const status = NullableBoolean.from(null)
327
+ const active = NullableBoolean.from(null)
325
328
 
326
- status.value
327
- status.isIndeterminate()
329
+ active.value
330
+ active.isIndeterminate()
328
331
  ```
329
332
 
330
333
  The `isIndeterminate()` method expresses an operation specific to the concept and allows the `null` state to be checked with explicit intent.
331
334
 
332
335
  #### Implement a value object
333
336
 
334
- We will create a discount percentage. The example has only one rule and one operation so the focus remains on the value object concept.
335
-
336
- **`sales/domain/discount-percentage.ts`**
337
+ We will create a user name. The example has one rule and one small operation so the focus stays on the value object itself.
337
338
 
338
339
  ```ts
339
340
  import { ValueError } from '../../shared/domain/errors.js'
340
341
  import { ValueObject } from '../../shared/domain/value-objects.js'
341
342
 
342
- export class DiscountPercentage extends ValueObject<number> {
343
- public override readonly value: number
343
+ export class UserName extends ValueObject<string> {
344
+ public override readonly value: string
344
345
 
345
- protected constructor(value: number) {
346
+ protected constructor(value: string) {
346
347
  super()
347
348
  this.value = value
348
349
  }
349
350
 
350
351
  public override equals(
351
- other: DiscountPercentage | null | undefined
352
+ other: UserName | null | undefined
352
353
  ): boolean {
353
- return other instanceof DiscountPercentage &&
354
+ return other instanceof UserName &&
354
355
  this.value === other.value
355
356
  }
356
357
 
357
- public applyTo(amount: number): number {
358
- return amount - amount * (this.value / 100)
358
+ public normalized(): string {
359
+ return this.value.trim().toLowerCase()
359
360
  }
360
361
 
361
362
  public static override isValid(value: unknown): boolean {
362
- return typeof value === 'number' &&
363
- Number.isFinite(value) &&
364
- value >= 0 &&
365
- value <= 100
363
+ if (super.isValid(value) === false) {
364
+ return false
365
+ }
366
+
367
+ return typeof value === 'string' &&
368
+ value.trim().length > 0
366
369
  }
367
370
 
368
- public static from(value: number): DiscountPercentage {
371
+ public static from(value: string): UserName {
369
372
  if (this.isValid(value) === false) {
370
- throw new ValueError(String(value), this.name)
373
+ throw new ValueError(value, this.name)
371
374
  }
372
375
 
373
376
  return new this(value)
@@ -378,21 +381,19 @@ export class DiscountPercentage extends ValueObject<number> {
378
381
  Now we can create and use the concept:
379
382
 
380
383
  ```ts
381
- const discount = DiscountPercentage.from(15)
382
- const finalPrice = discount.applyTo(100)
384
+ const name = UserName.from('Ana')
385
+ const normalized = name.normalized()
383
386
  ```
384
387
 
385
- `isValid()` centralizes the rule. `from()` creates the valid instance. `applyTo()` adds behavior specific to the concept.
388
+ `isValid()` centralizes the rule. `from()` creates the valid instance. `normalized()` adds behavior specific to the concept.
386
389
 
387
390
  ### Entities
388
391
 
389
392
  An entity represents a concept with its own identity. Two instances represent the same element when they share that identity.
390
393
 
391
- The `shared/domain/entities.ts` file generates the `Entity` base class. Each entity implements `equals()` and `toJSON()`.
394
+ The `shared/domain/entities.ts` file generates the `Entity` base class. Each entity implements `equals()`. The inherited `toJSON()` method can be overridden when you want to return a plain object.
392
395
 
393
- We will create an entity for the `users` context.
394
-
395
- **`users/domain/user.ts`**
396
+ This example creates an entity for the `users` context.
396
397
 
397
398
  ```ts
398
399
  import { Entity } from '../../shared/domain/entities.js'
@@ -401,9 +402,14 @@ import {
401
402
  NullableBoolean
402
403
  } from '../../shared/domain/value-objects.js'
403
404
 
405
+ type UserName = {
406
+ value: string
407
+ }
408
+
404
409
  export class User extends Entity {
405
410
  public constructor(
406
411
  public readonly id: string,
412
+ public readonly name: UserName,
407
413
  public readonly email: Email,
408
414
  public readonly active: NullableBoolean
409
415
  ) {
@@ -417,6 +423,7 @@ export class User extends Entity {
417
423
  public override toJSON(): Record<string, unknown> {
418
424
  return {
419
425
  id: this.id,
426
+ name: this.name.value,
420
427
  email: this.email.value,
421
428
  active: this.active.value
422
429
  }
@@ -431,50 +438,48 @@ The `id` property defines the identity. The `equals()` method compares entities
431
438
  ```ts
432
439
  const user = new User(
433
440
  'user-1',
434
- Email.from('alejandro@example.com'),
441
+ UserName.from('Ana'),
442
+ Email.from('ana@example.com'),
435
443
  NullableBoolean.from(true)
436
444
  )
437
445
 
438
446
  const json = user.toJSON()
439
447
  ```
440
448
 
441
- The result uses the internal values of `Email` and `NullableBoolean`.
449
+ The result uses the internal values of `UserName`, `Email`, and `NullableBoolean`.
442
450
 
443
451
  ### Aggregates
444
452
 
445
453
  An aggregate groups multiple entities into a logical unit. The aggregate's operations depend on all the identities that compose it.
446
454
 
447
- A team can group one leader and several members:
455
+ A user list can group several users:
448
456
 
449
457
  ```text
450
- Team
451
- ├── leader
452
- └── members
458
+ UserList
459
+ └── items
453
460
  ```
454
461
 
455
462
  Each element preserves its own identity within the unit.
456
463
 
457
- **`users/domain/team.ts`**
458
-
459
464
  ```ts
460
465
  import { Aggregate } from '../../shared/domain/aggregates.js'
461
- import type { User } from './user.js'
462
466
 
463
- export class Team extends Aggregate {
464
- public constructor(
465
- public readonly leader: User,
466
- public readonly members: User[]
467
- ) {
467
+ type User = {
468
+ id: string
469
+ }
470
+
471
+ export class UserList extends Aggregate {
472
+ public constructor(public readonly items: User[]) {
468
473
  super()
469
474
  }
470
475
 
471
- public size(): number {
472
- return this.members.length + 1
476
+ public count(): number {
477
+ return this.items.length
473
478
  }
474
479
  }
475
480
  ```
476
481
 
477
- `Team` groups multiple `User` entities into a single logical unit. The `size()` method operates on that group.
482
+ `UserList` groups multiple `User` entities into a single logical unit. The `count()` method operates on that group.
478
483
 
479
484
  ### Domain errors
480
485
 
@@ -483,8 +488,8 @@ The `shared/domain/errors.ts` file generates `ValueError`. This error represents
483
488
  ```ts
484
489
  import { ValueError } from '../../shared/domain/errors.js'
485
490
 
486
- if (quantity <= 0) {
487
- throw new ValueError(String(quantity), 'PositiveQuantity')
491
+ if (userId.trim().length === 0) {
492
+ throw new ValueError(userId, 'UserId')
488
493
  }
489
494
  ```
490
495
 
@@ -500,35 +505,34 @@ The `shared/application/validations.ts` file declares the `Validatable` contract
500
505
 
501
506
  ```ts
502
507
  isValid(): boolean
503
- validate(): unknown
504
508
  ```
505
509
 
506
- `isValid()` checks the validation state. `validate()` performs the validation and returns the result defined by the implementation.
510
+ `isValid()` checks whether the data is valid according to the rule defined by the implementation.
507
511
 
508
512
  We will create a validation for the use case that registers users.
509
513
 
510
- **`users/application/create-user-validation.ts`**
511
-
512
514
  ```ts
513
515
  import type { Validatable } from '../../shared/application/validations.js'
514
516
  import { Email } from '../../shared/domain/value-objects.js'
515
517
 
518
+ export type CreateUserValidationData = {
519
+ name: string
520
+ email: string
521
+ }
522
+
516
523
  export class CreateUserValidation implements Validatable {
517
- public constructor(private readonly email: string) {}
524
+ public constructor(
525
+ private readonly data: CreateUserValidationData
526
+ ) {}
518
527
 
519
528
  public isValid(): boolean {
520
- return Email.isValid(this.email)
521
- }
522
-
523
- public validate(): string[] {
524
- return this.isValid()
525
- ? []
526
- : ['The email is invalid.']
529
+ return this.data.name.trim().length > 0 &&
530
+ Email.isValid(this.data.email)
527
531
  }
528
532
  }
529
533
  ```
530
534
 
531
- The application service can run this validation before constructing the `User` entity.
535
+ In this example, the application service runs this validation before constructing the `User` entity.
532
536
 
533
537
  ### Services
534
538
 
@@ -538,30 +542,59 @@ The `shared/application/services.ts` file generates the `Service` base class. A
538
542
 
539
543
  We will begin with a service that creates a user.
540
544
 
541
- **`users/application/create-user-service.ts`**
542
-
543
545
  ```ts
546
+ import type { Validatable } from '../../shared/application/validations.js'
544
547
  import { Service } from '../../shared/application/services.js'
545
548
  import {
546
549
  Email,
547
550
  NullableBoolean
548
551
  } from '../../shared/domain/value-objects.js'
549
- import { User } from '../domain/user.js'
550
- import { CreateUserValidation } from './create-user-validation.js'
551
-
552
- export interface UserWriter {
553
- save(user: User): Promise<void>
554
- }
555
552
 
556
- export type CreateUserCommand = {
553
+ export type CreateUserData = {
557
554
  id: string
555
+ name: string
558
556
  email: string
559
557
  }
560
558
 
561
- export type CreatedUser = {
562
- id: string
563
- email: string
564
- active: boolean | null
559
+ export class UserName {
560
+ public constructor(public readonly value: string) {}
561
+
562
+ public static from(value: string): UserName {
563
+ return new UserName(value.trim())
564
+ }
565
+ }
566
+
567
+ export class User {
568
+ public constructor(
569
+ public readonly id: string,
570
+ public readonly name: UserName,
571
+ public readonly email: Email,
572
+ public readonly active: NullableBoolean
573
+ ) {}
574
+ }
575
+
576
+ export class CreateUserValidation implements Validatable {
577
+ public constructor(
578
+ private readonly data: CreateUserData
579
+ ) {}
580
+
581
+ public isValid(): boolean {
582
+ return this.data.name.trim().length > 0 &&
583
+ Email.isValid(this.data.email)
584
+ }
585
+ }
586
+
587
+ export interface UserWriter {
588
+ save(data: { user: User }): Promise<void>
589
+ }
590
+
591
+ export type CreateUserResult = {
592
+ user: {
593
+ id: string
594
+ name: string
595
+ email: string
596
+ active: boolean | null
597
+ }
565
598
  }
566
599
 
567
600
  export class CreateUserService extends Service {
@@ -569,78 +602,102 @@ export class CreateUserService extends Service {
569
602
  super()
570
603
  }
571
604
 
572
- public async execute(
573
- command: CreateUserCommand
574
- ): Promise<CreatedUser> {
575
- const validation = new CreateUserValidation(command.email)
576
- const errors = validation.validate()
605
+ public async execute(data: CreateUserData): Promise<CreateUserResult> {
606
+ const validation = new CreateUserValidation(data)
577
607
 
578
- if (errors.length !== 0) {
579
- throw new Error(errors.join(', '))
608
+ if (validation.isValid() === false) {
609
+ throw new Error('The name or email is invalid.')
580
610
  }
581
611
 
582
612
  const user = new User(
583
- command.id,
584
- Email.from(command.email),
613
+ data.id,
614
+ UserName.from(data.name),
615
+ Email.from(data.email),
585
616
  NullableBoolean.from(true)
586
617
  )
587
618
 
588
- await this.users.save(user)
619
+ await this.users.save({ user })
589
620
 
590
621
  return {
591
- id: user.id,
592
- email: user.email.value,
593
- active: user.active.value
622
+ user: {
623
+ id: user.id,
624
+ name: user.name.value,
625
+ email: user.email.value,
626
+ active: user.active.value
627
+ }
594
628
  }
595
629
  }
596
630
  }
597
631
  ```
598
632
 
599
- The service receives a command, validates the input, constructs the entity, and delegates persistence. `CreateUserCommand` expresses the process input. `CreatedUser` expresses the output.
633
+ In this example, the service receives one object, validates it, constructs the entity, delegates persistence, and returns another object with the result.
600
634
 
601
635
  ### HTTP responses
602
636
 
603
- The `shared/application/http.ts` file generates `HttpResponseBody`, a response body designed for REST APIs.
637
+ The `shared/application/http.ts` file generates lightweight HTTP contracts: `HttpRequest`, `HttpResponse`, `HttpResponseBody`, `HttpRequestHandler`, and `HttpMiddleware`.
604
638
 
605
- The class organizes the response into three properties:
639
+ `HttpResponseBody` is an interface organized into three properties:
606
640
 
607
641
  ```ts
608
- new HttpResponseBody(data, errors, links)
642
+ import type { HttpResponseBody } from '../../shared/application/http.js'
643
+
644
+ const body: HttpResponseBody = {
645
+ data,
646
+ errors,
647
+ links
648
+ }
609
649
  ```
610
650
 
611
651
  `data` contains the operation data and accepts `null` when the response has no data:
612
652
 
613
653
  ```ts
614
- const body = new HttpResponseBody({ id: 'user-1' })
654
+ const body: HttpResponseBody = {
655
+ data: {
656
+ id: 'user-1',
657
+ name: 'Ana'
658
+ },
659
+ errors: null,
660
+ links: null
661
+ }
615
662
  ```
616
663
 
617
664
  `errors` contains a list of messages:
618
665
 
619
666
  ```ts
620
- const body = new HttpResponseBody(
621
- null,
622
- ['The email is invalid.']
623
- )
667
+ const body: HttpResponseBody = {
668
+ data: null,
669
+ errors: ['The email is invalid.'],
670
+ links: null
671
+ }
624
672
  ```
625
673
 
626
674
  `links` contains HATEOAS links related to the resource and its available operations:
627
675
 
628
676
  ```ts
629
- const body = new HttpResponseBody(
630
- { id: 'user-1' },
631
- null,
632
- {
677
+ const body: HttpResponseBody = {
678
+ data: {
679
+ id: 'user-1',
680
+ name: 'Ana'
681
+ },
682
+ errors: null,
683
+ links: {
633
684
  self: new URL('https://api.example.com/users/user-1')
634
685
  }
635
- )
686
+ }
636
687
  ```
637
688
 
638
689
  An entity's plain output can be used as `data`:
639
690
 
640
691
  ```ts
641
- const body = new HttpResponseBody(user.toJSON())
692
+ const body: HttpResponseBody = {
693
+ data: user.toJSON(),
694
+ errors: null,
695
+ links: null
696
+ }
642
697
  ```
643
698
 
699
+ `HttpRequestHandler` and `HttpMiddleware` define the contracts for adapters that receive a request, delegate to a handler, and produce a response.
700
+
644
701
  ### Logs
645
702
 
646
703
  The `shared/application/loggers.ts` file declares the `Logger` contract. The application uses this abstraction to produce logs that an adapter sends to external services.
@@ -649,8 +706,6 @@ The contract includes the `debug`, `info`, `warning`, `error`, and `critical` le
649
706
 
650
707
  We will create a simple adapter that connects the contract to the console.
651
708
 
652
- **`users/adapters/console-logger-adapter.ts`**
653
-
654
709
  ```ts
655
710
  import { Logger } from '../../shared/application/loggers.js'
656
711
 
@@ -677,7 +732,7 @@ export class ConsoleLoggerAdapter extends Logger {
677
732
  }
678
733
  ```
679
734
 
680
- The service receives `Logger` as a dependency. The adapter decides where to send each level.
735
+ In this example, the service receives `Logger` as a dependency. The adapter decides where to send each level.
681
736
 
682
737
  ### Events
683
738
 
@@ -701,14 +756,12 @@ EventHandler
701
756
 
702
757
  `Event` represents something that occurred in the application. It contains the event time and its plain details.
703
758
 
704
- **`users/application/user-created.ts`**
705
-
706
759
  ```ts
707
760
  import { Event } from '../../shared/application/events.js'
708
761
 
709
762
  export class UserCreated extends Event {
710
- public constructor(userId: string) {
711
- super(Date.now(), { userId })
763
+ public constructor(details: { userId: string }) {
764
+ super(Date.now(), details)
712
765
  }
713
766
  }
714
767
  ```
@@ -717,8 +770,6 @@ export class UserCreated extends Event {
717
770
 
718
771
  `EventHandler` represents a reaction to the event.
719
772
 
720
- **`users/application/log-user-created.ts`**
721
-
722
773
  ```ts
723
774
  import {
724
775
  Event,
@@ -747,16 +798,16 @@ unsubscribe(key, handler)
747
798
  dispatch(event)
748
799
  ```
749
800
 
750
- The service can receive `EventDispatcher` and publish `UserCreated` after completing the use case.
801
+ In one possible implementation, the service can receive `EventDispatcher` and publish `UserCreated` after completing the use case.
751
802
 
752
803
  ## Data sources
753
804
 
754
- The `shared/application/data-sources.ts` file organizes access to a data source into four components: `DriverManager`, `DataManager`, `DatasetManager`, and `Repository`.
805
+ The `shared/application/data/` directory organizes access to a data source into three modules: `drivers.ts`, `managers.ts`, and `repositories.ts`. Together, they define `DriverAdapter`, `DataManager`, `DatasetManager`, and `Repository`.
755
806
 
756
807
  The complete flow looks like this:
757
808
 
758
809
  ```text
759
- DriverManager
810
+ DriverAdapter
760
811
  ↓ connects and enables
761
812
  DataManager or DatasetManager
762
813
  ↓ provides plain data to
@@ -769,37 +820,47 @@ Service
769
820
 
770
821
  We will begin with the connection and proceed to the use case.
771
822
 
772
- ### Driver manager
823
+ ### Driver adapter
773
824
 
774
- `DriverManager` connects and disconnects the source through a driver. When the connection is available, `connect()` returns an enabled data manager.
825
+ `DriverAdapter` connects and disconnects the source through a driver. When the connection is available, `connect()` returns an enabled data manager.
775
826
 
776
827
  ```ts
777
828
  connect(...args): Promise<DataManager>
778
829
  disconnect(): Promise<unknown>
779
830
  ```
780
831
 
781
- We will create a manager for an in-memory collection of people. The example avoids a database so the focus remains on the manager's responsibility.
782
-
783
- **`users/adapters/people-driver-manager.ts`**
832
+ We will create an adapter for an in-memory collection of users. This example avoids a database so the focus stays on the adapter's responsibility, not on infrastructure details.
784
833
 
785
834
  ```ts
786
- import { DriverManager } from '../../shared/application/data-sources.js'
787
- import { PeopleDataManager } from './people-data-manager.js'
835
+ import { DriverAdapter } from '../../shared/application/data/drivers.js'
836
+ import { DataManager } from '../../shared/application/data/managers.js'
788
837
 
789
- export type PersonRecord = {
838
+ type UserRecord = {
790
839
  id: string
791
840
  name: string
841
+ email: string
842
+ active: boolean | null
843
+ }
844
+
845
+ class UserDataManager extends DataManager<UserRecord> {
846
+ public constructor(private readonly records: UserRecord[]) {
847
+ super()
848
+ }
849
+
850
+ public async all(): Promise<UserRecord[]> {
851
+ return this.records
852
+ }
792
853
  }
793
854
 
794
- export class PeopleDriverManager
795
- extends DriverManager<PeopleDataManager> {
855
+ export class UserDriverAdapter
856
+ extends DriverAdapter<UserDataManager> {
796
857
 
797
- public constructor(private readonly records: PersonRecord[]) {
858
+ public constructor(private readonly records: UserRecord[]) {
798
859
  super()
799
860
  }
800
861
 
801
- public async connect(): Promise<PeopleDataManager> {
802
- return new PeopleDataManager(this.records)
862
+ public async connect(): Promise<UserDataManager> {
863
+ return new UserDataManager(this.records)
803
864
  }
804
865
 
805
866
  public async disconnect(): Promise<void> {
@@ -811,54 +872,52 @@ The concrete implementation can encapsulate a database driver, an HTTP client, a
811
872
 
812
873
  ### Data manager
813
874
 
814
- `DataManager` operates on the source and works with plain objects and arrays. Its base form defines two operations:
875
+ `DataManager` operates on the source and works with plain objects and arrays. Its base form exposes one abstract operation and one default operation:
815
876
 
816
877
  ```ts
817
878
  all(): Promise<Array<T>>
818
879
  none(): Array<T>
819
880
  ```
820
881
 
821
- `all()` retrieves the available records. `none()` creates an empty typed collection.
882
+ `all()` retrieves the available records. `none()` already creates an empty typed collection.
822
883
 
823
884
  First, we will define the shape used by the source:
824
885
 
825
886
  ```ts
826
- type PersonRecord = {
887
+ type UserRecord = {
827
888
  id: string
828
889
  name: string
890
+ email: string
891
+ active: boolean | null
829
892
  }
830
893
  ```
831
894
 
832
895
  Now we will implement the data manager.
833
896
 
834
- **`users/adapters/people-data-manager.ts`**
835
-
836
897
  ```ts
837
- import { DataManager } from '../../shared/application/data-sources.js'
898
+ import { DataManager } from '../../shared/application/data/managers.js'
838
899
 
839
- export type PersonRecord = {
900
+ export type UserRecord = {
840
901
  id: string
841
902
  name: string
903
+ email: string
904
+ active: boolean | null
842
905
  }
843
906
 
844
- export class PeopleDataManager
845
- extends DataManager<PersonRecord> {
907
+ export class UserDataManager
908
+ extends DataManager<UserRecord> {
846
909
 
847
- public constructor(private readonly records: PersonRecord[]) {
910
+ public constructor(private readonly records: UserRecord[]) {
848
911
  super()
849
912
  }
850
913
 
851
- public async all(): Promise<PersonRecord[]> {
914
+ public async all(): Promise<UserRecord[]> {
852
915
  return this.records
853
916
  }
854
-
855
- public none(): PersonRecord[] {
856
- return []
857
- }
858
917
  }
859
918
  ```
860
919
 
861
- The data manager reflects the structure of the source. In this example, it only provides plain records.
920
+ The data manager reflects the structure of the source. In this example, it only exposes plain records.
862
921
 
863
922
  #### Source operations
864
923
 
@@ -881,36 +940,32 @@ import {
881
940
  Creatable,
882
941
  DataManager,
883
942
  Filterable
884
- } from '../../shared/application/data-sources.js'
943
+ } from '../../shared/application/data/managers.js'
885
944
 
886
- export class PeopleDataManager
887
- extends DataManager<PersonRecord>
945
+ export class UserDataManager
946
+ extends DataManager<UserRecord>
888
947
  implements
889
- Filterable<Partial<PersonRecord>>,
890
- Creatable<PersonRecord> {
948
+ Filterable<Partial<UserRecord>>,
949
+ Creatable<UserRecord> {
891
950
 
892
- public constructor(private readonly records: PersonRecord[]) {
951
+ public constructor(private readonly records: UserRecord[]) {
893
952
  super()
894
953
  }
895
954
 
896
- public async all(): Promise<PersonRecord[]> {
955
+ public async all(): Promise<UserRecord[]> {
897
956
  return this.records
898
957
  }
899
958
 
900
- public none(): PersonRecord[] {
901
- return []
902
- }
903
-
904
959
  public async filter(
905
- selector: Partial<PersonRecord>
906
- ): Promise<PersonRecord[]> {
960
+ selector: Partial<UserRecord>
961
+ ): Promise<UserRecord[]> {
907
962
  return this.records.filter((record) =>
908
963
  (selector.id === undefined || record.id === selector.id) &&
909
- (selector.name === undefined || record.name === selector.name)
964
+ (selector.email === undefined || record.email === selector.email)
910
965
  )
911
966
  }
912
967
 
913
- public async create(data: PersonRecord): Promise<void> {
968
+ public async create(data: UserRecord): Promise<void> {
914
969
  this.records.push(data)
915
970
  }
916
971
  }
@@ -919,8 +974,8 @@ export class PeopleDataManager
919
974
  It can also declare operations specific to source queries:
920
975
 
921
976
  ```ts
922
- public async findByName(name: string): Promise<PersonRecord[]> {
923
- return this.filter({ name })
977
+ public async findByEmail(query: { email: string }): Promise<UserRecord[]> {
978
+ return this.filter({ email: query.email })
924
979
  }
925
980
  ```
926
981
 
@@ -934,7 +989,7 @@ The application layer decides when to execute these operations, combines their r
934
989
  union()
935
990
  intersection()
936
991
  difference()
937
- symmetric_difference()
992
+ symmetricDifference()
938
993
  complement()
939
994
  ```
940
995
 
@@ -945,143 +1000,138 @@ This implementation is useful when an operation works with unions, intersections
945
1000
  `Repository` acts as an intermediary between plain data and domain objects.
946
1001
 
947
1002
  ```text
948
- PersonRecord
1003
+ UserRecord
949
1004
  ↓ transform
950
- Person
951
-
952
- Person
953
- ↓ toRecord
954
- PersonRecord
1005
+ User
955
1006
  ```
956
1007
 
957
- In this example, the source and the domain have nearly the same shape so the focus remains on the repository's role: translating between plain data and domain objects.
1008
+ The generated base class focuses on reading plain data and transforming it into domain objects. Using the `UserDriverAdapter` from the previous example, a repository can stay very small.
958
1009
 
959
- **`users/domain/person.ts`**
1010
+ Now we will implement the repository.
960
1011
 
961
1012
  ```ts
962
- import { Entity } from '../../shared/domain/entities.js'
1013
+ import { DriverAdapter } from '../../shared/application/data/drivers.js'
1014
+ import { DataManager } from '../../shared/application/data/managers.js'
1015
+ import { Repository } from '../../shared/application/data/repositories.js'
1016
+ import {
1017
+ Email,
1018
+ NullableBoolean
1019
+ } from '../../shared/domain/value-objects.js'
963
1020
 
964
- export class Person extends Entity {
965
- public constructor(
966
- public readonly id: string,
967
- public readonly name: string
968
- ) {
969
- super()
970
- }
1021
+ type UserRecord = {
1022
+ id: string
1023
+ name: string
1024
+ email: string
1025
+ active: boolean | null
1026
+ }
971
1027
 
972
- public override equals(other: Entity): boolean {
973
- return other instanceof Person && other.id === this.id
974
- }
1028
+ class UserName {
1029
+ public constructor(public readonly value: string) {}
975
1030
 
976
- public override toJSON(): Record<string, unknown> {
977
- return {
978
- id: this.id,
979
- name: this.name
980
- }
1031
+ public static from(value: string): UserName {
1032
+ return new UserName(value)
981
1033
  }
982
1034
  }
983
- ```
984
-
985
- Now we will implement the repository.
986
1035
 
987
- **`users/adapters/people-repository.ts`**
988
-
989
- ```ts
990
- import { Repository } from '../../shared/application/data-sources.js'
991
- import type { PeopleReader } from '../application/list-people-service.js'
992
- import { Person } from '../domain/person.js'
993
- import type { PersonRecord } from './people-data-manager.js'
994
- import { PeopleDriverManager } from './people-driver-manager.js'
995
-
996
- export class PeopleRepository
997
- extends Repository<PeopleDriverManager>
998
- implements PeopleReader {
999
-
1000
- protected transform<T = Person>(data: Generic): T {
1001
- const record = data as PersonRecord
1036
+ class User {
1037
+ public constructor(
1038
+ public readonly id: string,
1039
+ public readonly name: UserName,
1040
+ public readonly email: Email,
1041
+ public readonly active: NullableBoolean
1042
+ ) {}
1043
+ }
1002
1044
 
1003
- return new Person(record.id, record.name) as T
1004
- }
1045
+ export class UserRepository
1046
+ extends Repository<UserRecord, User> {
1005
1047
 
1006
- private toRecord(person: Person): PersonRecord {
1007
- return {
1008
- id: person.id,
1009
- name: person.name
1010
- }
1048
+ public constructor(
1049
+ driver: DriverAdapter<DataManager<UserRecord>>
1050
+ ) {
1051
+ super(driver)
1011
1052
  }
1012
1053
 
1013
- public async findAll(): Promise<Person[]> {
1014
- const dataManager = await this.manager.connect()
1015
- const records = await dataManager.all()
1016
-
1017
- return records.map((record) =>
1018
- this.transform<Person>(record)
1054
+ protected override transform(data: UserRecord): User {
1055
+ return new User(
1056
+ data.id,
1057
+ UserName.from(data.name),
1058
+ Email.from(data.email),
1059
+ NullableBoolean.from(data.active)
1019
1060
  )
1020
1061
  }
1021
-
1022
- public async save(person: Person): Promise<void> {
1023
- const dataManager = await this.manager.connect()
1024
-
1025
- await dataManager.create(this.toRecord(person))
1026
- }
1027
1062
  }
1028
1063
  ```
1029
1064
 
1030
- `transform()` converts the record into an entity. `toRecord()` performs the reverse conversion.
1065
+ Here, `transform()` converts each record into an entity. The constructor receives any driver compatible with `DataManager<UserRecord>`, and the inherited `all()` method already handles the connect, read, transform, and disconnect flow.
1031
1066
 
1032
1067
  ### Queries and errors in the application
1033
1068
 
1034
1069
  Application services coordinate queries and catch errors from data sources. The process expresses the collaboration capabilities it needs through application-specific contracts. Adapters materialize those contracts.
1035
1070
 
1036
- **`users/application/list-people-service.ts`**
1037
-
1038
1071
  ```ts
1039
1072
  import { Service } from '../../shared/application/services.js'
1040
- import type { Person } from '../domain/person.js'
1041
1073
 
1042
- export interface PeopleReader {
1043
- findAll(): Promise<Person[]>
1074
+ type User = {
1075
+ id: string
1044
1076
  }
1045
1077
 
1046
- export class ListPeopleService extends Service {
1047
- public constructor(private readonly people: PeopleReader) {
1078
+ export interface UsersReader {
1079
+ all(): Promise<User[]>
1080
+ }
1081
+
1082
+ export class ListUsersService extends Service {
1083
+ public constructor(private readonly users: UsersReader) {
1048
1084
  super()
1049
1085
  }
1050
1086
 
1051
- public async execute(): Promise<Person[]> {
1087
+ public async execute(): Promise<{ users: User[] }> {
1052
1088
  try {
1053
- return await this.people.findAll()
1089
+ return {
1090
+ users: await this.users.all()
1091
+ }
1054
1092
  } catch {
1055
- throw new Error('Could not list people.')
1093
+ throw new Error('Could not list users.')
1056
1094
  }
1057
1095
  }
1058
1096
  }
1059
1097
  ```
1060
1098
 
1061
- `PeopleReader` expresses the collaboration required by the process. `PeopleRepository`, located in `adapters`, implements that collaboration and transforms source data into `Person` entities.
1099
+ `UsersReader` expresses the collaboration required by the process. `UserRepository`, located in `adapters`, already satisfies that collaboration through the inherited `all()` method.
1062
1100
 
1063
1101
  ## Context ports
1064
1102
 
1065
1103
  Ports describe communication between the context and other systems. Each port defines the shape of an interaction at the boundary: input data, output data, and the available operation.
1066
1104
 
1067
- The context generates `index.ts` as the main port file and `example-ports.ts` as an example of an additional file. Adapters import the ports that define the communication they materialize.
1105
+ The context generates `example-ports.ts` as a root-level starting point. As the context grows, you can add `index.ts` as the main port file or split ports across multiple `.ts` files. Adapters import the ports that define the communication they materialize.
1068
1106
 
1069
1107
  ### Main port
1070
1108
 
1071
- We will declare the communication for creating a user directly in `users/index.ts`.
1109
+ The generated `example-ports.ts` file is only a placeholder:
1072
1110
 
1073
- **`users/index.ts`**
1111
+ ```ts
1112
+ export function example(): void {
1113
+ // ...
1114
+ }
1115
+ ```
1116
+
1117
+ You can replace it with your own root-level contract. A minimal option for creating a user is:
1074
1118
 
1075
1119
  ```ts
1076
1120
  export type CreateUserRequest = {
1077
- id: string
1078
- email: string
1121
+ user: {
1122
+ id: string
1123
+ name: string
1124
+ email: string
1125
+ }
1079
1126
  }
1080
1127
 
1081
1128
  export type CreateUserResponse = {
1082
- id: string
1083
- email: string
1084
- active: boolean | null
1129
+ user: {
1130
+ id: string
1131
+ name: string
1132
+ email: string
1133
+ active: boolean | null
1134
+ }
1085
1135
  }
1086
1136
 
1087
1137
  export interface CreateUserPort {
@@ -1095,20 +1145,22 @@ export interface CreateUserPort {
1095
1145
 
1096
1146
  ### Adapt the port to the application process
1097
1147
 
1098
- The adapter imports the port and the service. Its responsibility is to translate the external request into the application command and transform the result into the port response.
1099
-
1100
- **`users/adapters/create-user-adapter.ts`**
1148
+ In this example, the adapter imports the port and the service. Its job here is to translate the root-level contract into the application input object and return the service result.
1101
1149
 
1102
1150
  ```ts
1103
1151
  import type {
1104
1152
  CreateUserPort,
1105
1153
  CreateUserRequest,
1106
1154
  CreateUserResponse
1107
- } from '../index.js'
1108
- import {
1109
- CreateUserService,
1110
- type CreateUserCommand
1111
- } from '../application/create-user-service.js'
1155
+ } from '../example-ports.js'
1156
+
1157
+ type CreateUserService = {
1158
+ execute(data: {
1159
+ id: string
1160
+ name: string
1161
+ email: string
1162
+ }): Promise<CreateUserResponse>
1163
+ }
1112
1164
 
1113
1165
  export class CreateUserAdapter implements CreateUserPort {
1114
1166
  public constructor(
@@ -1118,332 +1170,70 @@ export class CreateUserAdapter implements CreateUserPort {
1118
1170
  public async create(
1119
1171
  request: CreateUserRequest
1120
1172
  ): Promise<CreateUserResponse> {
1121
- const command: CreateUserCommand = {
1122
- id: request.id,
1123
- email: request.email
1124
- }
1125
-
1126
- const result = await this.service.execute(command)
1127
-
1128
- return {
1129
- id: result.id,
1130
- email: result.email,
1131
- active: result.active
1132
- }
1173
+ return this.service.execute({
1174
+ id: request.user.id,
1175
+ name: request.user.name,
1176
+ email: request.user.email
1177
+ })
1133
1178
  }
1134
1179
  }
1135
1180
  ```
1136
1181
 
1137
- The port expresses the communication. The adapter implements it. The service executes the process. The domain provides the capabilities used by that process.
1182
+ In this example, the port expresses the communication, the adapter implements it, the service executes the process, and the domain provides the capabilities used by that process.
1138
1183
 
1139
1184
  ### Additional ports
1140
1185
 
1141
1186
  A context can organize its communications across several files at the root. Each file declares the ports for a group of interactions.
1142
1187
 
1143
- **`users/example-ports.ts`**
1144
-
1145
- ```ts
1146
- export type FindUserRequest = {
1147
- id: string
1148
- }
1149
-
1150
- export type FindUserResponse = {
1151
- id: string
1152
- email: string
1153
- } | null
1154
-
1155
- export interface FindUserPort {
1156
- find(
1157
- request: FindUserRequest
1158
- ): Promise<FindUserResponse>
1159
- }
1160
- ```
1161
-
1162
- The corresponding adapter imports the contract from the file where it is declared:
1163
-
1164
1188
  ```ts
1165
- import type {
1166
- FindUserPort,
1167
- FindUserRequest,
1168
- FindUserResponse
1169
- } from '../example-ports.js'
1170
- ```
1171
-
1172
- > **Tip:** group ports that form a coherent communication in the same file. Use additional files when the context grows and groups of interactions with their own responsibilities emerge.
1173
-
1174
- ## Implement a context
1175
-
1176
- We will now walk through a complete implementation of `users`, following the layer order: capabilities, process, and communication.
1177
-
1178
- ### 1. Model the domain capabilities
1179
-
1180
- We begin with concepts that have rules and behavior. We use `Email` for the email address, `string` for identity, and an entity for the user.
1181
-
1182
- **`users/domain/user.ts`**
1183
-
1184
- ```ts
1185
- import { Entity } from '../../shared/domain/entities.js'
1186
- import {
1187
- Email,
1188
- NullableBoolean
1189
- } from '../../shared/domain/value-objects.js'
1190
-
1191
- export class User extends Entity {
1192
- public constructor(
1193
- public readonly id: string,
1194
- public readonly email: Email,
1195
- public readonly active: NullableBoolean
1196
- ) {
1197
- super()
1198
- }
1199
-
1200
- public override equals(other: Entity): boolean {
1201
- return other instanceof User && other.id === this.id
1202
- }
1203
-
1204
- public override toJSON(): Record<string, unknown> {
1205
- return {
1206
- id: this.id,
1207
- email: this.email.value,
1208
- active: this.active.value
1209
- }
1210
- }
1211
- }
1212
- ```
1213
-
1214
- `Email` provides the capability to validate and represent the email address. `User` provides user identity and representation.
1215
-
1216
- ### 2. Implement process validation
1217
-
1218
- The validation prepares the input used by the use case.
1219
-
1220
- **`users/application/create-user-validation.ts`**
1221
-
1222
- ```ts
1223
- import type { Validatable } from '../../shared/application/validations.js'
1224
- import { Email } from '../../shared/domain/value-objects.js'
1225
-
1226
- export class CreateUserValidation implements Validatable {
1227
- public constructor(private readonly email: string) {}
1228
-
1229
- public isValid(): boolean {
1230
- return Email.isValid(this.email)
1231
- }
1232
-
1233
- public validate(): string[] {
1234
- return this.isValid()
1235
- ? []
1236
- : ['The email is invalid.']
1237
- }
1238
- }
1239
- ```
1240
-
1241
- ### 3. Implement the application process
1242
-
1243
- The service receives its collaborators as dependencies and applies domain capabilities to complete the registration.
1244
-
1245
- **`users/application/create-user-service.ts`**
1246
-
1247
- ```ts
1248
- import { Service } from '../../shared/application/services.js'
1249
- import {
1250
- Email,
1251
- NullableBoolean
1252
- } from '../../shared/domain/value-objects.js'
1253
- import { User } from '../domain/user.js'
1254
- import { CreateUserValidation } from './create-user-validation.js'
1255
-
1256
- export interface UserWriter {
1257
- save(user: User): Promise<void>
1258
- }
1259
-
1260
- export type CreateUserCommand = {
1261
- id: string
1262
- email: string
1263
- }
1264
-
1265
- export type CreatedUser = {
1266
- id: string
1267
- email: string
1189
+ export type ListUsersRequest = {
1268
1190
  active: boolean | null
1269
1191
  }
1270
1192
 
1271
- export class CreateUserService extends Service {
1272
- public constructor(private readonly users: UserWriter) {
1273
- super()
1274
- }
1275
-
1276
- public async execute(
1277
- command: CreateUserCommand
1278
- ): Promise<CreatedUser> {
1279
- const validation = new CreateUserValidation(command.email)
1280
- const errors = validation.validate()
1281
-
1282
- if (errors.length !== 0) {
1283
- throw new Error(errors.join(', '))
1284
- }
1285
-
1286
- const user = new User(
1287
- command.id,
1288
- Email.from(command.email),
1289
- NullableBoolean.from(true)
1290
- )
1291
-
1292
- await this.users.save(user)
1293
-
1294
- return {
1295
- id: user.id,
1296
- email: user.email.value,
1297
- active: user.active.value
1298
- }
1299
- }
1300
- }
1301
- ```
1302
-
1303
- `UserWriter` declares the operation the service requires from the adapter. The flow has four steps: validate, construct the entity, save, and respond.
1304
-
1305
- ### 4. Declare the communication port
1306
-
1307
- The port defines how another system requests user creation.
1308
-
1309
- **`users/index.ts`**
1310
-
1311
- ```ts
1312
- export type CreateUserRequest = {
1313
- id: string
1314
- email: string
1315
- }
1316
-
1317
- export type CreateUserResponse = {
1318
- id: string
1319
- email: string
1320
- active: boolean | null
1193
+ export type ListUsersResponse = {
1194
+ users: Array<{
1195
+ id: string
1196
+ name: string
1197
+ email: string
1198
+ active: boolean | null
1199
+ }>
1321
1200
  }
1322
1201
 
1323
- export interface CreateUserPort {
1324
- create(
1325
- request: CreateUserRequest
1326
- ): Promise<CreateUserResponse>
1202
+ export interface ListUsersPort {
1203
+ list(request: ListUsersRequest): Promise<ListUsersResponse>
1327
1204
  }
1328
1205
  ```
1329
1206
 
1330
- ### 5. Implement the adapter
1331
-
1332
- The adapter imports the port, receives the external request, and executes the application process.
1333
-
1334
- **`users/adapters/create-user-adapter.ts`**
1207
+ An adapter can import the contract from the file where it is declared:
1335
1208
 
1336
1209
  ```ts
1337
1210
  import type {
1338
- CreateUserPort,
1339
- CreateUserRequest,
1340
- CreateUserResponse
1341
- } from '../index.js'
1342
- import {
1343
- CreateUserService,
1344
- type CreateUserCommand
1345
- } from '../application/create-user-service.js'
1346
-
1347
- export class CreateUserAdapter implements CreateUserPort {
1348
- public constructor(
1349
- private readonly service: CreateUserService
1350
- ) {}
1351
-
1352
- public async create(
1353
- request: CreateUserRequest
1354
- ): Promise<CreateUserResponse> {
1355
- const command: CreateUserCommand = {
1356
- id: request.id,
1357
- email: request.email
1358
- }
1359
-
1360
- const result = await this.service.execute(command)
1361
-
1362
- return {
1363
- id: result.id,
1364
- email: result.email,
1365
- active: result.active
1366
- }
1367
- }
1368
- }
1369
- ```
1370
-
1371
- The complete communication follows this path:
1372
-
1373
- ```text
1374
- External request
1375
-
1376
- CreateUserAdapter
1377
-
1378
- CreateUserService
1379
-
1380
- Validation + Email + User
1381
-
1382
- UserWriter
1383
-
1384
- External response
1385
- ```
1386
-
1387
- ### 6. Compose the implementation in `main.ts`
1388
-
1389
- `main.ts` brings together the library's main implementations. The composition creates the service and provides it to the adapter.
1390
-
1391
- **`core/main.ts`**
1392
-
1393
- ```ts
1394
- import { CreateUserService } from './users/application/create-user-service.js'
1395
- import { CreateUserAdapter } from './users/adapters/create-user-adapter.js'
1396
- import { InMemoryUserRepository } from './users/adapters/in-memory-user-repository.js'
1397
-
1398
- export class CoreApplication {
1399
- public readonly users: CreateUserAdapter
1400
-
1401
- public constructor() {
1402
- const users = new InMemoryUserRepository()
1403
- const createUserService = new CreateUserService(users)
1404
-
1405
- this.users = new CreateUserAdapter(createUserService)
1406
- }
1407
- }
1408
- ```
1409
-
1410
- `CoreApplication` provides a minimal composition: a concrete repository, a service, and an adapter.
1411
-
1412
- ### 7. Use the implementation
1413
-
1414
- The consumer uses the library's main entry point and accesses the adapter prepared in `main.ts`.
1415
-
1416
- ```ts
1417
- import { CoreApplication } from './core/main.js'
1418
-
1419
- const core = new CoreApplication()
1420
-
1421
- const result = await core.users.create({
1422
- id: 'user-1',
1423
- email: 'alejandro@example.com'
1424
- })
1211
+ ListUsersPort,
1212
+ ListUsersResponse
1213
+ } from '../example-ports.js'
1425
1214
  ```
1426
1215
 
1427
- The adapter receives the request, applies the port, executes the service, and returns the response.
1216
+ > **Tip:** group ports that form a coherent communication in the same file. Use additional files when the context grows and groups of interactions with their own responsibilities emerge.
1428
1217
 
1429
1218
  ## Generated file reference
1430
1219
 
1431
1220
  | File | Purpose |
1432
1221
  | --- | --- |
1433
1222
  | `core/index.d.ts` | Declares `Generic<T>` for plain objects. |
1434
- | `core/main.ts` | Contains the library's main implementation. |
1223
+ | `core/main.ts` | Starts as a placeholder for the library's main implementation and exports. |
1435
1224
  | `shared/domain/value-objects.ts` | Declares `ValueObject<T>` and implements `Email` and `NullableBoolean`. |
1436
1225
  | `shared/domain/entities.ts` | Declares the `Entity` base class. |
1437
1226
  | `shared/domain/aggregates.ts` | Declares the `Aggregate` base class. |
1438
1227
  | `shared/domain/errors.ts` | Implements `ValueError`. |
1439
1228
  | `shared/application/validations.ts` | Declares `Validatable`. |
1440
1229
  | `shared/application/services.ts` | Declares `Service` as the base class for use cases. |
1441
- | `shared/application/http.ts` | Implements `HttpResponseBody` for REST responses and HATEOAS links. |
1230
+ | `shared/application/http.ts` | Declares HTTP request, response, body, handler, and middleware contracts. |
1442
1231
  | `shared/application/loggers.ts` | Declares log levels and the `Logger` contract. |
1443
1232
  | `shared/application/events.ts` | Declares `Event`, `EventHandler`, and `EventDispatcher`. |
1444
- | `shared/application/data-sources.ts` | Declares source operations, managers, and repositories. |
1445
- | `users/index.ts` | Declares the context's main communication ports. |
1446
- | `users/example-ports.ts` | Shows an additional communication port file. |
1233
+ | `shared/application/data/drivers.ts` | Declares the `DriverAdapter` contract used to connect to a data source. |
1234
+ | `shared/application/data/managers.ts` | Declares source operations together with `DataManager` and `DatasetManager`. |
1235
+ | `shared/application/data/repositories.ts` | Declares the `Repository` base class for transforming records into domain objects. |
1236
+ | `users/example-ports.ts` | Provides a root-level starter file with a placeholder export for your context ports. |
1447
1237
  | `users/domain/` | Contains the context's capabilities. |
1448
1238
  | `users/application/` | Contains processes that apply domain capabilities to fulfill purposes. |
1449
1239
  | `users/adapters/` | Contains integrations that import ports and connect the context with other systems. |