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,96 @@
|
||||
/**
|
||||
* Review - Entity
|
||||
* Human quality assessment of a decision
|
||||
*
|
||||
* Invariants:
|
||||
* - Must have a reviewer
|
||||
* - Must have a status
|
||||
* - Cannot be approved without review
|
||||
*/
|
||||
|
||||
import { ReviewId, DecisionId } from '../common/ids';
|
||||
import { ReviewStatus } from '../common/enums';
|
||||
import { InvariantViolationError } from '../common/errors';
|
||||
|
||||
export interface Review {
|
||||
readonly id: ReviewId;
|
||||
readonly decisionId: DecisionId;
|
||||
readonly status: ReviewStatus;
|
||||
readonly reviewer: string;
|
||||
readonly comments: string;
|
||||
readonly createdAt: Date;
|
||||
readonly updatedAt?: Date;
|
||||
readonly completedAt?: Date;
|
||||
}
|
||||
|
||||
export interface CreateReviewParams {
|
||||
readonly id: ReviewId;
|
||||
readonly decisionId: DecisionId;
|
||||
readonly reviewer: string;
|
||||
}
|
||||
|
||||
export class ReviewFactory {
|
||||
static create(params: CreateReviewParams): Review {
|
||||
if (!params.reviewer) {
|
||||
throw new InvariantViolationError('Review must have a reviewer');
|
||||
}
|
||||
|
||||
return {
|
||||
...params,
|
||||
status: ReviewStatus.ASSIGNED,
|
||||
comments: '',
|
||||
createdAt: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
static startReview(review: Review): Review {
|
||||
if (review.status !== ReviewStatus.ASSIGNED) {
|
||||
throw new InvariantViolationError('Cannot start review that is not assigned');
|
||||
}
|
||||
|
||||
return {
|
||||
...review,
|
||||
status: ReviewStatus.IN_REVIEW,
|
||||
updatedAt: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
static approve(review: Review, comments: string): Review {
|
||||
if (review.status !== ReviewStatus.IN_REVIEW) {
|
||||
throw new InvariantViolationError('Cannot approve review that is not in review');
|
||||
}
|
||||
|
||||
return {
|
||||
...review,
|
||||
status: ReviewStatus.APPROVED,
|
||||
comments,
|
||||
completedAt: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
static reject(review: Review, comments: string): Review {
|
||||
if (review.status !== ReviewStatus.IN_REVIEW) {
|
||||
throw new InvariantViolationError('Cannot reject review that is not in review');
|
||||
}
|
||||
|
||||
return {
|
||||
...review,
|
||||
status: ReviewStatus.REJECTED,
|
||||
comments,
|
||||
completedAt: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
static requestMoreEvidence(review: Review, comments: string): Review {
|
||||
if (review.status !== ReviewStatus.IN_REVIEW) {
|
||||
throw new InvariantViolationError('Cannot request evidence for review that is not in review');
|
||||
}
|
||||
|
||||
return {
|
||||
...review,
|
||||
status: ReviewStatus.NEEDS_MORE_EVIDENCE,
|
||||
comments,
|
||||
completedAt: new Date()
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user