2025-12-26 14:53:03 +09:00
|
|
|
|
## Import Hygiene Guide (Domain)
|
|
|
|
|
|
|
|
|
|
|
|
### Principles
|
|
|
|
|
|
|
|
|
|
|
|
- **No deep imports**: internal file layout is not part of the contract.
|
|
|
|
|
|
- **Barrels define the public API**: if it’s not exported from the entrypoint, it’s not public.
|
|
|
|
|
|
- **Providers are integration-only**: Portal must never import provider adapters/types.
|
|
|
|
|
|
|
|
|
|
|
|
### Allowed import levels
|
|
|
|
|
|
|
|
|
|
|
|
- **Default (Portal + BFF)**:
|
|
|
|
|
|
- `@customer-portal/domain/<module>`
|
|
|
|
|
|
- `@customer-portal/domain/toolkit`
|
|
|
|
|
|
- **BFF-only (integration/infrastructure)**:
|
|
|
|
|
|
- `@customer-portal/domain/<module>/providers`
|
|
|
|
|
|
|
2025-12-26 17:27:22 +09:00
|
|
|
|
### Domain-internal helpers (for domain code only)
|
|
|
|
|
|
|
|
|
|
|
|
Sometimes provider helpers need to be shared across multiple domain mappers **without**
|
|
|
|
|
|
becoming part of the public contract (and without enabling deep imports).
|
|
|
|
|
|
|
|
|
|
|
|
In those cases, keep the helpers in a stable internal location (e.g.
|
|
|
|
|
|
`packages/domain/common/providers/whmcs-utils/`) and import them via **relative imports**
|
|
|
|
|
|
from within `packages/domain/**`.
|
|
|
|
|
|
|
|
|
|
|
|
- **Allowed**: inside `packages/domain/**` only
|
|
|
|
|
|
- **Never**: imported from `apps/**`
|
|
|
|
|
|
- **Purpose**: share provider-only helpers (parsing, encoding/serialization, provider quirks)
|
|
|
|
|
|
across multiple domain mappers while keeping the app-facing API surface clean.
|
|
|
|
|
|
|
2025-12-26 15:07:47 +09:00
|
|
|
|
## Quick Reference
|
|
|
|
|
|
|
|
|
|
|
|
| Context | Import Pattern | Example |
|
|
|
|
|
|
| ----------------------- | ---------------------------------- | ------------------------------------------------------------------- |
|
|
|
|
|
|
| Portal/BFF domain types | `@customer-portal/domain/<module>` | `import { Invoice } from "@customer-portal/domain/billing"` |
|
|
|
|
|
|
| BFF provider adapters | `.../<module>/providers` | `import { Whmcs } from "@customer-portal/domain/billing/providers"` |
|
|
|
|
|
|
| Toolkit utilities | `@customer-portal/domain/toolkit` | `import { Formatting } from "@customer-portal/domain/toolkit"` |
|
|
|
|
|
|
|
|
|
|
|
|
## Architecture Diagram
|
|
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
graph TD
|
|
|
|
|
|
subgraph Portal [Portal App]
|
|
|
|
|
|
PC[PortalComponents]
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
subgraph BFF [BFF App]
|
|
|
|
|
|
BC[BFFControllers]
|
|
|
|
|
|
BI[BFFIntegrations]
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
subgraph Domain [Domain Package]
|
|
|
|
|
|
DM[ModuleEntrypoints]
|
|
|
|
|
|
DP[ProviderEntrypoints]
|
|
|
|
|
|
DT[Toolkit]
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
PC -->|allowed| DM
|
|
|
|
|
|
PC -->|allowed| DT
|
|
|
|
|
|
PC -.->|BLOCKED| DP
|
|
|
|
|
|
|
|
|
|
|
|
BC -->|allowed| DM
|
|
|
|
|
|
BC -->|allowed| DT
|
|
|
|
|
|
BI -->|allowed| DM
|
|
|
|
|
|
BI -->|allowed| DP
|
|
|
|
|
|
BI -->|allowed| DT
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-12-26 14:53:03 +09:00
|
|
|
|
### Never
|
|
|
|
|
|
|
|
|
|
|
|
- `@customer-portal/domain/<module>/**` (anything deeper than the module entrypoint)
|
|
|
|
|
|
- `@customer-portal/domain/<module>/providers/**` (anything deeper than `/providers`)
|
|
|
|
|
|
- `apps/portal/**` importing any `@customer-portal/domain/*/providers`
|
|
|
|
|
|
|
|
|
|
|
|
### Rule of thumb
|
|
|
|
|
|
|
|
|
|
|
|
Import from the **highest stable entrypoint** that contains what you need.
|
|
|
|
|
|
|
|
|
|
|
|
- If it exists in `@customer-portal/domain/<module>`, don’t import a deeper file.
|
|
|
|
|
|
- If it’s provider-specific, use `@customer-portal/domain/<module>/providers` (BFF-only).
|
|
|
|
|
|
- If it’s cross-domain utility, use `@customer-portal/domain/toolkit`.
|
|
|
|
|
|
|
|
|
|
|
|
### When to create a new explicit entrypoint (instead of deep-importing)
|
|
|
|
|
|
|
|
|
|
|
|
Create/adjust exports when:
|
|
|
|
|
|
|
|
|
|
|
|
- The symbol is used in 2+ apps (Portal + BFF), or many call sites.
|
|
|
|
|
|
- The symbol is part of a workflow that should remain stable (pagination, formatting, shared validation helpers).
|
|
|
|
|
|
|
|
|
|
|
|
Where to export it:
|
|
|
|
|
|
|
|
|
|
|
|
- **Module root** (`@customer-portal/domain/<module>`): normalized domain types/models, schemas, provider-agnostic helpers.
|
|
|
|
|
|
- **Providers entrypoint** (`.../<module>/providers`): provider adapters, mapper/query builder logic, raw provider shapes (if truly needed).
|
|
|
|
|
|
- **Toolkit** (`@customer-portal/domain/toolkit`): shared utilities (`Formatting`, `Validation`, `Typing` namespaces).
|
|
|
|
|
|
|
|
|
|
|
|
### Naming conventions
|
|
|
|
|
|
|
|
|
|
|
|
- **Module root**: `Invoice`, `invoiceSchema`, `validateOrderBusinessRules`
|
|
|
|
|
|
- **Providers**: `Whmcs`, `Salesforce`, `Freebit` namespaces; raw shapes should be obviously integration-only.
|
|
|
|
|
|
|
|
|
|
|
|
### PR checklist
|
|
|
|
|
|
|
|
|
|
|
|
- No `@customer-portal/domain/*/*` imports (except exact `.../<module>/providers` in BFF).
|
|
|
|
|
|
- Portal has **zero** `.../providers` imports.
|
|
|
|
|
|
- No wildcard subpath exports added to `packages/domain/package.json#exports`.
|
2025-12-26 15:07:47 +09:00
|
|
|
|
|
|
|
|
|
|
## Validation
|
|
|
|
|
|
|
|
|
|
|
|
After changes:
|
|
|
|
|
|
|
|
|
|
|
|
1. Run `pnpm lint`
|
|
|
|
|
|
2. Run `pnpm type-check`
|
|
|
|
|
|
3. Run `pnpm build`
|