Domain Services Best Practices & Conventions
This document offers best practices for implementing Domain Service classes in your modules and applications based on Domain-Driven-Design principles.
Ensure you've read the Domain Services document first.
Domain Services
- Do define domain services in the domain layer.
- Do not create interfaces for the domain services unless you have a good reason to (like mock and test different implementations).
- Do name your domain service with Managersuffix.
For the example of a domain service:
public class IssueManager : DomainService
{
	//...
}
Domain Service Methods
- Do not define - GETmethods.- GETmethods do not change the state of an entity. Hence, use the repository directly in the Application Service instead of Domain Service method.
- Do define methods that only mutates data; changes the state of an entity or an aggregate root. 
- Do not define methods with generic names (like - UpdateIssueAsync).
- Do define methods with self explanatory names (like - AssignToAsync) that implements the specific domain logic.
- Do accept valid domain objects as parameters. 
public async Task AssignToAsync(Issue issue, IdentityUser user)
{
    //...
}
- Do throw - BusinessExceptionor custom business exception if a validation fails.- Do use domain error codes with unique code-namespace for exception localization.
 
public async Task AssignToAsync(Issue issue, IdentityUser user)
{
    var openIssueCount = await _issueRepository.GetCountAsync(
            i => i.AssignedUserId == user.Id && !i.IsClosed
        );
        if (openIssueCount >= 3)
        {
            throw new BusinessException("IssueTracking:ConcurrentOpenIssueLimit");
        }
        issue.AssignedUserId = user.Id;
}
- Do not return DTO. Return only domain objects when you need.
- Do not involve authenticated user logic. Instead, define extra parameter and send the related data of CurrentUserfrom the Application Service layer.
 
                                             
                                    