Table of Contents >> Show >> Hide
- What a Database Domain Actually Means
- Why Domains Matter (Even If Your App “Validates Inputs”)
- Domain vs. Data Type vs. Constraints
- How to Define Domains in Popular Databases
- A Practical Process for Defining Domains (Without Overthinking It)
- Domain Design Patterns That Save You Later
- Common Mistakes (AKA: How Bad Data Sneaks In Wearing a Fake Mustache)
- How to Document a Domain (So Humans Can Use It)
- Experiences From Real Projects: Defining Domains in the Wild (Extra ~)
- Conclusion
“Domain” is one of those database words that causes instant confusionmostly because the internet also uses “domain” to mean “a website address.” Today, we’re talking about the database kind: the rules that decide what values are allowed to live in a column (and which ones get politelyor rudelyrejected).
If you’ve ever looked at a table and thought, “Why is someone’s age ‘banana’?” then congratulations: you already understand why database domains matter.
What a Database Domain Actually Means
In the relational model, a domain is the set of valid values an attribute (column) is allowed to take. Think of it as a bouncer for your data: it checks IDs, enforces dress code, and doesn’t care how convincing your application code sounds.
Domain = Meaning + Type + Rules
A practical way to define a database domain is:
- Meaning: What the value represents in the real world (Age, Email, ZIP code, Order Status).
- Base type: The storage/primitive type (INTEGER, VARCHAR, DATE, DECIMAL).
- Rules (constraints): Range limits, formats, allowed lists, NOT NULL, defaults, etc.
So while a data type answers “what shape is the value?”, a domain answers “what values make sense here?” A column might be VARCHAR(2), but the domain might be “US State Code,” meaning it must be one of AL, AK, AZ, … (and not XX unless you’re secretly building a spy agency database).
Why Domains Matter (Even If Your App “Validates Inputs”)
Relying only on application validation is like trusting every houseguest to read your “Please remove shoes” sign. Some will. Some won’t. Some will claim their shoes are “emotionally supportive.”
Defining domains at the database level helps you:
- Protect data integrity across every insert/update path (apps, scripts, ETL jobs, imports, admin tools).
- Reduce duplication by reusing the same rules in many tables/columns.
- Improve readability (“this column is an EmailAddress domain”) instead of “varchar(254) with vibes.”
- Centralize business rules so changes happen once, not in 14 microservices and 3 forgotten cron jobs.
- Make bad data harder to create, which is the nicest gift you can give Future You.
Domain vs. Data Type vs. Constraints
Data Type
A data type defines storage format and operations: integer math, text comparisons, date arithmetic, and so on. It’s foundationalbut often too broad.
Constraints
Constraints restrict values: NOT NULL, CHECK, UNIQUE, FOREIGN KEY. These can be attached directly to a table column or table definition.
Domain (User-Defined Type / Domain Type)
A domain packages “type + constraints” into something reusable. Instead of repeating a CHECK constraint everywhere, you define it once and apply it many times.
Not every database engine implements domains the same way. Some support a formal CREATE DOMAIN. Others use user-defined types (aliases/distinct types). Others encourage constraints directly on columns. But the design goal is the same: define valid values clearly and consistently.
How to Define Domains in Popular Databases
Let’s make this concrete with examples. (Don’t worrythese are the good kind of examples, not the “assume the user is a spherical cow” kind.)
PostgreSQL: CREATE DOMAIN (Classic Domain Types)
PostgreSQL supports true domain types: a domain is a data type with optional constraints. You define it once, then use it in tables like any other type.
Why it’s nice: You stop copy-pasting the same constraints into every table. Also, your schema starts telling a story: “This is an email,” not “This is a text field we’re hoping behaves.”
Oracle Database: Domains as First-Class Objects
Oracle has introduced “domain” features that let you define a domain based on an underlying data type and apply domain-specific conditions/expressions (depending on version/features). This aims to standardize validation and meaning across the schema.
Whether you use Oracle domains, user-defined types, or column constraints, the domain idea remains the same: define “what values are valid” once and apply consistently.
SQL Server: User-Defined Data Type Aliases
SQL Server supports user-defined data type aliases (often created via CREATE TYPE) that build on system types. These help standardize meaningthough the rule-enforcement story often still leans heavily on constraints and validation logic.
Tip: In SQL Server, you’ll commonly combine type aliases for consistency with CHECK constraints for enforcement.
MySQL: Domains by Constraint Patterns (and CHECK Constraints)
MySQL commonly expresses domains using column definitions plus CHECK constraints. The exact behavior depends on version and configuration, so teams often standardize a “domain pattern” (same type + same check) across tables.
If you don’t define (and reuse) a consistent status domain, you eventually end up with paid, PAID, payed (noooo), and payment_received living together like awkward roommates.
SQLite: Be Extra Clear About Type Rules
SQLite is famously flexible about types (“type affinity”). That flexibility is great for embedded use cases, but it means your “domain” strategy often relies more on constraints, careful schema choices, and (when appropriate) stricter table settings.
The lesson: when the database is permissive, your domain definitions need to be even more intentional.
A Practical Process for Defining Domains (Without Overthinking It)
Here’s a clean workflow teams use to define database domains in a way that scales.
1) Name the Real-World Concept
Start with meaning: What is this value? “Age” is not the same as “YearsEmployed,” even if both are integers. “Money” is not the same as “Percent.” The name should communicate intent.
2) Pick the Narrowest Sensible Base Type
- Use
INTEGERwhen fractional values don’t make sense. - Use
DECIMALfor money (avoid floating-point surprises). - Use
DATE/TIMESTAMPfor time-based factsdon’t store dates as strings unless you enjoy debugging. - Use
CHAR(2)for fixed-length codes (like state abbreviations),VARCHARfor variable.
3) Add Rules That Reflect Reality
Common domain rules include:
- Ranges:
age BETWEEN 0 AND 130 - Allowed sets: status in a known list
- Format checks: ZIP code pattern, ISO code shape
- Nullability: is missing allowed?
- Defaults: only when there’s a truly safe default
4) Decide: Domain Type vs. Reference Table
Two popular approaches:
- Domain constraints (type + check): great for ranges and formats.
- Reference tables (foreign keys): great when the allowed values are data you manage (statuses with metadata, country lists, role definitions).
Rule of thumb: if the “allowed values” list needs descriptions, ordering, or lifecycle management, it probably belongs in a table.
Domain Design Patterns That Save You Later
Pattern: “Strongly Typed IDs”
Many schemas have multiple IDs that are all integers: customer_id, order_id, invoice_id. A domain like positive_int (or more specific types in engines that support them) helps prevent nonsense like negative IDs and makes intent clearer.
Pattern: “Money as Minor Units”
Storing money as cents (integer minor units) can simplify rounding and comparisons. A domain like nonnegative_cents makes it reusable and prevents negative pricing accidentsunless you’re intentionally modeling discounts and credits.
Pattern: “Normalized Text Domains”
For fields like email or usernames, domains can enforce trimming, casing rules, or format checks. Just don’t try to validate the entire internet with a single regex. (The internet always wins.)
Common Mistakes (AKA: How Bad Data Sneaks In Wearing a Fake Mustache)
Mistake 1: Confusing Format with Truth
A value that matches “email shape” is not guaranteed to be a real, deliverable email. Domains should enforce structural validity; deeper verification belongs in application workflows.
Mistake 2: Over-Validating Too Early
Overly strict domains can block real-world edge cases. Example: names aren’t just A–Z. Addresses aren’t always neat. If your domain rejects legitimate users, they will not thank you with a nice note.
Mistake 3: Inconsistent Definitions Across Systems
One service uses status as pending/paid/shipped. Another uses created/settled/fulfilled. Reporting becomes a translation project. A clear domain definition (and shared governance) prevents this drift.
Mistake 4: Assuming Every Database Treats Types the Same
Portability is real. PostgreSQL domains work differently from SQL Server type aliases; SQLite’s typing behavior is different from stricter engines. If you’re multi-database, document your domain rules and enforce them consistently.
How to Document a Domain (So Humans Can Use It)
A domain definition is most useful when it’s documented. A simple template:
- Name:
us_zip_code - Meaning: US ZIP or ZIP+4
- Base type:
VARCHAR(10) - Rules: 5 digits or 5+4 with hyphen
- Examples:
02139,30301-1234 - Non-examples:
ABCDE,1234
If you’re working with a team, store these definitions near your schema (migrations repo, internal docs, or data catalog). The best domain is the one people actually know exists.
Experiences From Real Projects: Defining Domains in the Wild (Extra ~)
Even though “domain” sounds academic, it shows up in everyday work fastusually right after something breaks. Here are some common real-world experiences teams run into when defining database domains, told in the “we learned this the hard way so you don’t have to” style.
Experience 1: The Mystery of the 17 Different “Status” Values
A team launches an e-commerce system with an orders table and a status column. At first, it’s simple: pending, paid, shipped. Then marketing wants on_hold. Support wants needs_review. Finance wants refunded. Someone adds refund_pending. Another person adds refund-in-progress because hyphens are fun (they are not).
Months later, analytics tries to answer a basic question“How many orders were shipped last week?”and discovers that “shipped” includes shipped, SHIP, fulfilled, and one very creative out_the_door. The fix usually comes in two steps:
- Define a domain for status (either a reference table + foreign key, or a strict
CHECKconstraint list). - Plan transitions (what statuses are valid next steps) so application logic and data stay aligned.
The big takeaway: statuses are a domain problem first, and a UI label problem second.
Experience 2: “Age” and “Date of Birth” Are Not the Same Domain
In a health or HR app, someone stores age as an integer. It works… until it doesn’t. Age changes, sometimes in the middle of your reporting windows. People also enter “0” for newborns, “999” as a placeholder, or “-1” because import files are chaotic beings.
Teams often recover by redefining the domain strategy:
- Store date of birth as a date (domain: valid date, reasonable range).
- Compute age when needed (domain: derived value, not stored fact).
- If you must store age, define a domain like
human_age_yearswith a realistic range check.
This change usually improves data quality immediatelyand makes product decisions easier (“Are we showing age at signup or age today?”) because the database no longer pretends those are identical concepts.
Experience 3: The “Stringly Typed” Migration Hangover
Many teams inherit databases where everything is stored as text because “it’s flexible.” Then one day they need to sort numbers correctly (so 100 doesn’t come before 20), compare dates reliably, or validate currency values without accidental commas.
A domain cleanup project often looks like this:
- Profile the data: find the weird values hiding in the corners (there are always corners).
- Define proper domains: types + constraints that match reality.
- Migrate incrementally: add new columns or domain types, backfill, validate, then swap.
- Lock it in: constraints in the database so the problem doesn’t return wearing a different hat.
The punchline: domains aren’t “extra paperwork.” They’re how you keep your database from turning into a junk drawer where every value is technically allowed and nothing is actually useful.
Conclusion
Defining a database domain is about making your data mean somethingand keeping it that way. A domain is the combination of type and rules that describes what values belong in a column. Whether your database supports CREATE DOMAIN, user-defined types, or primarily relies on constraints, the goal is consistent: prevent bad data, reduce repeated validation logic, and make your schema self-explanatory.
When domains are clear, your database becomes a partner in correctness instead of a silent accomplice to chaos. And honestly, in a world where someone will eventually try to store “banana” as an age, that partnership is priceless.