Skip to main content

Advanced

Domain-Driven Design (DDD)

DDD ConceptDefinitionCharacteristicsConsiderationsExample
Ubiquitous LanguageA common language shared between domain experts and developers, reflected in code and schema
  • Type names must use domain terminology, not technical terms
  • Field names should match business vocabulary exactly
  • Descriptions should use domain language
  • All type names use business terminology
  • Field names match domain expert vocabulary
  • Schema descriptions use ubiquitous language
  • No technical jargon in public schema
  • Consistent terminology across all types
# ✅ Good - Uses domain language
type Order {
orderNumber: String!
customer: Customer!
lineItems: [LineItem!]!
totalAmount: Money!
status: OrderStatus!
}

# ❌ Bad - Uses technical language
type OrderRecord {
id: ID!
customerId: ID!
items: [OrderItemRecord!]!
total: Float!
statusCode: Int!
}
Bounded ContextsExplicit boundaries within which a domain model is defined and applicable
  • Each subgraph represents one bounded context
  • Types within a context have consistent meaning
  • Cross-context references use federation
  • Each subgraph maps to exactly one bounded context
  • No overlapping responsibilities between subgraphs
  • Clear ownership boundaries defined
  • Context boundaries align with team boundaries
  • Minimal coupling between contexts

Consider separate bounded contexts when applicable:

  • Does this concept have different meanings in different parts of the business?
  • Do different teams own different aspects of this concept?
  • Would changes to this concept affect different business capabilities?
EntitiesObjects with distinct identity that persists over time
  • Always have an id field
  • Identity is stable across operations
  • Can be referenced from other contexts
  • Support federation @key directive
  • All entities have stable identity fields
  • Identity fields are immutable
  • Entities can be federated across subgraphs
  • Entity boundaries respect aggregate boundaries
  • Identity generation strategy is consistent
type Customer @key(fields: "id") {
id: ID!
customerNumber: String!
profile: CustomerProfile!
# Entity-specific behavior
orders: [Order!]!
}
Value ObjectsObjects that describe characteristics but have no conceptual identity
  • No id field
  • Immutable by design
  • Compared by value, not identity
  • Often embedded within entities
  • Value objects have no identity fields
  • All fields are non-nullable where appropriate
  • Value objects are immutable
  • Validation rules are embedded in type design
  • Consistent representation across contexts
type Money {
amount: Decimal!
currency: Currency!
}

type Address {
street: String!
city: String!
postalCode: String!
country: Country!
}
AggregatesClusters of entities and value objects that are treated as a single unit for data changes
  • Root entity serves as aggregate root
  • Internal consistency maintained within aggregate
  • External references only to aggregate root
  • Mutations operate on entire aggregate
  • Aggregate root is clearly identified
  • Internal entities not exposed as top-level types
  • Mutations target aggregate roots only
  • Aggregate boundaries respect business invariants
  • Cross-aggregate references use IDs only
# Aggregate Root
type Order @key(fields: "id") {
id: ID!
orderNumber: String!
customer: Customer! # External reference
lineItems: [LineItem!]! # Internal entities
shippingAddress: Address! # Value object
totalAmount: Money! # Value object
status: OrderStatus!
}

# Internal Entity (not directly accessible)
type LineItem {
productId: ID!
quantity: Int!
unitPrice: Money!
lineTotal: Money!
}
Domain ServicesOperations that don't naturally belong to entities or value objects
  • Implemented as mutations or queries
  • Operate across multiple aggregates
  • Encapsulate complex business logic
  • Domain services have clear business purpose
  • Services operate on domain concepts, not data structures
  • Complex business rules encapsulated in services
  • Services maintain aggregate boundaries
  • Input/output types use domain language
type Mutation {
# Domain Service: Order Processing
processOrder(input: ProcessOrderInput!): ProcessOrderPayload!

# Domain Service: Inventory Allocation
allocateInventory(input: AllocateInventoryInput!): AllocationResult!
}

type Query {
# Domain Service: Pricing Calculation
calculateShipping(input: ShippingCalculationInput!): ShippingQuote!
}
Domain EventsSomething that happened in the domain that domain experts care about
  • Modeled as types for event sourcing
  • Used in subscriptions for real-time updates
  • Enable loose coupling between contexts
  • Events represent business-significant occurrences
  • Event names use past tense
  • Events are immutable
  • Events include necessary context data
  • Event versioning strategy defined
type OrderPlaced implements DomainEvent {
eventId: ID!
aggregateId: ID!
occurredAt: DateTime!
version: Int!

# Event-specific data
orderNumber: String!
customerId: ID!
totalAmount: Money!
}

type Subscription {
orderEvents(customerId: ID): OrderEvent!
}

union OrderEvent = OrderPlaced | OrderShipped | OrderCancelled