Skip to content

Commit 4bca3e4

Browse files
committed
Refactor datastore package
1 parent d25a78d commit 4bca3e4

35 files changed

+6038
-180
lines changed

.syncpackrc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"versionGroups": [
3+
{
4+
"dependencies": ["@internal/**", "@nhsdigital/nhs-notify-event-schemas-supplier-api"],
5+
"specifierTypes": ["*"],
6+
"label": "Internal deps",
7+
"isIgnored": true
8+
}
9+
]
10+
}

internal/datastore/jest.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ export const baseJestConfig: Config = {
1515
// Indicates which provider should be used to instrument code for coverage
1616
coverageProvider: 'babel',
1717

18+
// Module name mapper to handle TypeScript path aliases
19+
moduleNameMapper: {
20+
'^@internal/helpers$': '<rootDir>/../helpers/src'
21+
},
22+
1823
coverageThreshold: {
1924
global: {
2025
branches: 100,

internal/datastore/package.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
"dependencies": {
33
"@aws-sdk/client-dynamodb": "^3.858.0",
44
"@aws-sdk/lib-dynamodb": "^3.858.0",
5+
"@internal/helpers": "*",
56
"pino": "^9.7.0",
6-
"zod": "^4.0.14"
7+
"zod": "^4.1.11"
78
},
89
"devDependencies": {
910
"@stylistic/eslint-plugin": "^3.1.0",
@@ -18,15 +19,13 @@
1819
"testcontainers": "^11.4.0",
1920
"ts-jest": "^29.4.0",
2021
"ts-node": "^10.9.2",
21-
"typescript": "^5.8.2",
22-
"zod-mermaid": "^1.0.9"
22+
"typescript": "^5.8.3"
2323
},
2424
"license": "MIT",
2525
"main": "src/index.ts",
26-
"name": "datastore",
26+
"name": "@internal/datastore",
2727
"private": true,
2828
"scripts": {
29-
"diagrams": "ts-node src/cli/diagrams.ts",
3029
"lint": "eslint .",
3130
"lint:fix": "eslint . --fix",
3231
"test:unit": "jest",

internal/datastore/src/cli/diagrams.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

internal/datastore/src/types.md

Lines changed: 0 additions & 46 deletions
This file was deleted.

internal/datastore/src/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { z } from 'zod';
2-
import { idRef } from 'zod-mermaid';
2+
import { idRef } from '@internal/helpers';
33

44
export const SupplerStatus = z.enum(['ENABLED', 'DISABLED']);
55

@@ -29,7 +29,7 @@ export const LetterSchemaBase = z.object({
2929
});
3030

3131
export const LetterSchema = LetterSchemaBase.extend({
32-
supplierId: z.string(),
32+
supplierId: idRef(SupplierSchema, 'id'),
3333
url: z.url(),
3434
createdAt: z.string(),
3535
updatedAt: z.string(),
@@ -48,7 +48,7 @@ export type LetterBase = z.infer<typeof LetterSchemaBase>;
4848

4949
export const MISchema = z.object({
5050
id: z.string(),
51-
supplierId: idRef(SupplierSchema),
51+
supplierId: idRef(SupplierSchema, 'id'),
5252
specificationId: z.string(),
5353
groupId: z.string(),
5454
lineItem: z.string(),

internal/datastore/tsconfig.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
{
2-
"compilerOptions": {
3-
"isolatedModules": true
4-
},
5-
"extends": "@tsconfig/node22/tsconfig.json",
2+
"compilerOptions": {},
3+
"extends": "../../tsconfig.base.json",
64
"include": [
75
"src/**/*",
86
"jest.config.ts"

internal/helpers/.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Dependency directories
2+
node_modules/
3+
4+
# Built output
5+
dist/
6+
7+
# Coverage directory used by tools like istanbul
8+
coverage/
9+
10+
# Logs
11+
logs
12+
*.log
13+
npm-debug.log*
14+
yarn-debug.log*
15+
yarn-error.log*
16+
17+
# Editor directories and files
18+
.idea/
19+
.vscode/
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

internal/helpers/.npmignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Source
2+
src/
3+
__tests__/
4+
*.test.ts
5+
6+
# Config files
7+
tsconfig.json
8+
jest.config.ts
9+
.eslintrc.js
10+
.gitignore
11+
12+
# Logs
13+
logs
14+
*.log
15+
npm-debug.log*
16+
yarn-debug.log*
17+
yarn-error.log*
18+
19+
# Coverage
20+
coverage/
21+
22+
# Development
23+
.vscode/
24+
.idea/

internal/helpers/README.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# NHS Notify Helpers (Internal)
2+
3+
Utility primitives shared across internal Supplier API packages. These helpers provide:
4+
5+
* Lightweight domain modelling conventions (branded IDs)
6+
* Type-safe relationship references
7+
* Common scalar validators (Version, Environment)
8+
9+
The goal is to keep domain packages small and consistent – not to become a general utility grab‑bag.
10+
11+
---
12+
13+
## Exports Overview
14+
15+
| Export | Kind | Purpose |
16+
|--------|------|---------|
17+
| `DomainBase(name)` | Function | Builds a base Zod object with a branded `domainId` for the named aggregate/entity |
18+
| `idRef(schema, idField?, entityName?)` | Function | Creates a reference field mirroring the ID field type of another schema |
19+
| `$Version` / `Version` | Zod schema / Type | Semantic version (major.minor.patch) branded as `Version` (from `version.ts`) |
20+
| `$Environment` | Zod schema | Environment identifier (e.g. `dev`, `int`, `prod`) (from `environment.ts`) |
21+
22+
---
23+
24+
## Installation (Internal Only)
25+
26+
Other workspaces in the mono‑repo reference the helpers via package name (preferred) or relative path. Example:
27+
28+
```jsonc
29+
// package.json
30+
"dependencies": {
31+
"@internal/helpers": "*"
32+
}
33+
```
34+
35+
No external publication is intended.
36+
37+
---
38+
39+
## Usage
40+
41+
### 1. Defining a Domain Model Base
42+
43+
```typescript
44+
import { DomainBase } from '@internal/helpers';
45+
import { z } from 'zod';
46+
47+
// Creates { domainId: BrandedString<'Letter'> }
48+
const $Letter = DomainBase('Letter').extend({
49+
createdAt: z.string().datetime(),
50+
});
51+
```
52+
53+
The branded ID prevents accidental mixing of IDs across entity types while remaining a plain string at runtime.
54+
55+
### 2. Referencing Another Entity
56+
57+
```typescript
58+
import { idRef, DomainBase } from '@internal/helpers';
59+
import { z } from 'zod';
60+
61+
const $Customer = DomainBase('Customer');
62+
const $Order = DomainBase('Order').extend({
63+
customerId: idRef($Customer), // Inherits type & metadata from Customer.domainId
64+
});
65+
```
66+
67+
You can supply a custom ID field name if the target schema uses something other than `domainId`.
68+
69+
### 3. Version & Environment Scalars
70+
71+
```typescript
72+
import { $Version, $Environment } from '@internal/helpers';
73+
74+
const cfg = {
75+
version: $Version.parse('1.2.3'),
76+
environment: $Environment.parse('dev'),
77+
};
78+
```
79+
80+
---
81+
82+
## API Details
83+
84+
### `DomainBase(name: string)`
85+
86+
Returns a Zod object: `{ domainId: BrandedString<name> }` with metadata describing the ID.
87+
88+
### `idRef(schema, idFieldName = 'domainId', entityName?)`
89+
90+
Clones the ID field schema from `schema.shape[idFieldName]`, re‑annotating metadata to describe the target entity reference. Throws if the field is absent.
91+
92+
### `$Version`
93+
94+
Defined in `src/version.ts`.
95+
96+
Regex: `^\d+\.\d+\.\d+$` – no pre-release/build metadata (keep simple for now). Consider extending when semver nuance is required.
97+
98+
### `$Environment`
99+
100+
Defined in `src/environment.ts`.
101+
102+
String with metadata; callers typically restrict further via union if they need a constrained set.
103+
104+
---
105+
106+
## Design Notes
107+
108+
* Zod is used for runtime validation + static type inference; no dual maintenance.
109+
* Branded string IDs provide nominal typing without runtime overhead.
110+
* Helpers avoid opinionated persistence or transport logic – they stay schema-focused.
111+
112+
---
113+
114+
## Scripts
115+
116+
| Script | Purpose |
117+
|--------|---------|
118+
| `npm run build` | Compile TypeScript to `dist/` |
119+
| `npm test` | Run unit tests (Jest) |
120+
| `npm run lint` | Lint sources |
121+
| `npm run lint:fix` | Auto-fix lint issues |
122+
| `npm run typecheck` | Type-only compile (no emit) |
123+
124+
---
125+
126+
## Conventions & Guidelines
127+
128+
1. Prefer composing small schemas over adding conditional logic inside helpers.
129+
2. Avoid adding business logic – keep to typing / structural utilities.
130+
3. If adding a new scalar helper, provide: regex (if applicable), examples, and rationale.
131+
4. Update this README when exports change.
132+
133+
---
134+
135+
## Extending
136+
137+
When introducing a new shared primitive:
138+
139+
1. Implement under `src/` with clear JSDoc.
140+
2. Export from `src/index.ts`.
141+
3. Add a focused test in `__tests__` (create folder if absent).
142+
4. Document in the Exports Overview table.
143+
144+
---
145+
146+
## License
147+
148+
MIT (internal usage only)
149+
150+
---
151+
152+
## Support
153+
154+
Use internal channel or repository discussions referencing the `helpers` package.

0 commit comments

Comments
 (0)