& Lt; ABP framework & gt; domain service, and domain service

Source: Internet
Author: User
Tags to domain

<ABP framework> domain services, and domain services

Document directory

 

Content of this section:

  • Introduction
  • Example
    • Create an Interface
    • Implementation Service
    • Use application services
  • Discussion
    • Why not just use application services?
    • How to force you to use domain services?

 

Introduction

Domain services (or services) are used to perform domain operations and business rules. Eric Evans describes that a good service requires three features (in his DDD book ):

Unlike application services that obtain/return DTO (data transmission objects), domain services obtain/return domain objects (such as objects or value types ).

Domain services can be called by application services or other domain services, but are not directly used by the presentation layer (the application service is for it.

 

IDomainService interface and DomainService

ABP defines the IDomainService interface, which is implemented by all domain services as agreed. After implementation, domain services are automatically registered to the dependency injection system temporarily.

Similarly, domain services (at Will) can be inherited from the DomainService class, so they can use the inherited log, localization, and other attributes. Even if you do not inherit the DomainService class, you can inject it as needed.

 

Example

Suppose we have a task management system with business rules assigned to people.

 

Create an Interface

First, we define an interface for the service (not required, but a good practice ):

public interface ITaskManager : IDomainService{    void AssignTaskToPerson(Task task, Person person);}

As you can see, the TaskManager service uses a domain object: a Task and a Person. Some naming conventions for domain services can be named TaskManager, TaskService, or TaskDomainService...

 

Implementation Service

Let's take a look at the implementation:

public class TaskManager : DomainService, ITaskManager{    public const int MaxActiveTaskCountForAPerson = 3;    private readonly ITaskRepository _taskRepository;    public TaskManager(ITaskRepository taskRepository)    {        _taskRepository = taskRepository;    }    public void AssignTaskToPerson(Task task, Person person)    {        if (task.AssignedPersonId == person.Id)        {            return;        }        if (task.State != TaskState.Active)        {            throw new ApplicationException("Can not assign a task to a person when task is not active!");        }        if (HasPersonMaximumAssignedTask(person))        {            throw new UserFriendlyException(L("MaxPersonTaskLimitMessage", person.Name));        }        task.AssignedPersonId = person.Id;    }    private bool HasPersonMaximumAssignedTask(Person person)    {        var assignedTaskCount = _taskRepository.Count(t => t.State == TaskState.Active && t.AssignedPersonId == person.Id);        return assignedTaskCount >= MaxActiveTaskCountForAPerson;    }}

Here we have two business rules:

  • A Task must be active before it can be assigned to a new Person.
  • One Person can be assigned up to three active tasks.

You may want to know why I threw an ApplicationException In the first check and UserFriendlyException In the second check (see Exception Handling), which is irrelevant to domain services, this is just an example. It depends on you. I think the user interface must check the status of a Task and should not be assigned to a Person. I think this is an application error and should be hidden from the user. The second is the check on the UI. We display a readable error message to the user.

 

Use application services

Now let's take a look at how the next Application Service uses the TaskManager service:

public class TaskAppService : ApplicationService, ITaskAppService{    private readonly IRepository<Task, long> _taskRepository;    private readonly IRepository<Person> _personRepository;    private readonly ITaskManager _taskManager;    public TaskAppService(IRepository<Task, long> taskRepository, IRepository<Person> personRepository, ITaskManager taskManager)    {        _taskRepository = taskRepository;        _personRepository = personRepository;        _taskManager = taskManager;    }    public void AssignTaskToPerson(AssignTaskToPersonInput input)    {        var task = _taskRepository.Get(input.TaskId);        var person = _personRepository.Get(input.PersonId);        _taskManager.AssignTaskToPerson(task, person);    }}

TaskApplicationService uses the given DTO (input) and warehouse to obtain related tasks and persons, and then passes them to TaskManager (domain service ).

 

Discussion

Based on the above example, you may want to ask some questions.

 

Why not only use application services??

That is to say, why does the application service not implement the business logic in the domain service?

We can simply say that it is not an application service task, because it is not a use case, but a business operation. In another use case, we may use the same domain logic of "assigning tasks to people". For example, we update tasks in some way on another interface, this update includes assigning tasks to another person. We may have two different UIS (one mobile application and one Web application) sharing the same domain; or a Web Api that provides tasks for remote clients.

If your domain is very simple, there is only one UI and only one place to use the operations assigned to the personnel, you may want to skip the domain service and directly implement the business logic in your application service, this is not the best practice of DDD, but it is not mandatory for you to use this design.

 

How to force you to use domain services?

The Application Service can simply do this:

public void AssignTaskToPerson(AssignTaskToPersonInput input){    var task = _taskRepository.Get(input.TaskId);    task.AssignedPersonId = input.PersonId;}

Developers who write application services may not know the existence of TaskManager. They directly assign the given PersonId to AssignedPersonId. So how can they prevent this?

There are many discussions about this issue and some usage models in the DDD field. We are not prepared to go into depth, but we provide a simple method:

Modify the Task object as follows:

public class Task : Entity<long>{    public virtual int? AssignedPersonId { get; protected set; }    //...other members and codes of Task entity    public void AssignToPerson(Person person, ITaskPolicy taskPolicy)    {        taskPolicy.CheckIfCanAssignTaskToPerson(this, person);        AssignedPersonId = person.Id;    }}

Modify AssignedPersonId to protected, so it cannot be modified outside the object class. Add an AssignToPerson method and accept a Person and a TaskPolicy. The CheckIfCanAssignTaskToPerson method checks whether the resource can be allocated and throws a corresponding exception when the resource cannot be allocated (how to implement the exception here is not important ). Then the application service is shown as follows:

public void AssignTaskToPerson(AssignTaskToPersonInput input){    var task = _taskRepository.Get(input.TaskId);    var person = _personRepository.Get(input.PersonId);    task.AssignToPerson(person, _taskPolicy);}

We inject _ taskPolicy into ITaskPolicy and pass it to the AssignToPerson method. So far, there is no second way to assign a task to a single employee. We can only use AssignToPerson, but can no longer skip the business rules.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.