前言
以下記錄 ABP 在 Domain Servcice 及 Repository 方面的設計,
為何需要 Domain Service?
設計 Repository 有什麼要注意的地方?
Implementing domain services
有時為了維持 Domain 物件的正確性,需要針對 Property 值做驗證,
但如果需要外部服務(例如 DB Repository …)來驗證,
那就需要將 外部服務 當作參數傳進來使用。
但這樣就會讓 Domain 物件相依外部服務,
也會變的複雜及難以測試。
而且也違反單一責任原則(single responsibility principle)。
所以比較好的方式是將那些需要外部服務的商業邏輯驗證程式放到 Domain Services 之中。
而原本的 Domain private 屬性驗證函式就改成 internal
,讓同一專案的 Domain Service 可以呼叫,但別專案一樣不能呼叫。
例如原本在 Event 物件中檢查 Capacity 的值是否合理的 SetCapacityAsync Method 就會移到 EventManager Class 之中。
如果 Domain 的屬性不需要商業邏輯的檢查則不需要特別建立一個 Method 來接受參數並進行驗證。
如果在進行 商業邏輯規則驗證 需要外部服務,才需要建立 Domain Service 來進行接受外部服務。
Implementing repositories
1.在 Domain Layer
2.只為 Aggreat Root Entity 建立,不為 sub-collection entities 建立,因為 sub-collection entities 的存取要透過 aggreat root entity
3.使用 domain object ,不是 DTOs
4.Repository interface 要獨上於 database provider,這樣才可以抽換不同的 provider 5.不要將商業邏輯實作在 Repository
在 Repository Method 實作中,會先取得 IQueryable 再透過它去 Filter,例如,
1 | public async Task<List<Event>> GetSpokenEventsAsync(Guid userId) |
這樣子的好處是這段程式碼可以被共用 …
但如果又有別的需求,那就要建立一堆這種 GetList 的 Method !!!
ABP 的 Respository 提供 GetListAsync ,輸入 predicate 參數(Func<Entity, bool>
)即可。
1 | public async Task<List<Event>> GetSpokenEventsAsync(Guid userId) |
這樣這段程式碼就會回到 Application Layer 造成複雜的程式碼,
那怎麼辦??
可以將 predicate 建立成 ABP Specification 物件
Building specifications
ABP Specification 物件可以讓我們重用、結合並且可測試的,
例如以下建立一個 篩選出 Online Event 的 Specification 物件
1 | public class OnlineEventSpecification: |
所以要取得線上的 Event 就可以使用以下方式,
1 | var events = _eventRepository |
測試則可以使用 specification 的 IsSatisfiedBy
1 | Event evnt = GetEvent(); |
前面取得 Speaker 可以將 Filter 抽到 Specification
1 | public class SpeakerSpecification: Specification<Event> |
所以 Applicatoin Code 就會變得簡潔,如下,
1 | public async Task<List<Event>> GetSpokenEventsAsync(Guid userId){ |
可以透過 使用 And, Or, AndNot 將 Specification 一起使用
1 | var events = _eventRepository.GetListAsync( |