Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/0Crazy-0/ClinicFlow/llms.txt

Use this file to discover all available pages before exploring further.

ClinicFlow uses a layered exception strategy: the Domain layer defines a rich hierarchy of typed exceptions that carry standardised string error codes, the Application layer adds a ValidationException for FluentValidation pipeline failures, and the presentation layer (once implemented) is responsible for mapping these to HTTP status codes and structured error responses.
All error codes are compile-time constants defined in DomainErrors — a static class in ClinicFlow.Domain.Common. The nested classes are: General, Validation, Appointment, AppointmentType, Schedule, Patient, Doctor, MedicalSpecialty, ClinicalFormTemplate, User, Penalty, and MedicalEncounter. Error strings are never written as magic strings inside business logic; always reference the constant directly (e.g., DomainErrors.Appointment.CannotCancel).

Domain Exception Hierarchy

All domain exceptions ultimately inherit from DomainException, which is the single base class:
// ClinicFlow.Domain.Exceptions.Base
public abstract class DomainException(string errorCode) : Exception(errorCode) { }
The Message property of every DomainException is the errorCode constant string — not a human-readable sentence. This keeps the domain layer free from presentation concerns; the host layer translates codes to user-facing messages.

Base Exception Types

Thrown when a value object or entity receives a value that violates a validation rule (wrong format, out of range, etc.).
namespace ClinicFlow.Domain.Exceptions.Base;

public class DomainValidationException(string errorCode) : DomainException(errorCode) { }
Typical triggers: invalid email format, negative duration, phone number not matching E.164, blood type not in allowed set.
Thrown when an operation is structurally valid but violates a business invariant (e.g., trying to activate an already-active specialty, or adding a specialty to a global appointment type).
namespace ClinicFlow.Domain.Exceptions.Base;

public class BusinessRuleValidationException(string errorCode) : DomainException(errorCode) { }
Typical triggers: duplicate name creation, status transition that is not allowed by the domain state machine, constraint violations on aggregate relationships.
Thrown by application-layer handlers when a repository returns null for a required entity lookup.
namespace ClinicFlow.Domain.Exceptions.Base;

public class EntityNotFoundException(string errorCode, string entityName, object id)
    : DomainException(errorCode)
{
    public string EntityName { get; } = entityName;
    public object Id { get; } = id;
}
EntityName and Id provide structured context that a presentation layer can include in a 404 response body.

Appointment Exceptions

ClassExtra PropertiesError Code Used
AppointmentCancellationNotAllowedExceptionAppointmentStatus CurrentStatusDomainErrors.Appointment.CannotCancel
AppointmentCancellationUnauthorizedExceptionDomainErrors.Appointment.UnauthorizedCancellation
AppointmentConflictExceptionGuid DoctorId, DateTime DateDomainErrors.Appointment.Conflict
AppointmentNoShowUnauthorizedExceptionDomainErrors.Appointment.UnauthorizedNoShow
AppointmentReschedulingNotAllowedExceptionDomainErrors.Appointment.CannotReschedule
AppointmentSchedulingUnauthorizedExceptionDomainErrors.Appointment.UnauthorizedScheduling
// Example: carries the current status so the caller can reason about it
public class AppointmentCancellationNotAllowedException(
    string errorCode,
    AppointmentStatus currentStatus
) : DomainException(errorCode)
{
    public AppointmentStatus CurrentStatus { get; } = currentStatus;
}

Patient Exceptions

ClassExtra PropertiesNotes
PatientBlockedExceptionDateOnly BlockedUntilPatient has an active penalty block
IncompleteProfileExceptionPatient profile missing required fields
PatientAccessUnauthorizedExceptionCaller is not authorised to access this patient record
public class PatientBlockedException(string errorCode, DateOnly blockedUntil)
    : DomainException(errorCode)
{
    public DateOnly BlockedUntil { get; } = blockedUntil;
}

Scheduling Exceptions

ClassExtra PropertiesNotes
DoctorNotAvailableExceptionGuid DoctorId, DayOfWeek DayOfWeekNo active schedule found for the day
InvalidTimeRangeExceptionStart time is not before end time
ScheduleAlreadyExistsExceptionGuid DoctorId, DayOfWeek DayOfWeekDuplicate schedule for the same doctor/day combination

Application Layer: ValidationException

The Application layer adds one additional exception type that is not a domain exception — it comes from the MediatR validation pipeline:
using FluentValidation.Results;

namespace ClinicFlow.Application.Exceptions;

public class ValidationException : Exception
{
    public const string DefaultErrorMessage = "One or more validation failures have occurred.";

    /// <summary>
    /// Gets the collection of validation failures grouped by property name.
    /// </summary>
    public IDictionary<string, string[]> Errors { get; }

    public ValidationException() : base(DefaultErrorMessage)
    {
        Errors = new Dictionary<string, string[]>();
    }

    public ValidationException(IEnumerable<ValidationFailure> failures) : this()
    {
        Errors = failures
            .GroupBy(e => e.PropertyName, e => e.ErrorMessage)
            .ToDictionary(g => g.Key, g => g.ToArray());
    }
}
ValidationException is thrown by the ValidationBehavior<TRequest, TResponse> MediatR pipeline behaviour when any registered AbstractValidator<T> returns failures. Errors is a dictionary keyed by property name — suitable for a structured 400 response with per-field error details.

DomainErrors Constant Catalogue

DomainErrors is a nested static class in ClinicFlow.Domain.Common that organises all error code strings by domain concept. The presentation layer maps these constants to HTTP status codes and localised messages.
public static class General
{
    public const string RequiredFieldNull = "REQUIRED_FIELD_NULL";
    public const string NotFound          = "ENTITY_NOT_FOUND";
}

public static class Validation
{
    public const string ValueRequired                = "VALUE_REQUIRED";
    public const string InvalidValue                 = "INVALID_VALUE";
    public const string ValueTooShort                = "VALUE_TOO_SHORT";
    public const string ValueCannotBeNegative        = "VALUE_CANNOT_BE_NEGATIVE";
    public const string ValueMustBePositive          = "VALUE_MUST_BE_POSITIVE";
    public const string ValueCannotBeInFuture        = "VALUE_CANNOT_BE_IN_FUTURE";
    public const string ValueMustBeInFuture          = "VALUE_MUST_BE_IN_FUTURE";
    public const string InvalidEmailFormat           = "INVALID_EMAIL_FORMAT";
    public const string InvalidPhoneFormat           = "INVALID_PHONE_FORMAT";
    public const string InvalidBloodType             = "INVALID_BLOOD_TYPE";
    public const string InvalidFormat                = "INVALID_FORMAT";
    public const string StartTimeMustBeBeforeEndTime = "START_TIME_MUST_BE_BEFORE_END_TIME";
    public const string EndTimeMustBeAfterStartTime  = "END_TIME_MUST_BE_AFTER_START_TIME";
    public const string InvalidDateRange             = "INVALID_DATE_RANGE";
    public const string InvalidEnumValue             = "INVALID_ENUM_VALUE";
    public const string ValueTooLong                 = "VALUE_TOO_LONG";
    public const string ValueExceedsMaximum          = "VALUE_EXCEEDS_MAXIMUM";
    public const string DuplicateValues              = "DUPLICATE_VALUES";
}

Exception-to-HTTP Mapping Reference

The following table shows the recommended mapping for the presentation layer (ASP.NET Core middleware or exception filter):
Exception TypeRecommended HTTP StatusNotes
EntityNotFoundException404 Not FoundInclude EntityName and Id in response body
DomainValidationException422 Unprocessable EntityValidation rule on a value object
BusinessRuleValidationException422 Unprocessable EntityBusiness invariant violated
AppointmentCancellationNotAllowedException422 Unprocessable EntityInclude CurrentStatus in response
AppointmentConflictException409 ConflictInclude DoctorId and Date
PatientBlockedException403 ForbiddenInclude BlockedUntil in response
PatientAccessUnauthorizedException403 Forbidden
AppointmentCancellationUnauthorizedException403 Forbidden
AppointmentSchedulingUnauthorizedException403 Forbidden
ValidationException (Application)400 Bad RequestInclude Errors dictionary

Build docs developers (and LLMs) love