Assist_Design/packages/domain/orders/RESTRUCTURING-PLAN.md

7.1 KiB
Raw Blame History

🏗️ Orders Domain Restructuring Plan

Date: October 2025
Status: Proposed
Priority: High Architectural Consistency


Why Split Validation?

Orders has two very different validation layers:

  1. Schema validation structural guarantees we can infer from Zod (e.g. shape of an incoming request or the normalization of a Salesforce record).
  2. Business rules SKU combinations, prerequisite checks, and cross-field rules that need to be reused by both the BFF and frontend logic.

Keeping those concerns in separate modules (*.schema.ts vs rules.ts) mirrors how other domains expose reusable behaviour (customer/validation.ts, sim/rules.ts) and makes it possible to import just the pure rule helpers in environments where running the full schema is overkill. It also prevents circular imports when business logic needs to call back into provider helpers.


Design Goals

  • Align the orders domain with the schema-first, provider-isolated structure used by customer, catalog, and sim.
  • Make the distinction between raw vendor data, normalized domain models, and business rules obvious.
  • Centralise dynamic Salesforce field-map handling so configuration is validated once and shared by mappers and query builders.
  • Improve testability by isolating pure transforms and validation helpers.

Current Pain Points

Area Issue
Contracts contract.ts mixes business constants with Salesforce configuration interfaces and schema re-exports.
Schemas schema.ts blends read models, write models, fulfillment data, and business refinements in a single block, making reuse awkward.
Providers The Salesforce mapper relies on unchecked configuration using Reflect.get, and WHMCS mapping redefines schemas inline.
Config Query helpers and the BFF maintain separate field lists, so config drift is likely.

Target File Layout

packages/domain/orders/
├── contract.ts                     # Business enums/constants only
├── index.ts                        # Public API surface
├── rules.ts                        # Domain business helpers (SKU rules, etc.)
├── schemas/
│   ├── read-model.schema.ts        # OrderSummary/OrderDetails
│   ├── fulfillment.schema.ts       # Fulfillment-specific schemas
│   ├── write-model.schema.ts       # CreateOrderRequest, OrderConfigurations
│   └── query.schema.ts             # Request/query params shared by API
├── providers/
│   ├── salesforce/
│   │   ├── field-map.schema.ts     # Zod schema + helpers for config
│   │   ├── raw.schema.ts           # Zod schemas for raw Order/OrderItem/Product2
│   │   ├── normalizer.ts           # Converts raw records using field resolver
│   │   ├── mapper.ts               # Maps normalised data to read-model schemas
│   │   └── query.ts                # Builds SOQL select lists via resolver
│   └── whmcs/
│       ├── raw.schema.ts           # Zod schemas for WHMCS payloads/responses
│       └── mapper.ts               # Uses fulfillment schema to build payloads
└── validation.ts (exports from rules.ts for backwards compatibility)

Note

: The schemas/ folder keeps runtime validation and inferred types colocated but organised by concern.


Workstreams & Tasks

1. Core Domain Modules

  • Rewrite contract.ts to expose ORDER_TYPE, ORDER_STATUS, ACTIVATION_TYPE, and other business constants only.
  • Create schemas/read-model.schema.ts, schemas/write-model.schema.ts, schemas/fulfillment.schema.ts, and schemas/query.schema.ts.
    • Move relevant sections from the existing schema.ts into those files.
    • Export inferred types alongside each schema.
  • Introduce rules.ts housing SKU/business helpers currently in validation.ts.
    • Keep validation.ts as a thin re-export for compatibility (mark for deprecation).
  • Update index.ts to surface the new modules explicitly.

2. Salesforce Provider Pipeline

  • Add field-map.schema.ts with a Zod schema that validates the dynamic field map coming from configuration.
    • Export a createFieldResolver(config) helper that normalises and memoises accessors.
  • Replace field-map.mapper.ts with raw.schema.ts, normalizer.ts, and mapper.ts.
    • Parse raw Salesforce records via raw.schema.ts.
    • Apply the field resolver in normalizer.ts to produce typed intermediate structures.
    • Feed intermediates into read-model schemas inside mapper.ts (no Reflect.get).
  • Update query.ts to consume the same resolver so the BFF and mapper stay in sync.

3. WHMCS Provider

  • Replace inline Zod definitions in providers/whmcs/mapper.ts with imports from schemas/fulfillment.schema.ts.
  • Ensure payload building reuses shared helpers (e.g. billing-cycle normaliser) and export them for testing.
  • Expand raw.schema.ts to cover response parsing where we currently trust types.

4. Public API & Backwards Compatibility

  • Adjust packages/domain/orders/index.ts to export:
    • Business constants from contract.ts.
    • Schemas/types from schemas/*.
    • Business helpers from rules.ts.
    • Provider modules via a consolidated Providers namespace.
  • Provide compatibility re-exports for previous paths (validation.ts, existing provider raw types) and document deprecations.

5. Integration Touchpoints

  • Update apps/bff/src/core/config/field-map.ts to import createFieldResolver and use the generated select lists.
  • Update services (order-orchestrator, order-validator) to consume new exports (rules, resolver helpers).
  • Add focused unit tests around the resolver, Salesforce normaliser, and WHMCS mapper changes.

Implementation Order

  1. Create new schema modules alongside the existing schema.ts, export them, and update imports progressively.
  2. Introduce the Salesforce field-map schema/resolver and migrate providers/BFF to use it.
  3. Refactor provider mappers to run through the new normaliser/mapper pipeline.
  4. Trim the legacy modules once callers are updated (delete old schema.ts sections, replace with barrel exports).
  5. Finalize index.ts and deprecation notes, then run type checks/tests.

Acceptance Criteria

  • Orders domain mirrors the modular structure outlined above.
  • Field-map configuration fails fast when misconfigured and is reused everywhere.
  • Provider mappers no longer rely on unchecked property access.
  • Business rule helpers are in rules.ts with tests demonstrating reuse.
  • BFF compiles against the new API surface, and manual order flows pass.

Next Steps

  1. Socialise this plan with the team and confirm the module naming/placement fits the broader architecture.
  2. Kick off Workstream 1 to get the new schemas and constants in place.
  3. Build the Salesforce resolver and migrate the BFF configuration service.
  4. Continue through the remaining workstreams, validating with tests at each step.

Ready to proceed once reviewers sign off on the structure. 🚀