ABPVNext搭建微服務入門(2)-- 從領域開始物件建模
阿新 • • 發佈:2020-12-06
DDD的好處
相對於傳統的資料驅動設計,基於領域驅動設計的程式碼可以提現通用語言,更具可讀性,更能準確表達業務。
一、確定領域、拆分子域
常見電商系統拆分
領域:電商
子域:銷售、商品、使用者、商家、訂單等
核心域:銷售
通用域:非業務模組,如日誌子域
支撐域:物流、商品等
二、限界上下文(語境)
當劃分子域之後,每個子域都對應有各自的上下文。在銷售子域和商品子域所在的上下文語境中,商品就是商品,無二義性。但如果子域對應多個上下文的時候,就要考慮一下是不是子域能否繼續劃分。
*三、領域物件
領域物件是服務的提供方,而不是資料容器,提供業務行為,而不是資料。
領域模型(實體)
領域模型在程式碼中提現為實體,對映到真實資料表。實體有唯一標識,具有可變性
領域模型應該是充血模型而不是貧血模型。貧血模型只包含屬性的 get set 方法。充血模型更加豐富,包含業務邏輯。
abp已經定義了 Entity 系列類,實現即可。
值物件
值物件沒有唯一標識,且不可變。如訂單地址就是不可變,而收貨地址是可變的且擁有唯一標識。abp已經定義了 ValueObject 類,實現即可。
聚合根
聚合根是主實體,子實體都不可能孤立存在,它們必須依附於一個聚合根存在。如訂單就是聚合根,物流資訊是依賴於訂單的子實體。abp已經定義了 AggregateRoot 系列類,實現即可。
聚合
一個聚合中可以包含多個實體和值物件。聚合是持久化的基本單位,它和倉儲具有一一對應的關係。如一個完整的訂單聚合就包含物流等資訊。
示例
public class BlogPost: AggregateRoot<int> { private BlogPost() { // just for EF } //使用引數化的建構函式可以確保我們的領域模型在例項化時有效 public BlogPost(string title, string summary, string body) { if (string.IsNullOrWhiteSpace(title)) { throw new ArgumentException("Title is required"); } ... Title = title; Summary = summary; Body = body; DateAdded = DateTime.UtcNow; } //引入更改狀態的方法使我們能夠集中業務邏輯並簡化呼叫程式碼 public void Publish() { if (Status == BlogPostStatus.Draft || Status == BlogPostStatus.Archived) { if (Status == BlogPostStatus.Draft) { DatePublished = DateTime.UtcNow; } Status = BlogPostStatus.Published; } } private string title; [Required] public string Title { get { return title; } set { //內部集中校驗 if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentException("Title must contain a value"); } title = value; } } [Required] [StringLength(500)] //清除公共屬性setter確保我們的模型在其整個生命週期內保持有效狀態 public string Summary { get;private set; } [Required] public string Body { get;private set; } public DateTime DateAdded { get;private set; } public DateTime? DatePublished { get;private set; } public BlogPostStatus Status { get;private set; } //使用值物件 生成 AdvertisingFee_Currency 和 AdvertisingFee_Amount 列 public Money AdvertisingFee { get; private set; } ... } public class Money:ValueObject { [StringLength(3)] public string Currency { get; private set; } public int Amount { get; private set; } private Money() { // just for EF } public Money(string currency, int amount) { // todo validation Currency = currency; Amount = amount; } protected override IEnumerable<object> GetAtomicValues() { yield return Currency; yield return Amount; } } public class BlogContext : DbContext { ... public DbSet<BlogPost> BlogPosts { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<BlogPost>().OwnsOne(x => x.AdvertisingFee); } }