程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> <<ABP框架>> 領域服務,abp框架領域服務

<<ABP框架>> 領域服務,abp框架領域服務

編輯:關於.NET

<<ABP框架>> 領域服務,abp框架領域服務


文檔目錄

 

本節內容:

  • 簡介
  • 例子
    • 創建一個接口
    • 實現服務
    • 使用應用服務
  • 相關論述
    • 為什麼不只用應用服務?
    • 如何強制你使用領域服務?

 

簡介

領域服務(或服務)用來執行領域操作和業務規則。Eric Evans描述一個好的服務需要三個特點(在他的DDD書裡):

與應用服務獲取/返回DTO(數據傳輸對象)不同,領域服務獲取/返回領域對象(如實體或值類型)。

領域服務可被應用服務或其它領域服務調用,但不直接被展現層(應用服務是針對它的)使用。

 

IDomainService 接口和DomainService 服務

ABP定義了IDomainService接口,按約定所有的領域服務都要實現它,實現之後,領域服務被自動暫時的注冊到依賴注入系統。

同樣,領域服務(隨意地)可以從DomainService類繼承,因此它可以使用繼承得來的日志、本地化、等屬性。即使不繼承DomainService類,也可以在需要時注入它。

 

例子

假設我們有一個任務管理系統,並有分配任務給人的業務規則。

 

創建一個接口

首先,我們為服務定義一個接口(不是必需,但是一個好的實踐):

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

如你所見,TaskManager服務使用領域對象:一個Task和一個Person。命名領域服務有些約定,可以命名為:TaskManager、TaskService、或TaskDomainService...

 

實現服務

讓我們看一下實現:

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;
    }
}

此處我們有兩個業務規則:

  • 一個Task應當處於活躍狀態,才能分配給一個新的Person。
  • 一個Person最多可以分配3個活躍Task

你可能想知道為什麼我在第一個檢查裡拋出一個ApplicationException,在第二個檢查裡拋出UserFriendlyException(見異常處理),這跟領域服務毫不相干,我這麼做只是舉個例子,具體怎麼做完全取決於你。我認為用戶接口必須檢查一個Task的狀態並且不應該分配給一個Person,我認為這是一個應用錯誤,應當對用戶隱藏。第二個是UI上的檢查,我們顯示一個具有可讀性的錯誤信息給用戶。

 

使用應用服務

現在,讓我們看一下一個應用服務如何使用TaskManager服務:

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使用給定的DTO(輸入)和倉儲來獲取相關的task和person,然後把它們傳遞給TaskManager(領域服務)。

 

相關論述

根據以上例子,你可能有些問題想問。

 

為什麼不只用應用服務

也就是說為什麼應用服務不實現領域服務裡的業務邏輯?

我們可以簡單的這麼說:它不是應用服務的任務,因為它不是一個使用案例,而是一個業務操作。我們可能在另一個使用案例裡使用同一個“分配任務給人員”的領域邏輯,例如:我們在另一個界面上,用某種方式更新task,這個更新包含分配任務給另一個人員;我們可能有兩個不同的UI(一個移動應用和一個Web應用)共享相同的領域;或是一個為遠程客戶端提供分配task操作的Web Api等。

如果你的領域很簡單, 只有一個UI、只有一個地方用到分配任務給人員的操作,你可能想跳過領域服務,直接在你的應用服務裡實現業務邏輯,這不是DDD的最佳實踐,但ABP沒有強制你不能使用這種設計。

 

如何強制你使用領域服務?

應用服務可以簡單的這麼做:

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

寫應用服務的開發人員可能不知道TaskManager的存在,直接把給定的PersonId賦值給AssignedPersonId,所以要如何阻止這麼做呢?

關於這個問題,在DDD領域裡有很多的論述,也有一些使用模式,我們不准備非常深入,但我們提供一個簡單的方式:

我們把Task實體修改成如下所示:

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;
    }
}

我們把AssignedPersonId的設置器修改成protected,所以在實體類外就不能修改它。添加一個AssignToPerson方法,接受一個Person和一個TaskPolicy。CheckIfCanAssignTaskToPerson方法檢查是否可以分配,並在不能分配時拋出一個對應的異常(此處如何實現不重要)。然後應用服務如下所示:

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

    task.AssignToPerson(person, _taskPolicy);
}

我們把_taskPolicy注入給ITaskPolicy並傳給AssignToPerson方法。至此就沒有第二方式能把一個任務分配給一個人員了,我們只能使用AssignToPerson,再也跳不過業務規則。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved