Goaは、ドメインを正確かつ明確にモデル化できる強力な型システムを提供します。 シンプルなプリミティブ型から複雑なネスト構造まで、DSLはデータの関係、制約、 バリデーションルールを自然な方法で表現する手段を提供します。
Goaの型システムの基礎は、プリミティブ型と基本的な型定義から始まります。これらの 構成要素により、シンプルながらも表現力豊かなデータ構造を作成できます。
Goaは、すべてのデータモデリングの基礎となる豊富な組み込みプリミティブ型を提供します:
Boolean // JSONブール値
Int // 符号付き整数
Int32 // 符号付き32ビット整数
Int64 // 符号付き64ビット整数
UInt // 符号なし整数
UInt32 // 符号なし32ビット整数
UInt64 // 符号なし64ビット整数
Float32 // 32ビット浮動小数点数
Float64 // 64ビット浮動小数点数
String // JSON文字列
Bytes // バイナリデータ
Any // 任意のJSON値
Type DSL関数は、構造化されたデータ型を定義する主要な方法です。属性、バリデーション、 ドキュメントをサポートします:
var Person = Type("Person", func() {
Description("人物")
// 基本的な属性
Attribute("name", String)
// バリデーション付きの属性
Attribute("age", Int32, func() {
Minimum(0)
Maximum(120)
})
// 必須フィールド
Required("name", "age")
})
実世界のドメインをモデル化する際には、より洗練されたデータ構造が必要になることが よくあります。Goaはコレクションとネスト型に対する包括的なサポートを提供します。
配列を使用すると、任意の型の順序付きコレクションをオプションのバリデーションルール 付きで定義できます:
var Names = ArrayOf(String, func() {
// 配列要素のバリデーション
MinLength(1)
MaxLength(10)
})
var Team = Type("Team", func() {
Attribute("members", ArrayOf(Person))
})
マップは、キーと値の両方に対する型安全性とバリデーションを備えたキーと値の関連付けを 提供します:
var Config = MapOf(String, Int32, func() {
// キーのバリデーション
Key(func() {
Pattern("^[a-z]+$")
})
// 値のバリデーション
Elem(func() {
Minimum(0)
})
})
Goaは、コードの再利用と関心事の明確な分離を可能にする洗練された型の合成パターンを サポートします。
Referenceを使用すると、別の型から属性のデフォルトプロパティを設定できます。現在の型の 属性が参照される型の属性と同じ名前を持つ場合、参照される属性のプロパティを継承します。 複数の参照を指定でき、プロパティは出現順に検索されます:
var Employee = Type("Employee", func() {
// Personから属性定義を再利用
Reference(Person)
Attribute("name") // name属性を再定義する必要なし
Attribute("age") // age属性を再定義する必要なし
// 新しい属性を追加
Attribute("employeeID", String, func() {
Format(FormatUUID)
})
})
Extend
は既存の型に基づいて新しい型を作成し、階層的な関係のモデル化に最適です。
Reference
とは異なり、Extend
は自動的にベース型からすべての属性を継承します。
var Manager = Type("Manager", func() {
// ベース型を拡張
Extend(Employee)
// マネージャー固有のフィールドを追加
Attribute("reports", ArrayOf(Employee))
})
Goaは、データの整合性を確保しビジネスルールを強制するための包括的なバリデーション 機能を提供します。以下がGoaで利用可能な主要なバリデーションルールです:
Pattern(regex)
- 正規表現に対するバリデーションMinLength(n)
- 最小文字列長MaxLength(n)
- 最大文字列長Format(format)
- 事前定義されたフォーマット(メール、URI等)に対するバリデーションMinimum(n)
- 最小値(含む)Maximum(n)
- 最大値(含む)ExclusiveMinimum(n)
- 最小値(含まない)ExclusiveMaximum(n)
- 最大値(含まない)MinLength(n)
- 最小要素数MaxLength(n)
- 最大要素数Required("field1", "field2")
- 必須フィールドEnum(value1, value2)
- 列挙された値に制限さらに、配列とマップの要素は、属性と同じルールを使用してバリデーションできます。
バリデーションルールを組み合わせて、包括的なバリデーションロジックを作成できます:
var UserProfile = Type("UserProfile", func() {
Attribute("username", String, func() {
Pattern("^[a-z0-9]+$") // 正規表現パターン
MinLength(3) // 最小文字列長
MaxLength(50) // 最大文字列長
})
Attribute("email", String, func() {
Format(FormatEmail) // 組み込みフォーマット
})
Attribute("age", Int32, func() {
Minimum(18) // 最小値
ExclusiveMaximum(150) // 最大値(含まない)
})
Attribute("tags", ArrayOf(String, func() { Enum("tag1", "tag2", "tag3") }), func() {
// 配列要素の列挙値
MinLength(1) // 最小配列長
MaxLength(10) // 最大配列長
})
Attribute("settings", MapOf(String, String), func() {
MaxLength(20) // 最大マップ長
})
Required("username", "email", "age") // 必須フィールド
})
ドメイン固有のフォーマットとバリデーションルールをカプセル化する再利用可能な カスタム型を作成します:
// カスタムフォーマットを定義
var UUID = Type("UUID", String, func() {
Format(FormatUUID)
Description("RFC 4122 UUID")
})
// カスタム型を使用
var Resource = Type("Resource", func() {
Attribute("id", UUID)
Attribute("name", String)
})
詳細についてはType DSLを参照してください。
Goaには、一般的なデータパターン用の包括的な事前定義フォーマットが含まれています。 これらのフォーマットは自動バリデーションと明確な意味を提供します:
FormatDate
- RFC3339日付値FormatDateTime
- RFC3339日時値FormatUUID
- RFC4122 UUID値FormatEmail
- RFC5322メールアドレスFormatHostname
- RFC1035インターネットホスト名FormatIPv4
- RFC2373 IPv4アドレス値FormatIPv6
- RFC2373 IPv6アドレス値FormatIP
- RFC2373 IPv4またはIPv6アドレス値FormatURI
- RFC3986 URI値FormatMAC
- IEEE 802 MAC-48、EUI-48またはEUI-64 MACアドレス値FormatCIDR
- RFC4632およびRFC4291 CIDR表記IPアドレス値FormatRegexp
- RE2で受け入れられる正規表現構文FormatJSON
- JSONテキストFormatRFC1123
- RFC1123日時値Goaは型の属性を定義する2つの同等の方法を提供します:Attribute
とField
です。
主な違いは、Field
がgRPCメッセージのフィールド番号に使用される追加のタグパラメータを
取ることです。
gRPCサポートが不要な場合や、gRPCメッセージで使用されない型を定義する場合に使用します:
var Person = Type("Person", func() {
Attribute("name", String)
Attribute("age", Int32)
})
gRPCメッセージで使用される型を定義する場合に使用します。最初の引数はフィールド番号 タグです:
var Person = Type("Person", func() {
Field(1, "name", String)
Field(2, "age", Int32)
})