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