ABP VNext 微服务搭建入门(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

posted @ 2020-12-06 14:04  李锦成  阅读(778)  评论(0编辑  收藏  举报