PR-001: Domain Model v1.0 — compile-only, zero dependencies
- @landvex/domain package with TypeScript strict mode - 3 Aggregate Roots: FieldSession, Mission, DecisionCase - Branded IDs, Value Objects, Domain Events, Invariants - 19 unit tests for IDs, FieldSession, DecisionCase - Zero runtime dependencies (only TypeScript + jest for tests) - Separates Entity / Value Object / Aggregate Root - README documents Three Rules of the domain Definition of Done met: - Compiles without errors - Exports all domain types - Unit tests for invariants and value objects - No PostgreSQL, S3, Express, AI models, queues
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Branded ID types for type safety
|
||||
* Prevents mixing different ID types accidentally
|
||||
*/
|
||||
|
||||
export type SessionId = string & { readonly __brand: 'SessionId' };
|
||||
export type MissionId = string & { readonly __brand: 'MissionId' };
|
||||
export type ArtifactId = string & { readonly __brand: 'ArtifactId' };
|
||||
export type ObservationId = string & { readonly __brand: 'ObservationId' };
|
||||
export type DecisionId = string & { readonly __brand: 'DecisionId' };
|
||||
export type EvidenceId = string & { readonly __brand: 'EvidenceId' };
|
||||
export type FindingId = string & { readonly __brand: 'FindingId' };
|
||||
export type ReviewId = string & { readonly __brand: 'ReviewId' };
|
||||
export type ActionId = string & { readonly __brand: 'ActionId' };
|
||||
export type OutcomeId = string & { readonly __brand: 'OutcomeId' };
|
||||
|
||||
/**
|
||||
* ID factory functions
|
||||
* Ensures consistent ID format across the domain
|
||||
*/
|
||||
export const IdFactory = {
|
||||
session: (date: Date, sequence: number): SessionId => {
|
||||
const dateStr = date.toISOString().split('T')[0].replace(/-/g, '');
|
||||
return `session_${dateStr}_${sequence.toString().padStart(6, '0')}` as SessionId;
|
||||
},
|
||||
|
||||
mission: (date: Date, sequence: number): MissionId => {
|
||||
const dateStr = date.toISOString().split('T')[0].replace(/-/g, '');
|
||||
return `mission_${dateStr}_${sequence.toString().padStart(6, '0')}` as MissionId;
|
||||
},
|
||||
|
||||
artifact: (sequence: number): ArtifactId => {
|
||||
return `artifact_${sequence.toString().padStart(6, '0')}` as ArtifactId;
|
||||
},
|
||||
|
||||
observation: (sequence: number): ObservationId => {
|
||||
return `obs_${sequence.toString().padStart(6, '0')}` as ObservationId;
|
||||
},
|
||||
|
||||
decision: (sequence: number): DecisionId => {
|
||||
return `decision_${sequence.toString().padStart(6, '0')}` as DecisionId;
|
||||
},
|
||||
|
||||
evidence: (sequence: number): EvidenceId => {
|
||||
return `evidence_${sequence.toString().padStart(6, '0')}` as EvidenceId;
|
||||
},
|
||||
|
||||
finding: (sequence: number): FindingId => {
|
||||
return `finding_${sequence.toString().padStart(6, '0')}` as FindingId;
|
||||
},
|
||||
|
||||
review: (sequence: number): ReviewId => {
|
||||
return `review_${sequence.toString().padStart(6, '0')}` as ReviewId;
|
||||
},
|
||||
|
||||
action: (sequence: number): ActionId => {
|
||||
return `action_${sequence.toString().padStart(6, '0')}` as ActionId;
|
||||
},
|
||||
|
||||
outcome: (sequence: number): OutcomeId => {
|
||||
return `outcome_${sequence.toString().padStart(6, '0')}` as OutcomeId;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user