docs/en/framework/architecture/domain-driven-design/domain-services.md
//[doc-seo]
{
"Description": "Learn how to implement Domain Services in ABP Framework for core business logic, enhancing your Domain Driven Design solutions effectively."
}
In a Domain Driven Design (DDD) solution, the core business logic is generally implemented in aggregates (entities) and the Domain Services. Creating a Domain Service is especially needed when;
Domain Services are simple, stateless classes. While you don't have to derive from any service or interface, ABP provides some useful base classes and conventions.
Either derive a Domain Service from the DomainService base class or directly implement the IDomainService interface.
Example: Create a Domain Service deriving from the DomainService base class.
using Volo.Abp.Domain.Services;
namespace MyProject.Issues
{
public class IssueManager : DomainService
{
}
}
When you do that;
It is suggested to name a Domain Service with a
ManagerorServicesuffix. We typically use theManagersuffix as used in the sample above.
Example: Implement the domain logic of assigning an Issue to a User
public class IssueManager : DomainService
{
private readonly IRepository<Issue, Guid> _issueRepository;
public IssueManager(IRepository<Issue, Guid> issueRepository)
{
_issueRepository = issueRepository;
}
public async Task AssignAsync(Issue issue, AppUser user)
{
var currentIssueCount = await _issueRepository
.CountAsync(i => i.AssignedUserId == user.Id);
//Implementing a core business validation
if (currentIssueCount >= 3)
{
throw new IssueAssignmentException(user.UserName);
}
issue.AssignedUserId = user.Id;
}
}
Issue is an aggregate root defined as shown below:
public class Issue : AggregateRoot<Guid>
{
public Guid? AssignedUserId { get; internal set; }
//...
}
internal ensures that it can not directly set in the upper layers and forces to always use the IssueManager to assign an Issue to a User.A Domain Service is typically used in an application service.
Example: Use the IssueManager to assign an Issue to a User
using System;
using System.Threading.Tasks;
using MyProject.Users;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace MyProject.Issues
{
public class IssueAppService : ApplicationService, IIssueAppService
{
private readonly IssueManager _issueManager;
private readonly IRepository<AppUser, Guid> _userRepository;
private readonly IRepository<Issue, Guid> _issueRepository;
public IssueAppService(
IssueManager issueManager,
IRepository<AppUser, Guid> userRepository,
IRepository<Issue, Guid> issueRepository)
{
_issueManager = issueManager;
_userRepository = userRepository;
_issueRepository = issueRepository;
}
public async Task AssignAsync(Guid id, Guid userId)
{
var issue = await _issueRepository.GetAsync(id);
var user = await _userRepository.GetAsync(userId);
await _issueManager.AssignAsync(issue, user);
await _issueRepository.UpdateAsync(issue);
}
}
}
Since the IssueAppService is in the Application Layer, it can't directly assign an issue to a user. So, it uses the IssueManager.
While both of Application Services and Domain Services implement the business rules, there are fundamental logical and formal differences;
Lifetime of Domain Services are transient and they are automatically registered to the dependency injection system.