/** * 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() }; } }