Quando dici "non tutti i database supportano questo", penso che un modo migliore per dirlo sia il seguente:
Tutti i principali database lo supportano, poiché supportano ampiamente trigger, funzioni e altre funzionalità avanzate.
Questo ci porta alla conclusione che questo fa parte di SQL avanzato e ha senso ad un certo punto.
Do people actually use domains in their database designs?
Meno possibile, a causa dell'ampia copertura richiesta (considerando operatori, indici, ecc.)
If so to what extent?
Ancora una volta, il più limitato possibile, se un tipo esistente combinato con una piccola logica definita aggiuntiva (es. Controlli ecc.) Può fare il trucco, perché andare così lontano?
How useful are they?
Un sacco. Consideriamo per un secondo un DBMS non così buono come MySQL, che ho scelto per questo esempio per un motivo: manca un buon supporto per il tipo inet (indirizzo IP).
Ora vuoi scrivere un'applicazione che si concentri principalmente su dati IP come intervalli e tutto il resto, e sei bloccato con il tipo predefinito e la sua funzionalità limitata, o scriverai funzioni e operatori aggiuntivi (come quelli supportati nativamente in postgreSQL per esempio) o scrivere query molto più complesse per ogni funzionalità di cui hai bisogno.
Questo è un caso in cui giustificherai facilmente il tempo impiegato per definire le tue funzioni (inet >> inet in PostgreSQL: range contenuto in range operator).
A quel punto, hai già giustificato l'estensione del supporto per il tipo di dati, c'è solo un altro passo per definire un nuovo tipo di dati.
Ora torniamo a PostgreSQL che ha un supporto di tipo davvero carino ma nessun int .. senza segno di cui hai bisogno, perché sei davvero preoccupato per l'archiviazione / le prestazioni (chissà ...), bene dovrai aggiungerlo così come il operatori - anche se questo ovviamente deriva principalmente dagli operatori int esistenti.
What pitfalls have you encountered?
Finora non ci ho pensato, non ho avuto un progetto che richiedesse e giustificasse il tempo necessario per questo.
I maggiori problemi che posso vedere che potrebbero essere reinventare la ruota, introducendo bug nel livello "sicuro" (db), supporto di tipo incompleto che realizzerai solo mesi dopo quando il CONCAT (cast * AS varchar) fallisce perché tu non ha definito un cast (newtype come varchar), ecc.
Ci sono risposte che parlano di "non comune" ecc. Sicuramente queste sono e dovrebbero essere non comuni (altrimenti significa che i dbms mancano di molti tipi importanti), ma d'altra parte, si dovrebbe ricordare che un (buono) db è compatibile ACID ( a differenza di un'applicazione) e che tutto ciò che riguarda la coerenza è meglio tenuto lì.
Esistono molti casi in cui la logica aziendale viene gestita nel livello software e può essere eseguita in SQL, dove è più sicura. Gli sviluppatori di app tendono a sentirsi più a loro agio nel livello dell'applicazione e spesso evitano soluzioni migliori implementate in SQL, questo non dovrebbe essere considerato una buona pratica.
Gli UDT possono essere una buona soluzione per l'ottimizzazione, un buon esempio è dato in un'altra risposta sul tipo m / f usando char (1). Se fosse stato un UDT potrebbe invece essere un valore booleano (a meno che non vogliamo offrire la terza e la quarta opzione). Ovviamente sappiamo tutti che questa non è davvero ottimizzazione a causa del sovraccarico della colonna, ma la possibilità è lì.