Ubiquitous Language | A common language shared between domain experts and developers, reflected in code and schema | - Type names must use domain terminology, not technical terms
- Names match domain expert vocabulary
- No technical abbreviations in public schema
- Consistent terminology across all types
- Names are meaningful to business users
- Domain concepts clearly expressed
| - All type names use business terminology
- Ensures clarity and maintainability
- Reduces miscommunication between teams
- Supports long-term schema evolution
| type Customer { customerNumber: String! loyaltyLevel: LoyaltyLevel! creditLimit: Money! }
type Order { orderNumber: String! fulfillmentStatus: FulfillmentStatus! shippingMethod: ShippingMethod! }
type CustomerRecord { custId: String! lvl: Int! creditAmt: Float! }
|
Clarity and Readability | Names should be self-documenting and unambiguous | - Names are descriptive and specific
- No ambiguous abbreviations
- Boolean fields clearly indicate true/false meaning (e.g.,
isAvailableForPurchase ) - Collection fields indicate plurality (e.g.,
lineItems ) - Purpose is clear from name alone
| - Improves understanding for all schema users
- Reduces the need for external documentation
- Facilitates easier debugging and maintenance
| type ProductCatalogEntry { productSku: String! displayName: String! shortDescription: String! detailedDescription: String! isAvailableForPurchase: Boolean! }
type Product { id: String! name: String! desc: String! info: String! flag: Boolean! }
|
Consistency | Apply naming patterns consistently across the entire schema | - ID fields follow consistent pattern (e.g.,
customerId , orderId ) - Date fields use consistent suffixes (e.g.,
registrationDate , lastLoginDate ) - Similar concepts use similar naming
- Patterns applied across all types
- Exceptions are documented
| - Ensures predictability and reduces cognitive load
- Streamlines development and integration
- Supports automated tooling and code generation
| type Customer { customerId: ID! customerNumber: String! registrationDate: DateTime! lastLoginDate: DateTime! }
type Order { orderId: ID! orderNumber: String! placementDate: DateTime! lastModifiedDate: DateTime! }
type Customer { id: ID! customerNum: String! registered: DateTime! lastLogin: DateTime! }
|
Entity Types Naming | Use singular nouns in PascalCase for entity types | - Singular noun form
- PascalCase formatting (e.g.,
Customer , Product ) - No prefixes or suffixes unless necessary for compound names
- Compound names clearly joined (e.g.,
CustomerProfile ) - Names reflect domain concepts
| - Clearly identifies core domain entities
- Aligns with common GraphQL type naming conventions
| type Customer @key(fields: "id") { id: ID! customerNumber: String! }
type Product @key(fields: "sku") { sku: String! name: String! }
type CustomerProfile { personalInfo: PersonalInfo! contactInfo: ContactInfo! }
|
Value Object Types Naming | Use descriptive nouns that indicate the value's purpose | - Names indicate value purpose (e.g.,
Money , Address ) - No "Value" or "Object" suffixes
- Descriptive and specific
- Consistent with domain language
- Clear semantic meaning
| - Distinguishes value objects from entities
- Promotes reusability of common data structures
| type Money { amount: Decimal! currency: Currency! }
type Address { street: String! city: String! postalCode: String! country: String! }
type Weight { value: Decimal! unit: WeightUnit! }
|
Enum Types Naming | Use singular nouns with descriptive suffixes when needed | - Enum type names are singular (e.g.,
Currency , OrderStatus ) - Enum values are UPPER_SNAKE_CASE (e.g.,
USD , PENDING_PAYMENT ) - Values are descriptive and clear
- No redundant prefixes in values
- Logical grouping of related values
| - Provides clear, self-documenting lists of discrete values
- Ensures consistent representation of fixed choices
| enum Currency { USD EUR GBP }
enum OrderStatus { DRAFT PENDING_PAYMENT CONFIRMED }
enum PaymentMethod { CREDIT_CARD DEBIT_CARD }
|
Interface Types Naming | Use descriptive names that indicate the common contract | - Names indicate common behavior or contract (e.g.,
Identifiable , Purchasable ) - Use adjective forms when appropriate (e.g.,
Timestamped ) - Clear semantic meaning
- No "Interface" suffix
- Focused on single responsibility
| - Defines shared behaviors and structures across types
- Promotes schema extensibility and polymorphism
| interface DomainEvent { eventId: ID! aggregateId: ID! occurredAt: DateTime! }
interface Identifiable { id: ID! }
interface Purchasable { price: Money! isAvailable: Boolean! }
|
Union Types Naming | Use descriptive names that indicate the union purpose | - Names indicate union purpose (e.g.,
OrderEvent , SearchResult ) - Clear semantic grouping
- No "Union" suffix
- Logical member types
- Consistent with domain concepts
| - Allows for returning multiple possible types for a single field
- Enhances flexibility in representing diverse but related data
| union OrderEvent = OrderPlaced | OrderShipped | OrderDelivered
union SearchResult = Product | Category | Brand
union ValidationError = FieldError | BusinessRuleError
|
Scalar Fields Naming | Use camelCase with descriptive names | - camelCase formatting (e.g.,
firstName , totalOrderCount ) - Descriptive and specific
- Boolean fields start with "is", "has", "can", etc. (e.g.,
isActive ) - Date fields end with "Date" (e.g.,
registrationDate ) - Count fields end with "Count" (e.g.,
totalOrderCount )
| - Provides clear and consistent field access
- Aligns with common JavaScript/GraphQL field naming conventions
| type Customer { firstName: String! lastName: String! emailAddress: String! isActive: Boolean! registrationDate: DateTime! totalOrderCount: Int! }
|
Object Fields Naming | Use camelCase names that indicate the relationship | - Names indicate relationship type (e.g.,
profile , billingAddress ) - Singular for single objects (e.g.,
customer ) - Plural for collections (e.g.,
lineItems ) - Clear ownership vs. reference
- Consistent relationship naming
| - Defines relationships between different types in the schema
- Enhances graph traversability and data retrieval
| type Customer { profile: CustomerProfile! billingAddress: Address! primaryPaymentMethod: PaymentMethod }
type Order { lineItems: [LineItem!]! customer: Customer! paymentMethod: PaymentMethod! }
|
Collection Fields Naming | Use plural nouns that clearly indicate the collection contents | - Plural noun forms (e.g.,
orders , paymentMethods ) - Clear collection contents
- Descriptive qualifiers when filtered (e.g.,
activeOrders , recommendedProducts ) - Consistent collection naming
- Appropriate nullability (e.g.,
[Order!]! )
| - Represents lists of related items
- Facilitates querying multiple entities at once
| type Customer { orders: [Order!]! paymentMethods: [PaymentMethod!]! activeOrders: [Order!]! }
type Product { variants: [ProductVariant!]! categories: [Category!]! images: [ProductImage!]! }
|
Query Operations Naming | Use descriptive verbs and nouns that indicate the query purpose | - Descriptive query names (e.g.,
customer , searchProducts , calculateShipping ) - Consistent parameter naming
- Clear return type indication
- Logical grouping of related queries
- Appropriate nullability
| - Defines entry points for data retrieval
- Ensures clarity on what data can be fetched
| type Query { customer(id: ID!): Customer products(filter: ProductFilter): [Product!]! searchProducts(query: String!): ProductSearchResult! calculateShipping(input: ShippingCalculationInput!): ShippingQuote! }
|
Mutation Operations Naming | Use imperative verbs that clearly describe the action | - Imperative verb forms (e.g.,
createCustomer , updateOrderStatus , cancelOrder ) - Clear action description
- Consistent input/payload pattern (e.g.,
CreateCustomerInput , CreateCustomerPayload ) - Business-focused operation names
- Appropriate error handling
| - Defines operations that modify data
- Provides clear intent for state changes
| type Mutation { createCustomer(input: CreateCustomerInput!): CreateCustomerPayload! updateOrderStatus(input: UpdateOrderStatusInput!): UpdateOrderStatusPayload! cancelOrder(orderId: ID!, reason: String!): CancelOrderPayload! }
|
Subscription Operations Naming | Use descriptive names that indicate the event stream | - Names indicate event streams (e.g.,
customerUpdates , orderEvents ) - Clear subscription purpose
- Appropriate filtering options
- Consistent event naming
- Real-time semantics clear
| - Enables real-time data updates
- Supports event-driven architectures
| type Subscription { customerUpdates(customerId: ID!): CustomerEvent! orderEvents(filter: OrderEventFilter): OrderEvent! priceUpdates(productSkus: [String!]!): PriceUpdate! }
|
Input Types Naming | Use descriptive names with "Input" suffix | - "Input" suffix for all input types (e.g.,
CreateCustomerInput , CustomerFilter ) - Descriptive base names
- Appropriate field nullability
- Logical field grouping
- Consistent with output types
| - Clearly identifies types used for mutation arguments
- Promotes reusability of complex input structures
| input CreateCustomerInput { firstName: String! lastName: String! emailAddress: String! }
input CustomerFilter { isActive: Boolean registeredAfter: DateTime }
|
Payload Types Naming | Use descriptive names with "Payload" suffix | - "Payload" suffix for all payload types (e.g.,
CreateCustomerPayload , UpdateCustomerPayload ) - Include primary result object
- Include error and warning arrays
- Additional metadata when relevant (e.g.,
orderNumber , estimatedDelivery ) - Consistent error handling pattern
| - Standardizes mutation responses
- Provides a consistent way to return data, errors, and warnings
| type CreateCustomerPayload { customer: Customer errors: [Error!]! warnings: [Warning!]! }
type PlaceOrderPayload { order: Order orderNumber: String! confirmationNumber: String! errors: [Error!]! }
|
Subgraph Naming | Use domain-focused names that reflect bounded contexts | - Domain-focused names (e.g.,
customer-management , order-processing ) - Kebab-case formatting
- Clear business capability indication
- No technical implementation details
- Consistent with bounded context names
| - Organizes a federated graph into logical, self-contained services
- Promotes team autonomy and clear ownership
| customer-management product-catalog order-processing inventory-management
|
Deprecation Naming | Use clear deprecation messages with migration guidance | - Clear deprecation reasons provided
- Migration guidance provided
- Version information when relevant
- Alternative field/value indicated
- Consistent deprecation messaging
| - Communicates changes to consumers without breaking existing integrations
- Facilitates a smooth evolution of the schema
| type Customer { email: String @deprecated(reason: "Use profile.contactInfo.emailAddress instead") phone: String @deprecated(reason: "Use profile.contactInfo.phoneNumber instead") }
enum OrderStatus { PENDING @deprecated(reason: "Use PENDING_PAYMENT instead") PENDING_PAYMENT }
|
Versioning Conventions | Use semantic versioning concepts in schema evolution | - Version information tracked
- Breaking changes documented
- Migration paths clear
- Backward compatibility maintained where possible
- Evolution strategy defined
| - Manages schema changes over time
- Ensures stability for consumers while allowing for new features
| type CustomerV2 { id: ID! customerNumber: String! profile: CustomerProfileV2! }
type SchemaInfo { version: String! deprecatedFields: [DeprecatedField!]! }
|