Domain Services
Reading time: ~12 minutes
Introduction​
Domain Services are a critical component of Prism Architecture, encapsulating business logic that doesn't naturally belong to any single entity. They play a vital role in implementing business rules, validations, and processes that operate on multiple entities or require specialized logic. This document explores what Domain Services are, how they fit into Prism Architecture, and how they differ from other types of services in the system.
Understanding Domain Services is essential for implementing a rich, well-structured domain model that accurately represents business concepts and rules. By placing business logic in the appropriate Domain Services, you create a more maintainable, testable, and expressive architecture that clearly communicates the intent of your business domain.
What are Domain Services?​
Definition and Purpose​
Domain Services are objects that implement domain logic that doesn't naturally fit within a specific entity or value object. They represent important processes or transformations in the domain and are part of the Domain Layer in Prism Architecture.
Their primary purposes include:
- Implementing Business Processes: Representing domain operations that involve multiple entities
- Enforcing Domain Rules: Implementing complex business rules that span entity boundaries
- Coordinating Entity Interactions: Managing interactions between different domain objects
- Providing Domain Abstraction: Creating higher-level abstractions of domain concepts
Unlike entities, Domain Services are defined primarily by what they do rather than by what they are. They represent behaviors or operations rather than things in the domain model.
Key Characteristics​
Domain Services are characterized by:
- Statelessness: They typically don't maintain state between operations
- Domain Focus: They express domain concepts and terminology
- Pure Business Logic: They contain only business logic, no infrastructure or application concerns
- Named After Domain Activities: They use names that reflect domain processes (e.g.,
PaymentProcessor
,TaxCalculator
) - Domain Object Parameters: They operate on domain entities and value objects
- Explicit Interfaces: They define clear, meaningful interfaces that express domain operations
Types of Domain Services​
Domain Services can be categorized into several types based on their primary responsibility. Understanding these types helps in identifying and designing appropriate services for your domain.
Process Services​
Process Services implement important business processes in the domain:
- Purpose: Represent significant business workflows or transformations
- Characteristics: Often involve multiple entities or steps
- Example Services:
OrderProcessor
,PaymentService
,ReservationManager
- Example Operations:
processOrder()
,makePayment()
,createReservation()
Calculation Services​
Calculation Services implement complex business calculations:
- Purpose: Perform domain-specific calculations that don't belong to a single entity
- Characteristics: Often implement business algorithms or formulas
- Example Services:
PricingCalculator
,TaxService
,InterestCalculator
- Example Operations:
calculatePrice()
,determineTaxes()
,computeInterest()
Validation Services​
Validation Services implement complex domain validation logic:
- Purpose: Enforce business rules across multiple entities
- Characteristics: Typically return validation results rather than throwing exceptions
- Example Services:
OrderValidator
,CreditApplicationEvaluator
,ComplianceChecker
- Example Operations:
validateOrder()
,evaluateCreditApplication()
,checkCompliance()
Coordination Services​
Coordination Services manage interactions between multiple domain objects:
- Purpose: Coordinate complex interactions between entities
- Characteristics: Often maintain the integrity of relationships
- Example Services:
InventoryAllocator
,ScheduleCoordinator
,AccountReconciler
- Example Operations:
allocateInventory()
,scheduleAppointment()
,reconcileAccounts()
Domain Event Services​
Domain Event Services manage the creation and processing of domain events:
- Purpose: Create, enrich, or process domain events
- Characteristics: Often work with the domain event system
- Example Services:
OrderEventService
,UserActivityTracker
,NotificationService
- Example Operations:
recordOrderPlaced()
,trackUserAction()
,createNotificationEvents()
Domain Services in Prism Architecture​
Location in the Architecture​
Domain Services reside in the Domain Layer of Prism Architecture. Their placement has several implications:
- Pure Domain Focus: They contain only domain logic, no infrastructure or application concerns
- Core Layer Dependence: They depend only on entities and value objects from the Core Layer
- No Outward Dependencies: They don't depend on the Orchestration or Infrastructure layers
- Accessibility: They are used by the Orchestration Layer to implement use cases
This placement ensures that Domain Services maintain a pure focus on business rules without being contaminated by technical concerns.
Relationship to Other Components​
Domain Services interact with other components in specific ways:
- Entities and Value Objects: Domain Services operate on entities and value objects from the Core Layer
- Repositories (Interfaces): Domain Services may use repository interfaces defined in the Core Layer
- Use Cases: Use Cases in the Orchestration Layer call Domain Services to implement business logic
- Domain Events: Domain Services may generate domain events to signal important changes
Domain Services should never directly access infrastructure components like databases, APIs, or file systems. Any external dependencies should be accessed through interfaces defined in the Core Layer and implemented in the Infrastructure Layer.
Domain Services vs. Other Service Types​
Understanding how Domain Services differ from other types of services helps in maintaining proper separation of concerns.
Domain Services vs. Application Services​
Application Services (like Use Cases in Prism Architecture) differ from Domain Services:
Domain Services | Application Services (Use Cases) |
---|---|
Focus on business rules and logic | Focus on orchestration and coordination |
Pure domain concepts | May involve technical concerns |
No infrastructure dependencies | May use infrastructure components |
Not concerned with use case flow | Manage application flow and user interaction |
Used by application services | Use domain services |
Domain Services vs. Infrastructure Services​
Infrastructure Services provide technical capabilities and differ significantly from Domain Services:
Domain Services | Infrastructure Services |
---|---|
Implement business logic | Implement technical capabilities |
Domain terminology | Technical terminology |
No external dependencies | Integrate with external systems |
No knowledge of persistence | Often handle persistence details |
Used by application layer | Used by multiple layers |
Domain Services vs. Domain Entities​
While both Domain Services and Entities reside in the domain model, they have different characteristics:
Domain Services | Domain Entities |
---|---|
Represent processes or operations | Represent domain objects or concepts |
Typically stateless | Have identity and state |
Named after activities or processes | Named after things or nouns |
Focus on behavior | Focus on both data and behavior |
Operations on multiple entities | Operations on their own state |
Designing Effective Domain Services​
Identification Strategies​
To identify when to create a Domain Service, look for these indicators:
- Multi-Entity Operations: Logic that spans multiple entities
- Processes, Not Things: Activities that don't naturally belong to an entity
- Stateless Operations: Logic that doesn't require maintaining state between calls
- Domain Expert Language: When domain experts talk about processes rather than things
- Complex Rules: Business rules that apply across entity boundaries
Design Principles​
Follow these principles when designing Domain Services:
- Domain-Driven Names: Use names that reflect domain activities and processes
- Verb-Based Interfaces: Define methods that express actions in the domain
- Explicit Parameters: Make all dependencies explicit as method parameters
- Return Values Over Side Effects: Prefer returning results rather than modifying state
- Immutability: Use immutable parameters and return immutable results when possible
- Domain Error Types: Use domain-specific error types rather than technical exceptions
Interface Design​
Well-designed Domain Service interfaces:
- Express Domain Intent: Clearly communicate what the service does in domain terms
- Hide Implementation Details: Abstract away complex implementation details
- Use Domain Types: Accept and return domain objects rather than primitives
- Group Related Operations: Keep related operations together in the same service
- Follow Cohesion Principles: Ensure that all operations in a service relate to the same domain concept
Implementation Patterns​
Common patterns for implementing Domain Services include:
- Strategy Pattern: For interchangeable business rules or calculations
- Specification Pattern: For complex, composable validation rules
- Decorator Pattern: For adding cross-cutting concerns to services
- Chain of Responsibility: For sequential processing steps
- Visitor Pattern: For operations across complex object structures
Testing Domain Services​
Testing Domain Services is typically straightforward due to their focus on pure business logic:
Unit Testing​
Domain Services are highly amenable to unit testing:
- Pure Logic Testing: Test business rules and calculations directly
- No Mocking Required: Often require minimal or no mocking
- Parameterized Tests: Use parameterized tests for different scenarios
- Boundary Testing: Test boundary conditions and edge cases
- Expected vs. Actual: Compare expected outputs with actual results
Behavior Testing​
For more complex Domain Services, behavior testing may be appropriate:
- Given-When-Then: Use behavior-driven testing approaches
- Business Scenario Testing: Test complete business scenarios
- Domain Event Verification: Verify that appropriate domain events are generated
- State Change Verification: Verify that entity state changes as expected
Common Pitfalls and Anti-Patterns​
Domain Service Anti-Patterns​
Avoid these common anti-patterns when designing Domain Services:
- Anemic Services: Services that are merely data carriers with no behavior
- God Services: Services that try to handle too many responsibilities
- Infrastructure Leakage: Including infrastructure concerns in Domain Services
- Procedural Services: Services that implement procedural logic rather than domain concepts
- Entity Behavior in Services: Putting entity behavior in services rather than in entities
- Technical Services: Services focused on technical rather than domain concerns
Signs of Problematic Domain Services​
Watch for these warning signs that may indicate problems:
- Technical Names: Services named after technical concerns (e.g.,
DatabaseService
) - CRUD-Only Operations: Services that only perform CRUD operations
- Infrastructure Dependencies: Services with dependencies on infrastructure components
- Large Interfaces: Services with many unrelated methods
- Primitive Parameters: Services that work primarily with primitives rather than domain objects
Examples of Domain Services​
Example 1: Pricing Service​
A Pricing Service might handle complex pricing rules:
Interface:
- calculatePrice(product, quantity, customer, promotions) -> Price
- calculateDiscount(product, customer, promotions) -> Discount
- determineTaxability(product, customer, location) -> Taxability
This service encapsulates pricing logic that depends on multiple factors and doesn't belong to any single entity.
Example 2: Inventory Allocation Service​
An Inventory Allocation Service might handle inventory management:
Interface:
- allocateInventory(order, inventory) -> AllocationResult
- releaseAllocation(allocation) -> ReleaseResult
- checkAvailability(products, quantities) -> AvailabilityResult
This service coordinates inventory allocation across multiple products and locations.
Example 3: Payment Processing Service​
A Payment Processing Service might handle payment operations:
Interface:
- processPayment(order, paymentMethod, amount) -> PaymentResult
- refundPayment(payment, amount, reason) -> RefundResult
- authorizePayment(order, paymentMethod, amount) -> AuthorizationResult
This service encapsulates payment logic that spans multiple entities.
Integration with Domain Events​
Domain Services often play a key role in generating and handling domain events:
Generating Events​
Domain Services may generate domain events to signal important changes:
- Business Process Events: Events that signal completion of business processes
- State Change Events: Events that indicate significant state changes
- Policy Violation Events: Events that signal violations of business policies
Handling Events​
Domain Services may also handle domain events:
- Trigger Cascading Actions: Perform additional actions in response to events
- Update Derived State: Update derived or calculated state based on events
- Cross-Aggregate Consistency: Maintain consistency across aggregate boundaries
Conclusion​
Domain Services are a critical component of Prism Architecture's Domain Layer, encapsulating important business processes, calculations, validations, and coordination logic that doesn't naturally fit within entities. By properly identifying and implementing Domain Services, you create a more expressive, maintainable domain model that accurately represents the business domain.
Remember that Domain Services should focus purely on domain logic, with no infrastructure or application concerns. They should express domain concepts clearly, use domain terminology, and operate on domain objects. By following the principles and patterns described in this document, you can create effective Domain Services that enhance your domain model and support your application's business logic.
Next Steps​
- Domain Events - Learn about reactive programming with domain events
- Domain Model Design - Explore advanced patterns for designing the domain model
- Integration with Use Cases - See how Domain Services integrate with Use Cases
- Repository Implementations - Understand how repositories implement data access