@xeno-js/shared 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,678 +1,675 @@
1
- <div align="center">
2
- <img src="logo/logo.png" alt="Xeno.JS Shared Logo" width="140" />
3
-
4
- <h1>@xeno-js/shared</h1>
5
-
6
- <p><strong>Domain primitives and application contracts for TypeScript.</strong></p>
7
-
8
- <p>
9
- Define your domain model, application contracts, and architectural boundaries
10
- without coupling them to a transport or framework.
11
- </p>
12
-
13
- <p>
14
- <a href="https://github.com/xeno-js/xeno-shared">
15
- <img src="https://img.shields.io/github/stars/xeno-js/xeno-shared?style=flat-square" alt="GitHub Stars" />
16
- </a>
17
- <a href="https://www.npmjs.com/package/@xeno-js/shared">
18
- <img src="https://img.shields.io/npm/v/@xeno-js/shared?style=flat-square" alt="npm version" />
19
- </a>
20
- <a href="https://github.com/xeno-js/xeno-shared/blob/develop/LICENSE">
21
- <img src="https://img.shields.io/npm/l/@xeno-js/shared?style=flat-square" alt="License: MIT" />
22
- </a>
23
- <a href="https://buymeacoffee.com/xenojs">
24
- <img src="https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-FFdd00?style=flat-square&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" />
25
- </a>
26
- </p>
27
- </div>
28
-
29
- ---
30
-
31
- ## What is `@xeno-js/shared`?
32
-
33
- `@xeno-js/shared` provides the **domain primitives and framework-neutral
34
- contracts** used across the Xeno.JS ecosystem.
35
-
36
- It gives TypeScript applications explicit building blocks for:
37
-
38
- - Domain-Driven Design
39
- - aggregates and value objects
40
- - domain events
41
- - entities and domain errors
42
- - Result-based application flows
43
- - CQRS contracts
44
- - repositories and data sources
45
- - application services and policies
46
- - request and execution context
47
- - transactions and infrastructure boundaries
48
-
49
- The goal is simple:
50
-
51
- > **Keep the meaning of your application explicit in code.**
52
-
53
- `@xeno-js/shared` defines the contracts.
54
-
55
- `@xeno-js/core` provides the runtime architecture that executes them.
56
-
57
- Your HTTP framework, CLI, worker, or other transport remains outside that
58
- boundary.
59
-
60
- ---
61
-
62
- ## The Xeno.JS architecture
63
-
64
- Xeno.JS separates **what an application means** from **how the application
65
- runs**.
66
-
67
- ```text
68
- ┌─────────────────────────────────────────────┐
69
- │ Transport / Host │
70
- │ HTTP · CLI · Worker · gRPC · Scheduler │
71
- └──────────────────────┬──────────────────────┘
72
- │
73
- ▼
74
- ┌─────────────────────────────────────────────┐
75
- │ @xeno-js/core │
76
- │ │
77
- │ DI · scopes · request context · pipelines │
78
- │ CQRS execution · modules · infrastructure │
79
- └──────────────────────┬──────────────────────┘
80
- │
81
- ▼
82
- ┌─────────────────────────────────────────────┐
83
- │ @xeno-js/shared │
84
- │ │
85
- │ Domain model · contracts · Result · errors │
86
- │ aggregates · value objects · domain events │
87
- │ application interfaces │
88
- └─────────────────────────────────────────────┘
89
- ```
90
-
91
- This separation lets the domain and application contracts remain independent
92
- from the transport hosting them.
93
-
94
- ---
95
-
96
- ## Why Shared?
97
-
98
- Most application frameworks start from the transport:
99
-
100
- ```text
101
- HTTP request
102
- ↓
103
- controller
104
- ↓
105
- service
106
- ↓
107
- database
108
- ```
109
-
110
- Xeno.JS starts from the application model instead:
111
-
112
- ```text
113
- Domain
114
- ↓
115
- Application contracts
116
- ↓
117
- Execution model
118
- ↓
119
- Transport
120
- ```
121
-
122
- That distinction matters when an application grows.
123
-
124
- The HTTP layer should not define your domain model.
125
-
126
- Your database should not define your application contracts.
127
-
128
- And your infrastructure should not become the place where business rules live.
129
-
130
- `@xeno-js/shared` provides the primitives and contracts that make those
131
- boundaries explicit.
132
-
133
- ---
134
-
135
- # Domain primitives
136
-
137
- ## Aggregate roots
138
-
139
- `AggregateRoot` provides a base abstraction for aggregates that need:
140
-
141
- - an explicit identity
142
- - aggregate versioning
143
- - domain event application
144
- - loading from event history
145
- - tracking of uncommitted domain events.
146
-
147
- ```ts
148
- import { AggregateRoot } from '@xeno-js/shared'
149
-
150
- class UserId {
151
- // ...
152
- }
153
-
154
- type UserEvent =
155
- | {
156
- type: 'UserCreated'
157
- name: string
158
- }
159
- | {
160
- type: 'UserRenamed'
161
- name: string
162
- }
163
-
164
- class User extends AggregateRoot<UserEvent> {
165
- private name = ''
166
-
167
- public rename(name: string): void {
168
- this.raise({
169
- eventType: 'UserRenamed',
170
- payload: {
171
- type: 'UserRenamed',
172
- name,
173
- },
174
- })
175
- }
176
-
177
- protected apply(event: IDomainEvent<UserEvent>, isNew: boolean): void {
178
- switch (event.eventType) {
179
- case 'UserRenamed':
180
- this.name = event.payload.name
181
- break
182
- }
183
- }
184
- }
185
- ```
186
-
187
- An aggregate keeps its domain changes explicit:
188
-
189
- ```text
190
- Aggregate
191
- │
192
- ├── identity
193
- ├── version
194
- ├── state
195
- │
196
- └── uncommitted events
197
- │
198
- ▼
199
- IDomainEvent
200
- ```
201
-
202
- `AggregateRoot` does not provide an event store. It provides the aggregate-side
203
- primitives required to model and track domain events.
204
-
205
- ---
206
-
207
- ## Domain events
208
-
209
- `IDomainEvent` defines a framework-neutral representation of an event produced
210
- by an aggregate.
211
-
212
- ```ts
213
- export interface IDomainEvent<
214
- TPayload = unknown,
215
- TValueObject extends object = object,
216
- > {
217
- readonly aggregateId: TValueObject
218
- readonly eventType: string
219
- readonly version: number
220
- readonly occurredAt: Date
221
- readonly payload: TPayload
222
- }
223
- ```
224
-
225
- A domain event carries:
226
-
227
- - the aggregate identity
228
- - an explicit event type
229
- - the aggregate version
230
- - the occurrence timestamp
231
- - the event payload.
232
-
233
- This makes domain changes representable without coupling the domain model to an
234
- HTTP server, database driver, or message broker.
235
-
236
- ---
237
-
238
- ## Value objects
239
-
240
- Value objects provide domain concepts whose meaning comes from their value
241
- rather than object identity.
242
-
243
- ```ts
244
- import { ValueObject } from '@xeno-js/shared'
245
-
246
- interface EmailProps {
247
- value: string
248
- }
249
-
250
- class Email extends ValueObject<EmailProps> {
251
- public static create(value: string): Email {
252
- return new Email({ value })
253
- }
254
- }
255
- ```
256
-
257
- The base implementation provides:
258
-
259
- - immutable properties
260
- - value retrieval
261
- - equality comparison
262
- - string representation.
263
-
264
- ```ts
265
- const first = Email.create('user@example.com')
266
- const second = Email.create('user@example.com')
267
-
268
- first.equals(second) // true
269
- ```
270
-
271
- ---
272
-
273
- # Application contracts
274
-
275
- The package also defines contracts used to keep application code independent
276
- from concrete infrastructure.
277
-
278
- These include abstractions for areas such as:
279
-
280
- - CQRS
281
- - repositories
282
- - data sources
283
- - services
284
- - factories
285
- - policies
286
- - transactions
287
- - request context
288
- - middleware
289
- - logging
290
- - caching
291
- - storage
292
- - HTTP
293
- - mapping
294
- - idempotency.
295
-
296
- The important distinction is between the **contract** and its implementation.
297
-
298
- For example:
299
-
300
- ```text
301
- Application
302
- │
303
- │ depends on
304
- ▼
305
- Repository contract
306
- │
307
- │ implemented by
308
- ▼
309
- Infrastructure adapter
310
- ```
311
-
312
- The application therefore does not need to know whether data is stored in
313
- PostgreSQL, Supabase, Redis, or another persistence mechanism.
314
-
315
- ---
316
-
317
- # CQRS contracts
318
-
319
- `@xeno-js/shared` includes the contracts used to model commands and queries.
320
-
321
- ```text
322
- Command
323
- │
324
- ▼
325
- Application handler
326
- │
327
- ▼
328
- Domain
329
- ```
330
-
331
- and:
332
-
333
- ```text
334
- Query
335
- │
336
- ▼
337
- Application handler
338
- │
339
- ▼
340
- Read model / data source
341
- ```
342
-
343
- The package defines the contracts.
344
-
345
- `@xeno-js/core` provides the execution infrastructure around them.
346
-
347
- This distinction keeps CQRS from becoming tied to a particular transport.
348
-
349
- ---
350
-
351
- # Result and errors
352
-
353
- Application code often needs to represent an expected failure without turning
354
- every business outcome into an exception.
355
-
356
- Xeno.JS provides `Result` primitives alongside application/domain errors.
357
-
358
- Conceptually:
359
-
360
- ```text
361
- Operation
362
- │
363
- ├── success → Result success
364
- │
365
- └── expected failure → Result failure
366
- ```
367
-
368
- This allows application boundaries to make outcomes explicit while keeping error
369
- handling independent from the HTTP layer.
370
-
371
- ---
372
-
373
- # Runtime utilities
374
-
375
- The package also contains shared runtime utilities used across the Xeno
376
- ecosystem.
377
-
378
- These include utilities for areas such as:
379
-
380
- - runtime guards
381
- - strings
382
- - dates
383
- - enumerables
384
- - GUIDs
385
- - promises
386
- - HTTP helpers
387
- - sanitization
388
- - abort handling
389
- - mathematical helpers.
390
-
391
- These utilities are deliberately secondary to the architectural role of the
392
- package.
393
-
394
- The purpose of `@xeno-js/shared` is not to be a generic utility collection.
395
-
396
- Its primary role is to provide **shared domain and application building
397
- blocks**.
398
-
399
- ---
400
-
401
- # Infrastructure adapters
402
-
403
- `@xeno-js/shared` also exports a limited set of reusable infrastructure
404
- components, including integrations and adapters for areas such as:
405
-
406
- - HTTP clients
407
- - Supabase authentication
408
- - caching
409
- - storage
410
- - validation
411
- - mapping
412
- - factories.
413
-
414
- These are exported as reusable building blocks; they do not define the
415
- architecture of the application.
416
-
417
- For applications using `@xeno-js/core`, infrastructure can be composed through
418
- the application architecture rather than becoming part of the domain model.
419
-
420
- ---
421
-
422
- # Framework independent by design
423
-
424
- `@xeno-js/shared` does not define an HTTP application lifecycle.
425
-
426
- You can model your domain and application contracts without choosing a
427
- particular HTTP framework.
428
-
429
- For example:
430
-
431
- ```text
432
- ┌── Fastify
433
- │
434
- ├── Express
435
- Application ─────┼── Hono
436
- │
437
- ├── CLI
438
- │
439
- └── Worker
440
- ```
441
-
442
- The transport is the host.
443
-
444
- The domain and application contracts remain the application model.
445
-
446
- ---
447
-
448
- # Installation
449
-
450
- ```bash
451
- npm install @xeno-js/shared
452
- ```
453
-
454
- For the complete Xeno.JS application architecture:
455
-
456
- ```bash
457
- npm install @xeno-js/core
458
- ```
459
-
460
- You can use `@xeno-js/shared` independently when you only need the domain and
461
- application building blocks.
462
-
463
- ---
464
-
465
- # `@xeno-js/shared` vs `@xeno-js/core`
466
-
467
- The two packages have different responsibilities.
468
-
469
- | Package | Responsibility |
470
- | ------------------- | --------------------------------------------------- |
471
- | `@xeno-js/shared` | Domain primitives and application contracts |
472
- | `@xeno-js/core` | Application runtime and architecture |
473
- | Your transport | HTTP, CLI, worker, gRPC, etc. |
474
- | Your infrastructure | Database, cache, external services, messaging, etc. |
475
-
476
- A useful mental model is:
477
-
478
- ```text
479
- @xeno-js/shared
480
- defines the language
481
-
482
- ↓
483
-
484
- @xeno-js/core
485
- executes the architecture
486
-
487
- ↓
488
-
489
- your application
490
- defines the business behavior
491
-
492
- ↓
493
-
494
- your transport / infrastructure
495
- hosts and connects the system
496
- ```
497
-
498
- ---
499
-
500
- # What `@xeno-js/shared` is not
501
-
502
- `@xeno-js/shared` is not:
503
-
504
- - an HTTP framework
505
- - an application server
506
- - an ORM
507
- - an event bus
508
- - an event store
509
- - a complete event-sourcing framework
510
- - a replacement for your transport framework.
511
-
512
- It provides the primitives and contracts that let those concerns remain
513
- separated from the domain model.
514
-
515
- ---
516
-
517
- # Design principles
518
-
519
- The package follows a few simple principles.
520
-
521
- ### Explicit contracts
522
-
523
- Important application boundaries should be represented by explicit TypeScript
524
- contracts.
525
-
526
- ### Domain first
527
-
528
- Business concepts such as aggregates, value objects and domain events should not
529
- depend on transport details.
530
-
531
- ### Infrastructure at the boundary
532
-
533
- Concrete integrations belong outside the domain model.
534
-
535
- ### Framework independence
536
-
537
- Domain and application contracts should not require a specific HTTP framework.
538
-
539
- ### Composition over magic
540
-
541
- The architecture should be understandable from the code rather than depending on
542
- runtime discovery or hidden conventions.
543
-
544
- ---
545
-
546
- # Relationship with Xeno
547
-
548
- The Xeno.JS ecosystem can be understood as three layers:
549
-
550
- ```text
551
- Your application
552
- │
553
- ▼
554
- ┌───────────────────┐
555
- │ @xeno-js/core │
556
- │ │
557
- │ Runtime │
558
- │ DI │
559
- │ Scopes │
560
- │ CQRS execution │
561
- │ Pipelines │
562
- │ Request context │
563
- └─────────┬─────────┘
564
- │
565
- ▼
566
- ┌───────────────────┐
567
- │ @xeno-js/shared │
568
- │ │
569
- │ Domain │
570
- │ Contracts │
571
- │ Result / Errors │
572
- │ Aggregates │
573
- │ Value Objects │
574
- │ Domain Events │
575
- └───────────────────┘
576
- ```
577
-
578
- The transport sits around the application rather than defining it.
579
-
580
- > **Shared defines the contracts. Core executes the architecture. Your transport
581
- > hosts the application.**
582
-
583
- ---
584
-
585
- ## Documentation
586
-
587
- The documentation hub contains the architecture and integration guides:
588
-
589
- **[xeno-js.it](https://www.xeno-js.it/introduction)**
590
-
591
- Recommended starting points:
592
-
593
- - [Introduction](https://www.xeno-js.it/introduction)
594
- - [Overview](https://www.xeno-js.it/docs/shared/overview)
595
- - [Guards Utils](https://www.xeno-js.it/docs/shared/utils/guards)
596
- - [Enumerabe Utils](https://www.xeno-js.it/docs/shared/utils/enumerable)
597
- - [CLI](https://www.xeno-js.it/docs/cli/overview)
598
-
599
- ---
600
-
601
- # Development
602
-
603
- Clone the repository:
604
-
605
- ```bash
606
- git clone https://github.com/xeno-js/xeno-shared.git
607
- cd xeno-shared
608
- npm install
609
- ```
610
-
611
- Run the main checks:
612
-
613
- ```bash
614
- npm run check
615
- ```
616
-
617
- Available scripts:
618
-
619
- | Command | Description |
620
- | ----------------------- | ---------------------------- |
621
- | `npm run build` | Build the package |
622
- | `npm run typecheck` | Run TypeScript type checking |
623
- | `npm run lint` | Run ESLint |
624
- | `npm run format` | Format the repository |
625
- | `npm run format:check` | Check formatting |
626
- | `npm run test` | Run tests |
627
- | `npm run test:watch` | Run tests in watch mode |
628
- | `npm run test:coverage` | Run tests with coverage |
629
- | `npm run check` | Typecheck, lint and test |
630
- | `npm run changelog` | Generate the changelog |
631
-
632
- ---
633
-
634
- # Contributing
635
-
636
- Contributions are welcome.
637
-
638
- Create a feature or fix branch from `develop`:
639
-
640
- ```bash
641
- git checkout develop
642
- git pull origin develop
643
- git checkout -b feat/your-feature
644
- ```
645
-
646
- Before opening a pull request:
647
-
648
- ```bash
649
- npm run check
650
- ```
651
-
652
- Use Conventional Commits:
653
-
654
- ```text
655
- feat(domain): add aggregate primitive
656
- fix(result): correct failure handling
657
- refactor(events): simplify event contract
658
- docs(readme): improve architecture documentation
659
- ```
660
-
661
- Pull requests should target `develop`.
662
-
663
- ---
664
-
665
- ## Support
666
-
667
- If Xeno.JS is useful to you, you can support the project through the community
668
- and sponsorship channels documented on the website:
669
-
670
- **[Support Xeno](https://www.xeno-js.it/docs/support-us)**
671
-
672
- ---
673
-
674
- ## License
675
-
676
- Copyright (c) 2026 Xeno.
677
-
678
- Licensed under the [MIT License](LICENSE).
1
+ <div align="center">
2
+ <img src="logo/logo.png" alt="Xeno.JS Shared Logo" width="140" />
3
+
4
+ <h1>@xeno-js/shared</h1>
5
+
6
+ <p><strong>Domain primitives and application contracts for TypeScript.</strong></p>
7
+
8
+ <p>
9
+ Define your domain model, application contracts, and architectural boundaries
10
+ without coupling them to a transport or framework.
11
+ </p>
12
+
13
+ <p>
14
+ <a href="https://github.com/xeno-js/xeno-shared">
15
+ <img src="https://img.shields.io/github/stars/xeno-js/xeno-shared?style=flat-square" alt="GitHub Stars" />
16
+ </a>
17
+ <a href="https://www.npmjs.com/package/@xeno-js/shared">
18
+ <img src="https://img.shields.io/npm/v/@xeno-js/shared?style=flat-square" alt="npm version" />
19
+ </a>
20
+ <a href="https://github.com/xeno-js/xeno-shared/blob/develop/LICENSE">
21
+ <img src="https://img.shields.io/npm/l/@xeno-js/shared?style=flat-square" alt="License: MIT" />
22
+ </a>
23
+ <a href="https://buymeacoffee.com/xenojs">
24
+ <img src="https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-FFdd00?style=flat-square&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" />
25
+ </a>
26
+ </p>
27
+ </div>
28
+
29
+ ---
30
+
31
+ ## What is `@xeno-js/shared`?
32
+
33
+ `@xeno-js/shared` provides the **domain primitives and framework-neutral
34
+ contracts** used across the Xeno.JS ecosystem.
35
+
36
+ It gives TypeScript applications explicit building blocks for:
37
+
38
+ - Domain-Driven Design
39
+ - aggregates and value objects
40
+ - domain events
41
+ - entities and domain errors
42
+ - Result-based application flows
43
+ - CQRS contracts
44
+ - repositories and data sources
45
+ - application services and policies
46
+ - request and execution context
47
+ - transactions and infrastructure boundaries
48
+
49
+ The goal is simple:
50
+
51
+ > **Keep the meaning of your application explicit in code.**
52
+
53
+ `@xeno-js/shared` defines the contracts.
54
+
55
+ `@xeno-js/core` provides the runtime architecture that executes them.
56
+
57
+ Your HTTP framework, CLI, worker, or other transport remains outside that
58
+ boundary.
59
+
60
+ ---
61
+
62
+ ## The Xeno.JS architecture
63
+
64
+ Xeno.JS separates **what an application means** from **how the application
65
+ runs**.
66
+
67
+ ```text
68
+ ┌─────────────────────────────────────────────┐
69
+ │ Transport / Host │
70
+ │ HTTP · CLI · Worker · gRPC · Scheduler │
71
+ └──────────────────────┬──────────────────────┘
72
+ │
73
+ ▼
74
+ ┌─────────────────────────────────────────────┐
75
+ │ @xeno-js/core │
76
+ │ │
77
+ │ DI · scopes · request context · pipelines │
78
+ │ CQRS execution · modules · infrastructure │
79
+ └──────────────────────┬──────────────────────┘
80
+ │
81
+ ▼
82
+ ┌─────────────────────────────────────────────┐
83
+ │ @xeno-js/shared │
84
+ │ │
85
+ │ Domain model · contracts · Result · errors │
86
+ │ aggregates · value objects · domain events │
87
+ │ application interfaces │
88
+ └─────────────────────────────────────────────┘
89
+ ```
90
+
91
+ This separation lets the domain and application contracts remain independent
92
+ from the transport hosting them.
93
+
94
+ ---
95
+
96
+ ## Why Shared?
97
+
98
+ Most application frameworks start from the transport:
99
+
100
+ ```text
101
+ HTTP request
102
+ ↓
103
+ controller
104
+ ↓
105
+ service
106
+ ↓
107
+ database
108
+ ```
109
+
110
+ Xeno.JS starts from the application model instead:
111
+
112
+ ```text
113
+ Domain
114
+ ↓
115
+ Application contracts
116
+ ↓
117
+ Execution model
118
+ ↓
119
+ Transport
120
+ ```
121
+
122
+ That distinction matters when an application grows.
123
+
124
+ The HTTP layer should not define your domain model.
125
+
126
+ Your database should not define your application contracts.
127
+
128
+ And your infrastructure should not become the place where business rules live.
129
+
130
+ `@xeno-js/shared` provides the primitives and contracts that make those
131
+ boundaries explicit.
132
+
133
+ ---
134
+
135
+ # Domain primitives
136
+
137
+ ## Aggregate roots
138
+
139
+ `AggregateRoot` provides a base abstraction for aggregates that need:
140
+
141
+ - an explicit identity
142
+ - aggregate versioning
143
+ - domain event application
144
+ - loading from event history
145
+ - tracking of uncommitted domain events.
146
+
147
+ ```ts
148
+ import { AggregateRoot } from '@xeno-js/shared'
149
+
150
+ class UserId {
151
+ // ...
152
+ }
153
+
154
+ type UserEvent =
155
+ | {
156
+ type: 'UserCreated'
157
+ name: string
158
+ }
159
+ | {
160
+ type: 'UserRenamed'
161
+ name: string
162
+ }
163
+
164
+ class User extends AggregateRoot<UserEvent> {
165
+ private name = ''
166
+
167
+ public rename(name: string): void {
168
+ this.raise({
169
+ eventType: 'UserRenamed',
170
+ payload: {
171
+ type: 'UserRenamed',
172
+ name,
173
+ },
174
+ })
175
+ }
176
+
177
+ protected apply(event: IDomainEvent<UserEvent>, isNew: boolean): void {
178
+ switch (event.eventType) {
179
+ case 'UserRenamed':
180
+ this.name = event.payload.name
181
+ break
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ An aggregate keeps its domain changes explicit:
188
+
189
+ ```text
190
+ Aggregate
191
+ │
192
+ ├── identity
193
+ ├── version
194
+ ├── state
195
+ │
196
+ └── uncommitted events
197
+ │
198
+ ▼
199
+ IDomainEvent
200
+ ```
201
+
202
+ `AggregateRoot` does not provide an event store. It provides the aggregate-side
203
+ primitives required to model and track domain events.
204
+
205
+ ---
206
+
207
+ ## Domain events
208
+
209
+ `IDomainEvent` defines a framework-neutral representation of an event produced
210
+ by an aggregate.
211
+
212
+ ```ts
213
+ export interface IDomainEvent<
214
+ TPayload = unknown,
215
+ TValueObject extends object = object,
216
+ > {
217
+ readonly aggregateId: TValueObject
218
+ readonly eventType: string
219
+ readonly version: number
220
+ readonly occurredAt: Date
221
+ readonly payload: TPayload
222
+ }
223
+ ```
224
+
225
+ A domain event carries:
226
+
227
+ - the aggregate identity
228
+ - an explicit event type
229
+ - the aggregate version
230
+ - the occurrence timestamp
231
+ - the event payload.
232
+
233
+ This makes domain changes representable without coupling the domain model to an
234
+ HTTP server, database driver, or message broker.
235
+
236
+ ---
237
+
238
+ ## Value objects
239
+
240
+ Value objects provide domain concepts whose meaning comes from their value
241
+ rather than object identity.
242
+
243
+ ```ts
244
+ import { ValueObject } from '@xeno-js/shared'
245
+
246
+ interface EmailProps {
247
+ value: string
248
+ }
249
+
250
+ class Email extends ValueObject<EmailProps> {
251
+ public static create(value: string): Email {
252
+ return new Email({ value })
253
+ }
254
+ }
255
+ ```
256
+
257
+ The base implementation provides:
258
+
259
+ - immutable properties
260
+ - value retrieval
261
+ - equality comparison
262
+ - string representation.
263
+
264
+ ```ts
265
+ const first = Email.create('user@example.com')
266
+ const second = Email.create('user@example.com')
267
+
268
+ first.equals(second) // true
269
+ ```
270
+
271
+ ---
272
+
273
+ # Application contracts
274
+
275
+ The package also defines contracts used to keep application code independent
276
+ from concrete infrastructure.
277
+
278
+ These include abstractions for areas such as:
279
+
280
+ - CQRS
281
+ - repositories
282
+ - data sources
283
+ - services
284
+ - factories
285
+ - policies
286
+ - transactions
287
+ - request context
288
+ - middleware
289
+ - logging
290
+ - caching
291
+ - storage
292
+ - HTTP
293
+ - mapping
294
+ - idempotency.
295
+
296
+ The important distinction is between the **contract** and its implementation.
297
+
298
+ For example:
299
+
300
+ ```text
301
+ Application
302
+ │
303
+ │ depends on
304
+ ▼
305
+ Repository contract
306
+ │
307
+ │ implemented by
308
+ ▼
309
+ Infrastructure adapter
310
+ ```
311
+
312
+ The application therefore does not need to know whether data is stored in
313
+ PostgreSQL, Supabase, Redis, or another persistence mechanism.
314
+
315
+ ---
316
+
317
+ # CQRS contracts
318
+
319
+ `@xeno-js/shared` includes the contracts used to model commands and queries.
320
+
321
+ ```text
322
+ Command
323
+ │
324
+ ▼
325
+ Application handler
326
+ │
327
+ ▼
328
+ Domain
329
+ ```
330
+
331
+ and:
332
+
333
+ ```text
334
+ Query
335
+ │
336
+ ▼
337
+ Application handler
338
+ │
339
+ ▼
340
+ Read model / data source
341
+ ```
342
+
343
+ The package defines the contracts.
344
+
345
+ `@xeno-js/core` provides the execution infrastructure around them.
346
+
347
+ This distinction keeps CQRS from becoming tied to a particular transport.
348
+
349
+ ---
350
+
351
+ # Result and errors
352
+
353
+ Application code often needs to represent an expected failure without turning
354
+ every business outcome into an exception.
355
+
356
+ Xeno.JS provides `Result` primitives alongside application/domain errors.
357
+
358
+ Conceptually:
359
+
360
+ ```text
361
+ Operation
362
+ │
363
+ ├── success → Result success
364
+ │
365
+ └── expected failure → Result failure
366
+ ```
367
+
368
+ This allows application boundaries to make outcomes explicit while keeping error
369
+ handling independent from the HTTP layer.
370
+
371
+ ---
372
+
373
+ # Runtime utilities
374
+
375
+ The package also contains shared runtime utilities used across the Xeno
376
+ ecosystem.
377
+
378
+ These include utilities for areas such as:
379
+
380
+ - runtime guards
381
+ - strings
382
+ - dates
383
+ - enumerables
384
+ - GUIDs
385
+ - promises
386
+ - HTTP helpers
387
+ - sanitization
388
+ - abort handling
389
+ - mathematical helpers.
390
+
391
+ These utilities are deliberately secondary to the architectural role of the
392
+ package.
393
+
394
+ The purpose of `@xeno-js/shared` is not to be a generic utility collection.
395
+
396
+ Its primary role is to provide **shared domain and application building
397
+ blocks**.
398
+
399
+ ---
400
+
401
+ # Infrastructure adapters
402
+
403
+ `@xeno-js/shared` also exports a limited set of reusable infrastructure
404
+ components, including integrations and adapters for areas such as:
405
+
406
+ - HTTP clients
407
+ - Supabase authentication
408
+ - caching
409
+ - storage
410
+ - validation
411
+ - mapping
412
+ - factories.
413
+
414
+ These are exported as reusable building blocks; they do not define the
415
+ architecture of the application.
416
+
417
+ For applications using `@xeno-js/core`, infrastructure can be composed through
418
+ the application architecture rather than becoming part of the domain model.
419
+
420
+ ---
421
+
422
+ # Framework independent by design
423
+
424
+ `@xeno-js/shared` does not define an HTTP application lifecycle.
425
+
426
+ You can model your domain and application contracts without choosing a
427
+ particular HTTP framework.
428
+
429
+ For example:
430
+
431
+ ```text
432
+ ┌── Fastify
433
+ │
434
+ ├── Express
435
+ Application ─────┼── Hono
436
+ │
437
+ ├── CLI
438
+ │
439
+ └── Worker
440
+ ```
441
+
442
+ The transport is the host.
443
+
444
+ The domain and application contracts remain the application model.
445
+
446
+ ---
447
+
448
+ # Installation
449
+
450
+ ```bash
451
+ npm install @xeno-js/shared
452
+ ```
453
+
454
+ For the complete Xeno.JS application architecture:
455
+
456
+ ```bash
457
+ npm install @xeno-js/core
458
+ ```
459
+
460
+ You can use `@xeno-js/shared` independently when you only need the domain and
461
+ application building blocks.
462
+
463
+ ---
464
+
465
+ # `@xeno-js/shared` vs `@xeno-js/core`
466
+
467
+ The two packages have different responsibilities.
468
+
469
+ | Package | Responsibility |
470
+ | ------------------- | --------------------------------------------------- |
471
+ | `@xeno-js/shared` | Domain primitives and application contracts |
472
+ | `@xeno-js/core` | Application runtime and architecture |
473
+ | Your transport | HTTP, CLI, worker, gRPC, etc. |
474
+ | Your infrastructure | Database, cache, external services, messaging, etc. |
475
+
476
+ A useful mental model is:
477
+
478
+ ```text
479
+ @xeno-js/shared
480
+ defines the language
481
+
482
+ ↓
483
+
484
+ @xeno-js/core
485
+ executes the architecture
486
+
487
+ ↓
488
+
489
+ your application
490
+ defines the business behavior
491
+
492
+ ↓
493
+
494
+ your transport / infrastructure
495
+ hosts and connects the system
496
+ ```
497
+
498
+ ---
499
+
500
+ # What `@xeno-js/shared` is not
501
+
502
+ `@xeno-js/shared` is not:
503
+
504
+ - an HTTP framework
505
+ - an application server
506
+ - an ORM
507
+ - an event bus
508
+ - an event store
509
+ - a complete event-sourcing framework
510
+ - a replacement for your transport framework.
511
+
512
+ It provides the primitives and contracts that let those concerns remain
513
+ separated from the domain model.
514
+
515
+ ---
516
+
517
+ # Design principles
518
+
519
+ The package follows a few simple principles.
520
+
521
+ ## Explicit contracts
522
+
523
+ Important application boundaries should be represented by explicit TypeScript
524
+ contracts.
525
+
526
+ ### Domain first
527
+
528
+ Business concepts such as aggregates, value objects and domain events should not
529
+ depend on transport details.
530
+
531
+ ### Infrastructure at the boundary
532
+
533
+ Concrete integrations belong outside the domain model.
534
+
535
+ ### Framework independence
536
+
537
+ Domain and application contracts should not require a specific HTTP framework.
538
+
539
+ ### Composition over magic
540
+
541
+ The architecture should be understandable from the code rather than depending on
542
+ runtime discovery or hidden conventions.
543
+
544
+ ---
545
+
546
+ # Relationship with Xeno
547
+
548
+ The Xeno.JS ecosystem can be understood as three layers:
549
+
550
+ ```text
551
+ Your application
552
+ │
553
+ ▼
554
+ ┌───────────────────┐
555
+ │ @xeno-js/core │
556
+ │ │
557
+ │ Runtime │
558
+ │ DI │
559
+ │ Scopes │
560
+ │ CQRS execution │
561
+ │ Pipelines │
562
+ │ Request context │
563
+ └─────────┬─────────┘
564
+ │
565
+ ▼
566
+ ┌───────────────────┐
567
+ │ @xeno-js/shared │
568
+ │ │
569
+ │ Domain │
570
+ │ Contracts │
571
+ │ Result / Errors │
572
+ │ Aggregates │
573
+ │ Value Objects │
574
+ │ Domain Events │
575
+ └───────────────────┘
576
+ ```
577
+
578
+ The transport sits around the application rather than defining it.
579
+
580
+ > **Shared defines the contracts. Core executes the architecture. Your transport
581
+ > hosts the application.**
582
+
583
+ ---
584
+
585
+ ## Documentation
586
+
587
+ The documentation hub contains the architecture and integration guides:
588
+
589
+ **[xeno-js.it](https://www.xeno-js.it/docs/introduction)**
590
+
591
+ Recommended starting points:
592
+
593
+ - [Introduction](https://www.xeno-js.it/docs/introduction)
594
+ - [CLI](https://www.xeno-js.it/docs/cli/overview)
595
+
596
+ ---
597
+
598
+ # Development
599
+
600
+ Clone the repository:
601
+
602
+ ```bash
603
+ git clone https://github.com/xeno-js/xeno-shared.git
604
+ cd xeno-shared
605
+ npm install
606
+ ```
607
+
608
+ Run the main checks:
609
+
610
+ ```bash
611
+ npm run check
612
+ ```
613
+
614
+ Available scripts:
615
+
616
+ | Command | Description |
617
+ | ----------------------- | ---------------------------- |
618
+ | `npm run build` | Build the package |
619
+ | `npm run typecheck` | Run TypeScript type checking |
620
+ | `npm run lint` | Run ESLint |
621
+ | `npm run format` | Format the repository |
622
+ | `npm run format:check` | Check formatting |
623
+ | `npm run test` | Run tests |
624
+ | `npm run test:watch` | Run tests in watch mode |
625
+ | `npm run test:coverage` | Run tests with coverage |
626
+ | `npm run check` | Typecheck, lint and test |
627
+ | `npm run changelog` | Generate the changelog |
628
+
629
+ ---
630
+
631
+ # Contributing
632
+
633
+ Contributions are welcome.
634
+
635
+ Create a feature or fix branch from `develop`:
636
+
637
+ ```bash
638
+ git checkout develop
639
+ git pull origin develop
640
+ git checkout -b feat/your-feature
641
+ ```
642
+
643
+ Before opening a pull request:
644
+
645
+ ```bash
646
+ npm run check
647
+ ```
648
+
649
+ Use Conventional Commits:
650
+
651
+ ```text
652
+ feat(domain): add aggregate primitive
653
+ fix(result): correct failure handling
654
+ refactor(events): simplify event contract
655
+ docs(readme): improve architecture documentation
656
+ ```
657
+
658
+ Pull requests should target `develop`.
659
+
660
+ ---
661
+
662
+ ## Support
663
+
664
+ If Xeno.JS is useful to you, you can support the project through the community
665
+ and sponsorship channels documented on the website:
666
+
667
+ **[Support Xeno](https://www.xeno-js.it/support-us)**
668
+
669
+ ---
670
+
671
+ ## License
672
+
673
+ Copyright (c) 2026 Xeno.
674
+
675
+ Licensed under the [MIT License](LICENSE).