Skip to main content

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:

  1. Implementing Business Processes: Representing domain operations that involve multiple entities
  2. Enforcing Domain Rules: Implementing complex business rules that span entity boundaries
  3. Coordinating Entity Interactions: Managing interactions between different domain objects
  4. 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:

  1. Pure Domain Focus: They contain only domain logic, no infrastructure or application concerns
  2. Core Layer Dependence: They depend only on entities and value objects from the Core Layer
  3. No Outward Dependencies: They don't depend on the Orchestration or Infrastructure layers
  4. 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:

  1. Entities and Value Objects: Domain Services operate on entities and value objects from the Core Layer
  2. Repositories (Interfaces): Domain Services may use repository interfaces defined in the Core Layer
  3. Use Cases: Use Cases in the Orchestration Layer call Domain Services to implement business logic
  4. 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 ServicesApplication Services (Use Cases)
Focus on business rules and logicFocus on orchestration and coordination
Pure domain conceptsMay involve technical concerns
No infrastructure dependenciesMay use infrastructure components
Not concerned with use case flowManage application flow and user interaction
Used by application servicesUse domain services

Domain Services vs. Infrastructure Services​

Infrastructure Services provide technical capabilities and differ significantly from Domain Services:

Domain ServicesInfrastructure Services
Implement business logicImplement technical capabilities
Domain terminologyTechnical terminology
No external dependenciesIntegrate with external systems
No knowledge of persistenceOften handle persistence details
Used by application layerUsed by multiple layers

Domain Services vs. Domain Entities​

While both Domain Services and Entities reside in the domain model, they have different characteristics:

Domain ServicesDomain Entities
Represent processes or operationsRepresent domain objects or concepts
Typically statelessHave identity and state
Named after activities or processesNamed after things or nouns
Focus on behaviorFocus on both data and behavior
Operations on multiple entitiesOperations on their own state

Designing Effective Domain Services​

Identification Strategies​

To identify when to create a Domain Service, look for these indicators:

  1. Multi-Entity Operations: Logic that spans multiple entities
  2. Processes, Not Things: Activities that don't naturally belong to an entity
  3. Stateless Operations: Logic that doesn't require maintaining state between calls
  4. Domain Expert Language: When domain experts talk about processes rather than things
  5. Complex Rules: Business rules that apply across entity boundaries

Design Principles​

Follow these principles when designing Domain Services:

  1. Domain-Driven Names: Use names that reflect domain activities and processes
  2. Verb-Based Interfaces: Define methods that express actions in the domain
  3. Explicit Parameters: Make all dependencies explicit as method parameters
  4. Return Values Over Side Effects: Prefer returning results rather than modifying state
  5. Immutability: Use immutable parameters and return immutable results when possible
  6. Domain Error Types: Use domain-specific error types rather than technical exceptions

Interface Design​

Well-designed Domain Service interfaces:

  1. Express Domain Intent: Clearly communicate what the service does in domain terms
  2. Hide Implementation Details: Abstract away complex implementation details
  3. Use Domain Types: Accept and return domain objects rather than primitives
  4. Group Related Operations: Keep related operations together in the same service
  5. 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:

  1. Strategy Pattern: For interchangeable business rules or calculations
  2. Specification Pattern: For complex, composable validation rules
  3. Decorator Pattern: For adding cross-cutting concerns to services
  4. Chain of Responsibility: For sequential processing steps
  5. 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:

  1. Pure Logic Testing: Test business rules and calculations directly
  2. No Mocking Required: Often require minimal or no mocking
  3. Parameterized Tests: Use parameterized tests for different scenarios
  4. Boundary Testing: Test boundary conditions and edge cases
  5. Expected vs. Actual: Compare expected outputs with actual results

Behavior Testing​

For more complex Domain Services, behavior testing may be appropriate:

  1. Given-When-Then: Use behavior-driven testing approaches
  2. Business Scenario Testing: Test complete business scenarios
  3. Domain Event Verification: Verify that appropriate domain events are generated
  4. 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:

  1. Anemic Services: Services that are merely data carriers with no behavior
  2. God Services: Services that try to handle too many responsibilities
  3. Infrastructure Leakage: Including infrastructure concerns in Domain Services
  4. Procedural Services: Services that implement procedural logic rather than domain concepts
  5. Entity Behavior in Services: Putting entity behavior in services rather than in entities
  6. Technical Services: Services focused on technical rather than domain concerns

Signs of Problematic Domain Services​

Watch for these warning signs that may indicate problems:

  1. Technical Names: Services named after technical concerns (e.g., DatabaseService)
  2. CRUD-Only Operations: Services that only perform CRUD operations
  3. Infrastructure Dependencies: Services with dependencies on infrastructure components
  4. Large Interfaces: Services with many unrelated methods
  5. 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:

  1. Business Process Events: Events that signal completion of business processes
  2. State Change Events: Events that indicate significant state changes
  3. Policy Violation Events: Events that signal violations of business policies

Handling Events​

Domain Services may also handle domain events:

  1. Trigger Cascading Actions: Perform additional actions in response to events
  2. Update Derived State: Update derived or calculated state based on events
  3. 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