domain-objects
Version:
A simple, convenient way to represent domain objects, leverage domain knowledge, and add runtime validation in your code base.
116 lines (115 loc) • 5.77 kB
TypeScript
/**
* .what = the kind of a domain object — which base class it extends
* .why =
* - the pragma stamps the true subclass so a consumer can pick the right base class to rebuild
* - replaces the fragile `primary`-non-empty heuristic (a literal may declare `primary`; an
* entity may omit it) with the class's own marker-symbol truth
* .note = `object` is the fallback for a plain `DomainObject` with a schema but no subclass marker
*/
export type DomainObjectKind = 'entity' | 'literal' | 'event' | 'object';
/**
* .what = the type of a domain object's `x-domain-object` **pragma** — the static declarations
* stamped onto its `.contract` schema so a cross-service consumer can reconstruct the class.
*
* .why =
* a domain object splits into two halves on the wire:
* - **instance** data (a Job's `uuid`, `status` values) → already carried by the schema/contract
* - **static** declarations (the Job class's `primary`, `unique`, `kind`) → the schema cannot
* carry class statics, so the pragma exists to carry them
* the *only* reason to read this type is to **rebuild the class** (codegen turns it back into
* `class Job extends DomainEntity { static primary = [...] }`). a consumer who does not care
* about the class does not need these fields.
*
* .note = the term **pragma**, from Greek `prâgma` ("a deed, a matter of fact"; cf. C's `#pragma`):
* a stated fact stamped alongside a payload that declares how to interpret it. the domain-object
* pragma family states a schema node's domain-object semantics:
*
* | pragma (json keyword) | states | type |
* |-----------------------|---------------------------|-------------------------------------|
* | `x-domain-object` | this node **IS** dobj X | `DomainObjectPragma` (this type) |
* | `x-domain-object-ref` | this node **REFERENCES** X | `DomainObjectPragmaRef` |
*
* the base + `Ref` partner mirrors the runtime pair `DomainObject : Ref`. both ship today:
* `DomainObjectPragma` (the full body, via `.contract`) and `DomainObjectPragmaRef` (a key-only
* reference, via `.contract.ref(by)`).
*
* .note = fields are **optional** where the stamp omits them: `getContract` writes `primary`/
* `unique`/`alias`/`nested` only when the dobj declares them, so a consumer reads e.g.
* `pragma.primary` as `string[] | undefined`. field names mirror the class statics exactly.
*
* @example
* // the `x-domain-object` node comes off an untyped json-schema blob, so the cast is a
* // boundary read — domain-objects ships the type, the consumer owns the node access
* const pragma = jsonSchema['x-domain-object'] as DomainObjectPragma;
* pragma.kind; // 'entity' | 'literal' | 'event' | 'object'
* pragma.primary; // string[] | undefined
*/
export type DomainObjectPragma = {
/**
* the domain object's class name (`constructor.name`) — names the reconstructed type
*/
name: string;
/**
* which base class the domain object extends — picks `DomainEntity` / `DomainLiteral` /
* `DomainEvent` (or a plain `DomainObject`) when the consumer rebuilds the class
*/
kind: DomainObjectKind;
/**
* the surrogate key (`DomainEntity.primary` / `DomainLiteral.primary`), when declared
*/
primary?: string[];
/**
* the natural key (`DomainEntity.unique`), when declared
*/
unique?: string[];
/**
* the colloquial alias (`DomainObject.alias`), when declared
*/
alias?: string | {
singular?: string;
plural?: string;
};
/**
* the nested dobj names by key (`DomainObject.nested`), when declared — a single name, or an
* array of names for polymorphic nested choices
*/
nested?: Record<string, string | string[]>;
};
/**
* .what = which key(s) a domain-object reference carries over the wire
* .why = a reference points at a dobj *by* one of its keys — the surrogate (`primary`), the
* natural key (`unique`), or either (`ref`, a union of the two). mirrors the runtime `Ref` family.
*/
export type DomainObjectRefBy = 'primary' | 'unique' | 'ref';
/**
* .what = the type of a domain object's `x-domain-object-ref` **pragma** — stamped onto a schema
* node that *references* a dobj by key (rather than *is* the dobj, which `x-domain-object` states).
*
* .why =
* a contract field often references another dobj by its key, not by its whole body:
* `class SurfTrophy { rider: Seaturtle.contract.ref('primary') }` → the wire carries just
* `{ uuid }`, and this pragma states "that `{ uuid }` **references** a Seaturtle by primary".
* a consumer reads it to emit a typed `RefByPrimary<typeof Seaturtle>` instead of an anonymous
* `{ uuid: string }`. deliberately smaller than `DomainObjectPragma`: a reference needs only
* *which dobj* + *which key*, not the full body nor `kind` — the target's own `.contract`
* (bound elsewhere) already states those.
*
* .note = the schema-level partner of the runtime `refByPrimary` / `refByUnique` ops and the
* `RefByPrimary` / `RefByUnique` types. it is to `DomainObjectPragma` what a reference is to a
* composition — see the table on `DomainObjectPragma`.
*
* @example
* const pragma = jsonSchema.properties.rider['x-domain-object-ref'] as DomainObjectPragmaRef;
* pragma.of; // 'Seaturtle'
* pragma.by; // 'primary'
*/
export type DomainObjectPragmaRef = {
/**
* the referenced dobj's class name (`constructor.name`) — names the reconstructed reference type
*/
of: string;
/**
* which key(s) the reference carries — `primary` / `unique` / `ref` (a union of both)
*/
by: DomainObjectRefBy;
};