Objects and behaviours
Rich domain objects
Section titled “Rich domain objects”Objects hold clinically meaningful state and expose clinically meaningful behaviour.
class StudySite { identity: StudySiteIdentity; study: StudyReference; facility: FacilityReference; country: StudyCountryReference; participation: SiteParticipation; activationHistory: SiteActivationDecision[];
select(decision: SiteSelectionDecision): SiteSelected; beginStartup(plan: SiteStartupPlan): SiteStartupStarted; activate(assessment: SiteReadinessAssessment): SiteActivated; openEnrollment(decision: EnrollmentOpeningDecision): SiteOpenedForEnrollment; suspendEnrollment(reason: SuspensionReason): SiteEnrollmentSuspended; close(decision: SiteClosureDecision): StudySiteClosed;}Value objects
Section titled “Value objects”Value objects prevent clinical meaning from degrading into primitive strings and numbers.
class VisitWindow { target: ClinicalDate; earliest: ClinicalDate; latest: ClinicalDate;
contains(date: ClinicalDate): boolean; classify(date: ClinicalDate): 'early' | 'in-window' | 'late';}Other foundational values include:
ClinicalDate,PartialClinicalDate, andClinicalDateTimeCodedTermandTerminologyReleaseReferenceMeasurement,Quantity,Unit, andReferenceRangeVisitWindowandTimingConstraintReason,Rationale, andCommentConsentScopeandPermittedPurposeBlindClassificationandPrivacyClassificationProtocolVersionReferenceandDefinitionRevisionReference
Policies
Section titled “Policies”Some decisions require several objects and should not be forced into one entity.
class ParticipantEligibilityPolicy { assess( criteria: EligibilityCriterion[], evidence: EligibilityEvidence, protocol: ProtocolVersionReference, ): EligibilityAssessment;}class SiteReadinessPolicy { assess( site: StudySite, approvals: ApplicableApprovalSet, agreement: ExecutedAgreement, qualifications: QualificationSet, requiredEvidence: EssentialEvidenceSet, ): SiteReadinessAssessment;}Actions and outcomes
Section titled “Actions and outcomes”An action describes intent. The object or policy determines the outcome.
Action Object / policy Outcome────────────────────────────────────────────────────────────────────ApproveProtocolVersion -> Protocol -> ProtocolVersionApprovedActivateStudySite -> StudySite -> StudySiteActivatedDetermineEligibility -> EligibilityPolicy -> EligibilityDecisionMadeEnrollParticipant -> StudyParticipant -> ParticipantEnrolledRandomizeParticipant -> RandomizationDesign -> TreatmentAssignedRecordObservation -> ObservationSet -> ObservationRecordedDispenseKit -> SiteInventory -> KitDispensedConfirmDeviation -> DeviationAssessment -> DeviationConfirmedLockDatabase -> DataFinalization -> DatabaseLockedInvalid behaviour
Section titled “Invalid behaviour”Invalid actions produce domain explanations, not infrastructure errors.
type EnrollmentRefusal = | ConsentNotEffective | ParticipantNotEligible | SiteNotOpenForEnrollment | ProtocolVersionMismatch | ConflictingEnrollment | EnrollmentLimitReached;These refusal objects carry the clinical reason and affected objects. They do not contain HTTP status codes or UI messages.
Behaviour belongs with meaning
Section titled “Behaviour belongs with meaning”The domain library should determine:
- whether an action is valid;
- which invariant would be violated;
- what state changes conceptually;
- which domain outcome occurred;
- what evidence must accompany the outcome.
The domain library should not determine:
- where the object is stored;
- which transaction technology is used;
- how the action reached the library;
- which screen initiated it;
- how notifications or integrations are delivered.