@exium/core 1.0.0-rc.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/LICENSE.md +21 -0
- package/README.md +140 -0
- package/dist/index.d.mts +1801 -0
- package/dist/index.d.ts +1801 -0
- package/dist/index.js +7247 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +7035 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +119 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Exium Framework
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Exium
|
|
2
|
+
|
|
3
|
+
Opinionated NestJS-based framework for building DDD, hexagonal, modular-monolith-first microservices.
|
|
4
|
+
|
|
5
|
+
Exium is built primarily for Exium-based services. It is public so applications can install it from the npm registry, but it remains an opinionated platform framework rather than a generic NestJS replacement.
|
|
6
|
+
|
|
7
|
+
Published package:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @exium/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
CLI package:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @exium/cli
|
|
17
|
+
exium new order-management
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Exium is designed as an internal platform framework. It intentionally ships with the infrastructure choices this codebase standardizes on:
|
|
21
|
+
|
|
22
|
+
- NestJS runtime
|
|
23
|
+
- Express HTTP adapter
|
|
24
|
+
- MikroORM persistence
|
|
25
|
+
- PostgreSQL and MongoDB persistence drivers
|
|
26
|
+
- Kysely read-side query engine
|
|
27
|
+
- RabbitMQ integration messaging
|
|
28
|
+
- Redis cache and idempotency adapters
|
|
29
|
+
|
|
30
|
+
## Layers
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
src/foundation Result and failure primitives
|
|
34
|
+
src/domain DDD building blocks
|
|
35
|
+
src/application CQRS, middleware, ports, read contracts
|
|
36
|
+
src/infrastructure Persistence, messaging, Redis, cache, idempotency adapters
|
|
37
|
+
src/http External/internal HTTP contracts, responses, filters, interceptors
|
|
38
|
+
src/runtime Outermost Nest runtime and Exium bootstrap
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## HTTP Model
|
|
42
|
+
|
|
43
|
+
HTTP endpoints can be marked as external or internal contracts.
|
|
44
|
+
|
|
45
|
+
External APIs are the public contract exposed through a gateway:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
@Controller('orders')
|
|
49
|
+
@ExternalApi({ name: 'orders', owner: 'order-management' })
|
|
50
|
+
export class OrderController {}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Internal APIs are module/service-to-module/service contracts. Internal calls can resolve locally in a modular monolith or remotely over HTTP after extraction:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
@Controller('orders/internal')
|
|
57
|
+
@InternalApi({ name: 'orders-internal', mode: 'auto' })
|
|
58
|
+
export class InternalOrderController {}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Application Model
|
|
62
|
+
|
|
63
|
+
Commands and queries return `Result`.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
@Transactional()
|
|
67
|
+
export class CreateOrderCommand extends Command<string> {}
|
|
68
|
+
|
|
69
|
+
@HandleCommand(CreateOrderCommand)
|
|
70
|
+
export class CreateOrderCommandHandler extends CommandHandler<CreateOrderCommand> {
|
|
71
|
+
async handle(command: CreateOrderCommand): Promise<ResultType<string>> {
|
|
72
|
+
return Result.ok('order-id');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Transactional commands commit only on `Result.ok(...)`. `Result.fail(...)` rolls back.
|
|
78
|
+
|
|
79
|
+
## Exception vs Result
|
|
80
|
+
|
|
81
|
+
Exium follows a strict policy:
|
|
82
|
+
|
|
83
|
+
- **Thrown exceptions = unexpected failures.** Programmer errors, infrastructure
|
|
84
|
+
faults, broken invariants. The exception filter logs them and surfaces a
|
|
85
|
+
generic HTTP 500 — internal details never leak to clients.
|
|
86
|
+
- **`Result.fail(Failure...)` = expected business failures.** Validation,
|
|
87
|
+
not-found, business rule violations, conflicts, authentication/authorization
|
|
88
|
+
denials. The response interceptor maps each `Failure.kind` to the appropriate
|
|
89
|
+
4xx status.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// Expected — return as Result
|
|
93
|
+
async handle(command: CreateOrderCommand): Promise<ResultType<string>> {
|
|
94
|
+
if (await this.repo.exists(command.id)) {
|
|
95
|
+
return Result.fail(
|
|
96
|
+
Failure.conflict('ORDER_ALREADY_EXISTS', 'Order already exists.', {
|
|
97
|
+
orderId: command.id,
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return Result.ok(command.id);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Unexpected — let it throw, filter turns it into 500
|
|
105
|
+
if (!this.connection.isOpen) {
|
|
106
|
+
throw new Error('Database connection is closed.');
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`HttpException` is the only exception type that keeps its original status —
|
|
111
|
+
preserved for compatibility with NestJS pipes (e.g. `ValidationPipe`).
|
|
112
|
+
|
|
113
|
+
## Release Gate
|
|
114
|
+
|
|
115
|
+
Start the test databases first:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm run test:db:up
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Run the v1 gate:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npm test
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The root test gate includes domain, config, infrastructure, application, core, HTTP, e2e, CLI workspace, and example build checks.
|
|
128
|
+
|
|
129
|
+
The CLI workspace smoke test also verifies that `exium new order-management --skip-install` creates a default `Order` aggregate project that typechecks. The CLI package is kept separate from `@exium/core`.
|
|
130
|
+
|
|
131
|
+
## ADRs
|
|
132
|
+
|
|
133
|
+
Architecture decisions live in [docs/adr](./docs/adr).
|
|
134
|
+
|
|
135
|
+
Start with:
|
|
136
|
+
|
|
137
|
+
- [ADR-0000: Expected Failures as Result](./docs/adr/adr-0000-expected-failures-as-result.md)
|
|
138
|
+
- [ADR-0009: Application Middleware Pipeline and Priority](./docs/adr/adr-0009-application-middleware-pipeline-and-priority.md)
|
|
139
|
+
- [ADR-0011: Unit of Work as Application Transaction Port](./docs/adr/adr-0011-unit-of-work-as-application-transaction-port.md)
|
|
140
|
+
- [ADR-0017: HTTP Layer API Exposure and Internal Calls](./docs/adr/adr-0017-http-layer-api-exposure-and-internal-calls.md)
|