This guide shows how we use Domain-Driven Design (DDD) to build a clean, maintainable GraphQL server architecture with visual diagrams and practical examples.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GraphQL Request Flow β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 1. HTTP Request β Presentation Layer (GraphQL endpoint) β
β 2. Parse & Validate β Application Layer (Use cases) β
β 3. Execute Query β Domain Layer (Business logic) β
β 4. Fetch Data β Infrastructure Layer (Resolvers, DB) β
β 5. Return Response β All layers collaborate β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββ HTTP requests, GraphQL over HTTP
β Presentation Layer β β Controllers, GraphQL endpoints
βββββββββββββββββββββββ€
β Application Layer β β Use cases, orchestration
βββββββββββββββββββββββ€
β Domain Layer β β Business logic, entities, rules
βββββββββββββββββββββββ€
β Infrastructure β β Data access, external services
βββββββββββββββββββββββ
Query: { user(id: "123") { name, posts { title } } }
1. Presentation Layer
βββββββββββββββββββ
β GraphQL Handler β β Receives HTTP POST with query
βββββββββββββββββββ
β
2. Application Layer
βββββββββββββββββββ
β ExecuteQuery β β Parses, validates, orchestrates
β UseCase β
βββββββββββββββββββ
β
3. Domain Layer
βββββββββββββββββββ
β QueryExecutor β β Applies business rules, validation
β SchemaValidator β
βββββββββββββββββββ
β
4. Infrastructure Layer
βββββββββββββββββββ
β UserResolver β β Fetches data from database
β PostResolver β
βββββββββββββββββββ
DDD focuses on modeling the business domain and organizing code around domain concepts rather than technical concerns. This approach helps create more maintainable and understandable software.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GraphQL Domain Model β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββ contains βββββββββββββββ β
β β Schema β ββββββββββββββββββΆ β TypeDef β β
β β (Entity) β β (Value Obj) β β
β βββββββββββββββ βββββββββββββββ β
β β β β
β β validates β describes β
β βΌ βΌ β
β βββββββββββββββ executes βββββββββββββββ β
β β Query β ββββββββββββββββββΆ β Selection β β
β β (Entity) β β (Value Obj) β β
β βββββββββββββββ βββββββββββββββ β
β β β
β β managed by β
β βΌ β
β βββββββββββββββ β
β βSchema β β Aggregate Root β
β βAggregate β (Consistency boundary) β
β βββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
1. Client Query Request
βββββββββββββββ
β Query β β "{ user { name } }"
β (Entity) β
βββββββββββββββ
β passed to
βΌ
2. Schema Validation
βββββββββββββββ
β Schema β β Has TypeDefinitions, validates query
β (Entity) β
βββββββββββββββ
β if valid
βΌ
3. Execution Planning
βββββββββββββββ
β Selection β β Breaks query into field selections
β(Value Obj) β
βββββββββββββββ
β executed by
βΌ
4. Aggregate Coordination
βββββββββββββββ
βSchema β β Orchestrates validation + execution
βAggregate β
βββββββββββββββ
Our primary bounded context encompasses the GraphQL specification implementation.
pub struct Schema {
id: SchemaId,
version: SchemaVersion,
types: HashMap<TypeName, TypeDefinition>,
query_type: ObjectType,
mutation_type: Option<ObjectType>,
subscription_type: Option<ObjectType>,
}
pub struct Query {
id: QueryId,
document: Document,
operation_name: Option<String>,
variables: Variables,
validation_result: ValidationResult,
}
Represents GraphQL type information without identity:
#[derive(Clone, PartialEq)]
pub enum TypeDefinition {
Scalar(ScalarType),
Object(ObjectType),
Interface(InterfaceType),
Union(UnionType),
Enum(EnumType),
InputObject(InputObjectType),
}
Represents a field within an object type:
#[derive(Clone, PartialEq)]
pub struct Field {
name: FieldName,
type_ref: TypeReference,
arguments: Vec<Argument>,
description: Option<String>,
deprecation_reason: Option<String>,
}
Represents field selections in a query:
#[derive(Clone, PartialEq)]
pub enum Selection {
Field {
name: String,
alias: Option<String>,
arguments: Vec<(String, Value)>,
selection_set: Vec<Selection>,
},
InlineFragment {
type_condition: Option<String>,
selection_set: Vec<Selection>,
},
FragmentSpread {
name: String,
},
}
Manages the complete GraphQL schema with consistency boundaries:
pub struct SchemaAggregate {
schema: Schema,
resolver_registry: ResolverRegistry,
validation_rules: Vec<ValidationRule>,
}
impl SchemaAggregate {
pub fn validate_query(&self, query: &Query) -> ValidationResult {
// Validate query against schema
}
pub fn execute_query(&self, query: Query) -> ExecutionResult {
// Execute validated query
}
}
Validates schema definitions for correctness:
pub struct SchemaValidator;
impl SchemaValidator {
pub fn validate(&self, schema: &Schema) -> SchemaValidationResult {
// Validate schema structure and rules
}
}
Orchestrates query execution:
pub struct QueryExecutor {
schema: Arc<Schema>,
resolver_registry: Arc<ResolverRegistry>,
}
impl QueryExecutor {
pub async fn execute(&self, query: Query) -> ExecutionResult {
// Execute query with proper error handling
}
}
Validates queries against schema:
pub struct QueryValidator {
schema: Arc<Schema>,
rules: Vec<ValidationRule>,
}
impl QueryValidator {
pub fn validate(&self, query: &Query) -> ValidationResult {
// Apply all validation rules
}
}
Manages schema persistence and retrieval:
pub trait SchemaRepository {
async fn save(&self, schema: Schema) -> Result<(), SchemaError>;
async fn find_by_id(&self, id: SchemaId) -> Result<Option<Schema>, SchemaError>;
async fn find_latest(&self) -> Result<Option<Schema>, SchemaError>;
}
Manages field resolution and data fetching:
pub struct ResolverContext {
pub data_loaders: HashMap<String, Box<dyn DataLoader>>,
pub user_context: Option<UserContext>,
pub request_context: RequestContext,
}
Handles error reporting and formatting:
pub struct GraphQLError {
message: String,
locations: Vec<SourceLocation>,
path: Option<Vec<PathSegment>>,
extensions: Option<ErrorExtensions>,
}
domain/
βββ entities/
β βββ schema.rs
β βββ query.rs
β βββ mod.rs
βββ value_objects/
β βββ types.rs
β βββ selections.rs
β βββ mod.rs
βββ services/
β βββ validator.rs
β βββ executor.rs
β βββ mod.rs
βββ repositories/
βββ schema_repository.rs
βββ mod.rs
application/
βββ use_cases/
β βββ execute_query.rs
β βββ validate_schema.rs
β βββ mod.rs
βββ services/
β βββ schema_service.rs
β βββ query_service.rs
β βββ mod.rs
βββ dto/
βββ query_request.rs
βββ execution_result.rs
βββ mod.rs
infrastructure/
βββ http/
β βββ handlers/
β βββ middleware/
β βββ mod.rs
βββ persistence/
β βββ memory/
β βββ redis/
β βββ mod.rs
βββ resolvers/
βββ user_resolver.rs
βββ post_resolver.rs
βββ mod.rs
presentation/
βββ graphql/
β βββ schema.rs
β βββ resolvers.rs
β βββ mod.rs
βββ rest/
β βββ health.rs
β βββ mod.rs
βββ websocket/
βββ subscriptions.rs
βββ mod.rs
GraphQL operations can trigger domain events for cross-cutting concerns:
pub enum GraphQLEvent {
QueryExecuted {
query_id: QueryId,
execution_time: Duration,
field_count: usize,
},
SchemaUpdated {
schema_id: SchemaId,
version: SchemaVersion,
},
ValidationFailed {
query_id: QueryId,
errors: Vec<ValidationError>,
},
}
Abstract data access behind domain interfaces.
Separate read (Query) and write (Mutation) operations.
Encapsulate complex validation rules.
Handle domain events and side effects.
This domain model provides a solid foundation for implementing GraphQL server features while maintaining clean separation of concerns and domain-focused design.