Goa fornisce un potente sistema di tipi che ti permette di modellare il tuo dominio con precisione e chiarezza. Dai primitivi semplici alle strutture nidificate complesse, il DSL offre un modo naturale per esprimere relazioni tra dati, vincoli e regole di validazione.
La fondazione del sistema di tipi di Goa inizia con i tipi primitivi e le definizioni di tipo base. Questi blocchi di costruzione ti permettono di creare strutture dati semplici ma espressive.
Goa fornisce un ricco set di tipi primitivi integrati che servono come fondamento per tutta la modellazione dei dati:
Boolean // booleano JSON
Int // intero con segno
Int32 // intero con segno a 32 bit
Int64 // intero con segno a 64 bit
UInt // intero senza segno
UInt32 // intero senza segno a 32 bit
UInt64 // intero senza segno a 64 bit
Float32 // numero in virgola mobile a 32 bit
Float64 // numero in virgola mobile a 64 bit
String // stringa JSON
Bytes // dati binari
Any // valore JSON arbitrario
La funzione DSL Type è il modo principale per definire tipi di dati strutturati. Supporta attributi, validazioni e documentazione:
var Person = Type("Person", func() {
Description("Una persona")
// Attributo base
Attribute("name", String)
// Attributo con validazione
Attribute("age", Int32, func() {
Minimum(0)
Maximum(120)
})
// Campi obbligatori
Required("name", "age")
})
Quando si modellano domini del mondo reale, spesso si necessita di strutture dati più sofisticate. Goa fornisce un supporto completo per collezioni e tipi nidificati.
Gli array ti permettono di definire collezioni ordinate di qualsiasi tipo, con regole di validazione opzionali:
var Names = ArrayOf(String, func() {
// Valida elementi dell'array
MinLength(1)
MaxLength(10)
})
var Team = Type("Team", func() {
Attribute("members", ArrayOf(Person))
})
Le mappe forniscono associazioni chiave-valore con type safety e validazione sia per le chiavi che per i valori:
var Config = MapOf(String, Int32, func() {
// Validazione chiave
Key(func() {
Pattern("^[a-z]+$")
})
// Validazione valore
Elem(func() {
Minimum(0)
})
})
Goa supporta pattern sofisticati di composizione dei tipi che abilitano il riuso del codice e una chiara separazione delle responsabilità.
Usa Reference per impostare proprietà predefinite per gli attributi da un altro tipo. Quando un attributo nel tipo corrente ha lo stesso nome di uno nel tipo referenziato, eredita le proprietà dell’attributo referenziato. Possono essere specificate multiple reference, con le proprietà cercate nell’ordine di apparizione:
var Employee = Type("Employee", func() {
// Riusa definizioni di attributi da Person
Reference(Person)
Attribute("name") // Non serve definire l'attributo name di nuovo
Attribute("age") // Non serve definire l'attributo age di nuovo
// Aggiungi nuovi attributi
Attribute("employeeID", String, func() {
Format(FormatUUID)
})
})
Extend
crea un nuovo tipo basato su uno esistente, perfetto per modellare
relazioni gerarchiche. A differenza di Reference
, Extend
eredita automaticamente
tutti gli attributi dal tipo base.
var Manager = Type("Manager", func() {
// Estendi tipo base
Extend(Employee)
// Aggiungi campi specifici del manager
Attribute("reports", ArrayOf(Employee))
})
Goa fornisce capacità di validazione complete per assicurare l’integrità dei dati e applicare regole di business: Ecco le regole di validazione chiave disponibili in Goa:
Pattern(regex)
- Valida contro un’espressione regolareMinLength(n)
- Lunghezza minima stringaMaxLength(n)
- Lunghezza massima stringaFormat(format)
- Valida contro formati predefiniti (email, URI, ecc)Minimum(n)
- Valore minimo (inclusivo)Maximum(n)
- Valore massimo (inclusivo)ExclusiveMinimum(n)
- Valore minimo (esclusivo)ExclusiveMaximum(n)
- Valore massimo (esclusivo)MinLength(n)
- Numero minimo di elementiMaxLength(n)
- Numero massimo di elementiRequired("field1", "field2")
- Campi obbligatoriEnum(value1, value2)
- Restringe a valori enumeratiInoltre gli elementi di array e mappe possono essere validati usando le stesse regole degli attributi.
Le regole di validazione possono essere combinate per creare logica di validazione completa:
var UserProfile = Type("UserProfile", func() {
Attribute("username", String, func() {
Pattern("^[a-z0-9]+$") // Pattern regex
MinLength(3) // Lunghezza minima stringa
MaxLength(50) // Lunghezza massima stringa
})
Attribute("email", String, func() {
Format(FormatEmail) // Formato integrato
})
Attribute("age", Int32, func() {
Minimum(18) // Valore minimo
ExclusiveMaximum(150) // Valore massimo esclusivo
})
Attribute("tags", ArrayOf(String, func() { Enum("tag1", "tag2", "tag3") }), func() {
// Valori enum per elementi array
MinLength(1) // Lunghezza minima array
MaxLength(10) // Lunghezza massima array
})
Attribute("settings", MapOf(String, String), func() {
MaxLength(20) // Lunghezza massima mappa
})
Required("username", "email", "age") // Campi obbligatori
})
Crea tipi personalizzati riutilizzabili per incapsulare formati specifici del dominio e regole di validazione:
// Definisci formato personalizzato
var UUID = Type("UUID", String, func() {
Format(FormatUUID)
Description("UUID RFC 4122")
})
// Usa tipo personalizzato
var Resource = Type("Resource", func() {
Attribute("id", UUID)
Attribute("name", String)
})
Vedi il DSL Type per maggiori dettagli.
Goa include un set completo di formati predefiniti per pattern di dati comuni. Questi formati forniscono validazione automatica e chiaro significato semantico:
FormatDate
- valori data RFC3339FormatDateTime
- valori data e ora RFC3339FormatUUID
- valori UUID RFC4122FormatEmail
- indirizzi email RFC5322FormatHostname
- nomi host Internet RFC1035FormatIPv4
- valori indirizzo IPv4 RFC2373FormatIPv6
- valori indirizzo IPv6 RFC2373FormatIP
- valori indirizzo IPv4 o IPv6 RFC2373FormatURI
- valori URI RFC3986FormatMAC
- valori indirizzo MAC IEEE 802 MAC-48, EUI-48 o EUI-64FormatCIDR
- valori indirizzo IP in notazione CIDR RFC4632 e RFC4291FormatRegexp
- sintassi espressione regolare accettata da RE2FormatJSON
- testo JSONFormatRFC1123
- valori data e ora RFC1123Goa fornisce due modi equivalenti per definire attributi di tipo: Attribute
e Field
. La differenza principale è che Field
prende un parametro tag aggiuntivo che viene usato per i numeri di campo dei messaggi gRPC.
Usato quando non hai bisogno del supporto gRPC o quando definisci tipi che non saranno usati nei messaggi gRPC:
var Person = Type("Person", func() {
Attribute("name", String)
Attribute("age", Int32)
})
Usato quando si definiscono tipi che saranno usati nei messaggi gRPC. Il primo argomento è il tag numero di campo:
var Person = Type("Person", func() {
Field(1, "name", String)
Field(2, "age", Int32)
})