1. 程式人生 > 實用技巧 >ABPVNext搭建微服務入門(2)-- 從領域開始物件建模

ABPVNext搭建微服務入門(2)-- 從領域開始物件建模

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

參考

https://www.cnblogs.com/sheng-jie/category/997516.html