Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 4: Species and Breeds

The receptionist had entered “dgo” as the species for a patient. Then “Feline (domestic shorthair)”. Then just “bird”. Dr. Portbridge realized that a string field for species was a liability, it offered infinite freedom where she needed a closed list.


The problem with strings

When a field accepts any string, you get inconsistency:

  • "Dog" vs "dog" vs "Canine"
  • "Cat" vs "Feline" vs "feline (domestic)"

Queries break. Reports are nonsense. You need a controlled vocabulary.

Closed concepts

A closed concept defines a fixed, exhaustive set of allowed values:


concept Species:
  one of:
    Dog
    Cat
    Bird
    Rabbit
    Reptile
    Other

Now replace the string in Animal:


concept Animal:
  has name: string
  has species: Species
  has age: int
  has weight: float
  has owner: Owner

species can only be one of the six values listed in the enum. No typos, no ambiguity, no “dgo”.

Multiple closed concepts

The clinic also needs to categorize appointment urgency:


concept Urgency:
  one of:
    Routine
    Urgent
    Emergency

And appointment status:


concept AppointmentStatus:
  one of:
    Scheduled
    InProgress
    Completed
    Cancelled

Now Appointment becomes:


concept Appointment:
  has animal: Animal
  has date: string
  has reason: string
  has urgency: Urgency
  has status: AppointmentStatus
  has diagnosis: string
  has treatment: string

Enums vs concepts

Enums and concepts are very different things:

EnumConcept
InstancesFixed at design timeCreated at runtime
Has attributes
Can be extended❌ (closed list)✅ (via sub)
Use caseDropdowns, categoriesReal-world entities

Use enums for things that won’t change or change rarely (statuses, categories, units). Use concepts for things that live and breathe (patients, people, appointments).

The story so far


package <http://happypaws.com/clinic>:
  dolfin_version "1"
  version "0.1.0"
  author "Dr. Helen Portbridge"
  description "The Happy Paws veterinary clinic data model"

concept Species:
  one of:
    Dog
    Cat
    Bird
    Rabbit
    Reptile
    Other

concept Urgency:
  one of:
    Routine
    Urgent
    Emergency

concept AppointmentStatus:
  one of:
    Scheduled
    InProgress
    Completed
    Cancelled

concept Owner:
  has first_name: string
  has last_name: string
  has phone: string

concept Veterinarian:
  has name: string
  has license_number: string

concept Animal:
  has name: string
  has species: Species
  has age: int
  has weight: float
  has owner: Owner

concept Appointment:
  has animal: Animal
  has date: string
  has reason: string
  has urgency: Urgency
  has status: AppointmentStatus
  has diagnosis: string
  has treatment: string

property treatedBy:
  Animal -> Veterinarian

Try it

Add an enum PaymentMethod with values Cash, Card, Insurance, and Pending. Then add a payment attribute to Appointment:


concept PaymentMethod:
  one of:
    Cash

concept Appointment:
  has animal: Animal
  has date: string
  has reason: string
  has urgency: Urgency
  has status: AppointmentStatus

The enums solved the typo problem. Then a cardboard box appeared on the exam table. Inside was a tiny stray kitten, no collar, no owner, no known age. Dr. Portbridge named her Pixel on the spot and tried to enter her into the system: species Cat, name “Pixel”, and… that was it. No owner to reference, no age to record. The system accepted Pixel without complaint. In fact, it would have accepted an Animal with no name at all, or one with five species. Every attribute had cardinality “any”: no minimum, no maximum, no constraints whatsoever. That was fine for a first sketch, but now she needed precision. “Every animal MUST have exactly one name. An owner might not have an email. A phone number is required.” She needed cardinality.