From d28440004493d2d9c68bd5f79d010e2fe8e91abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 3 Nov 2020 18:26:17 +0300 Subject: [PATCH] Completed the Domain Services document --- docs/en/Domain-Driven-Design.md | 2 +- docs/en/Domain-Services.md | 134 +++++++++++++++++++++++++++++++- docs/en/docs-nav.json | 3 +- 3 files changed, 136 insertions(+), 3 deletions(-) diff --git a/docs/en/Domain-Driven-Design.md b/docs/en/Domain-Driven-Design.md index bbf22eab13..9e0343d562 100644 --- a/docs/en/Domain-Driven-Design.md +++ b/docs/en/Domain-Driven-Design.md @@ -29,7 +29,7 @@ See the following documents to learn what ABP Framework provides to you to imple * [Entities & Aggregate Roots](Entities.md) * Value Objects * [Repositories](Repositories.md) - * Domain Services + * [Domain Services](Domain-Services.md) * Specifications * **Application Layer** * [Application Services](Application-Services.md) diff --git a/docs/en/Domain-Services.md b/docs/en/Domain-Services.md index 9f4a5ee544..5a4f6393e1 100644 --- a/docs/en/Domain-Services.md +++ b/docs/en/Domain-Services.md @@ -1,3 +1,135 @@ # Domain Services -TODO \ No newline at end of file +## 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 _issueRepository; + + public IssueManager(IRepository 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 +{ + 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 _userRepository; + private readonly IRepository _issueRepository; + + public IssueAppService( + IssueManager issueManager, + IRepository userRepository, + IRepository 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. \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 838e41d5be..8034368347 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -351,7 +351,8 @@ "path": "Repositories.md" }, { - "text": "Domain Services" + "text": "Domain Services", + "path": "Domain-Services.md" }, { "text": "Specifications",