<<ABP架構>> 領網域服務

來源:互聯網
上載者:User

標籤:http   href   共用   資訊   相關   ssi   ddd   應用服務   domain   

文檔目錄

 

本節內容:

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

 

簡介

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

  1. 操作與領域概念(不是一個實體或值對象天生的一部分)相關。
  2. 介面要按照其它領域模型元素來定義。
  3. 操作是無狀態的。

與應用服務擷取/返回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(一個行動裝置 App和一個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,再也跳不過商務規則。

 

<<ABP架構>> 領網域服務

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.