| 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!]! }
|