mirror of https://github.com/abpframework/abp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
5.0 KiB
135 lines
5.0 KiB
# Domain Services
|
|
|
|
## Introduction
|
|
|
|
In a [Domain Driven Design](Domain-Driven-Design.md) (DDD) solution, the core business logic is generally implemented in aggregates ([entities](Entities.md)) and the Domain Services. Creating a Domain Service is especially needed when;
|
|
|
|
* You implement a core domain logic that depends on some services (like repositories or other external services).
|
|
* The logic you need to implement is related to more than one aggregate/entity, so it doesn't properly fit in any of the aggregates.
|
|
|
|
## ABP Domain Service Infrastructure
|
|
|
|
Domain Services are simple, stateless classes. While you don't have to derive from any service or interface, ABP Framework provides some useful base classes and conventions.
|
|
|
|
### DomainService & IDomainService
|
|
|
|
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.**
|
|
|
|
````csharp
|
|
using Volo.Abp.Domain.Services;
|
|
|
|
namespace MyProject.Issues
|
|
{
|
|
public class IssueManager : DomainService
|
|
{
|
|
|
|
}
|
|
}
|
|
````
|
|
|
|
When you do that;
|
|
|
|
* ABP Framework automatically registers the class to the Dependency Injection system with a Transient lifetime.
|
|
* You can directly use some common services as base properties, without needing to manually inject (e.g. [ILogger](Logging.md) and [IGuidGenerator](Guid-Generation.md)).
|
|
|
|
> It is suggested to name a Domain Service with a `Manager` or `Service` suffix. We typically use the `Manager` suffix as used in the sample above.
|
|
|
|
**Example: Implement the domain logic of assigning an Issue to a User**
|
|
|
|
````csharp
|
|
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](Entities.md) defined as shown below:
|
|
|
|
````csharp
|
|
public class Issue : AggregateRoot<Guid>
|
|
{
|
|
public Guid? AssignedUserId { get; internal set; }
|
|
|
|
//...
|
|
}
|
|
````
|
|
|
|
* Making the setter `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`.
|
|
|
|
### Using a Domain Service
|
|
|
|
A Domain Service is typically used in an [application service](Application-Services.md).
|
|
|
|
**Example: Use the `IssueManager` to assign an Issue to a User**
|
|
|
|
````csharp
|
|
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`.
|
|
|
|
## Application Services vs Domain Services
|
|
|
|
While both of [Application Services](Application-Services.md) and Domain Services implement the business rules, there are fundamental logical and formal differences;
|
|
|
|
* Application Services implement the **use cases** of the application (user interactions in a typical web application), while Domain Services implement the **core, use case independent domain logic**.
|
|
* Application Services get/return [Data Transfer Objects](Data-Transfer-Objects.md), Domain Service methods typically get and return the **domain objects** ([entities](Entities.md), [value objects](Value-Objects.md)).
|
|
* Domain services are typically used by the Application Services or other Domain Services, while Application Services are used by the Presentation Layer or Client Applications.
|
|
|
|
## Lifetime
|
|
|
|
Lifetime of Domain Services are [transient](https://docs.abp.io/en/abp/latest/Dependency-Injection) and they are automatically registered to the dependency injection system. |