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,73 @@
|
||||
/**
|
||||
* FieldSession - Aggregate Root
|
||||
* Organizes field work. A pilot day produces many missions.
|
||||
*
|
||||
* Invariants:
|
||||
* - Must have exactly one location
|
||||
* - Must have a date
|
||||
* - Can have zero or more missions
|
||||
* - Cannot be completed if any mission is still uploading
|
||||
*/
|
||||
|
||||
import { SessionId, MissionId } from '../common/ids';
|
||||
import { SessionStatus } from '../common/enums';
|
||||
import { GeoLocation } from '../common/value-objects';
|
||||
import { InvariantViolationError } from '../common/errors';
|
||||
|
||||
export interface FieldSession {
|
||||
readonly id: SessionId;
|
||||
readonly location: GeoLocation;
|
||||
readonly date: Date;
|
||||
readonly status: SessionStatus;
|
||||
readonly missionIds: MissionId[];
|
||||
readonly createdAt: Date;
|
||||
readonly updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface CreateSessionParams {
|
||||
readonly id: SessionId;
|
||||
readonly location: GeoLocation;
|
||||
readonly date: Date;
|
||||
}
|
||||
|
||||
export class FieldSessionFactory {
|
||||
static create(params: CreateSessionParams): FieldSession {
|
||||
if (!params.location) {
|
||||
throw new InvariantViolationError('Session must have a location');
|
||||
}
|
||||
if (!params.date) {
|
||||
throw new InvariantViolationError('Session must have a date');
|
||||
}
|
||||
|
||||
return {
|
||||
id: params.id,
|
||||
location: params.location,
|
||||
date: params.date,
|
||||
status: SessionStatus.PLANNED,
|
||||
missionIds: [],
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
static addMission(session: FieldSession, missionId: MissionId): FieldSession {
|
||||
return {
|
||||
...session,
|
||||
missionIds: [...session.missionIds, missionId],
|
||||
status: SessionStatus.ACTIVE,
|
||||
updatedAt: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
static complete(session: FieldSession): FieldSession {
|
||||
if (session.status !== SessionStatus.ACTIVE) {
|
||||
throw new InvariantViolationError('Cannot complete session that is not active');
|
||||
}
|
||||
|
||||
return {
|
||||
...session,
|
||||
status: SessionStatus.COMPLETED,
|
||||
updatedAt: new Date()
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user