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
stringfield 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:
| Enum | Concept | |
|---|---|---|
| Instances | Fixed at design time | Created at runtime |
| Has attributes | ❌ | ✅ |
| Can be extended | ❌ (closed list) | ✅ (via sub) |
| Use case | Dropdowns, categories | Real-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.