前言:DDD的基础知识这里就不讲了,网上有很多,无外乎 架构从以外的三层变成四层,有聚合、实体、值对象、领域服务等这些概念,我也是最近看了很多,但无从下手,正好我们现有的项目是基于ABP
框架的,虽说也支持DDD,也是分为了4个项目,有领域有Domain,但感觉还是个三层项目,想了想,最大的问题还是收到新的任务后,总是从数据库开始,然后T4生成实体,没有太多的去进行领域划分。所以本次
我们使用EFCore的CodeFirst和ABPVnext来体验一下怎么在项目中真正的运用DDD。
--------------------------------------------------------------------------------------------------------------------------------------------------
新建项目
我这里使用ABP的CLI命令创建的,当然也可以直接在网站(https://www.abp.io/get-started)上下载
切换后,我们使用下面这俩命令创建一下数据库
Add-Migration "Init"Update-DataBase
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
重点来了
接下来我们正式从一个订单的领域开始, 首先看一张图
首先我们先创建个地址的值对象
///
///地址(值对象)///
public classAddress : ValueObject
{public String Street { get; private set; }public String City { get; private set; }public String State { get; private set; }public String Country { get; private set; }public String ZipCode { get; private set; }publicAddress() { }public Address(string street, string city, string state, string country, stringzipcode)
{
Street=street;
City=city;
State=state;
Country=country;
ZipCode=zipcode;
}protected override IEnumerableGetAtomicValues()
{//Using a yield return statement to return each element one at a time
yield returnStreet;yield returnCity;yield returnState;yield returnCountry;yield returnZipCode;
}
}
创建实体
///
///订单明细实体///
public classOrderItem : Entity
{public virtual Guid OrderId { get; protected set; }public virtual Guid ProductId { get; protected set; }public virtual int Count { get; protected set; }protectedOrderItem()
{
}internal OrderItem(Guid orderId, Guid productId, intcount)
{
OrderId=orderId;
ProductId=productId;
Count=count;
}internal void ChangeCount(intnewCount)
{
Count=newCount;
}public override object[] GetKeys()
{return newObject[] { OrderId, ProductId };
}
}
创建订单聚合根
///
///订单聚合根///
public class Order : AggregateRoot{public virtual string ReferenceNo { get; protected set; }public virtual int TotalItemCount { get; protected set; }public virtual DateTime CreationTime { get; protected set; }public virtual List OrderLines { get; protected set; }public Address Address { get; protected set; }//值对象
public string UserName { get; protected set; }protectedOrder()
{
}public Order(Guid id, string referenceNo, stringuserName, Address address)
{
Check.NotNull(referenceNo, nameof(referenceNo));
Id=id;
ReferenceNo=referenceNo;
Address=address;
OrderLines= new List();
CreationTime=DateTime.Now;
UserName=userName;
}public void AddProduct(Guid productId, intcount)
{if (count <= 0)
{throw newArgumentException("You can not add zero or negative count of products!",
nameof(count)
);
}var existingLine = OrderLines.FirstOrDefault(ol => ol.ProductId ==productId);if (existingLine == null)
{
OrderLines.Add(new OrderItem(this.Id, productId, count));
}else{
existingLine.ChangeCount(existingLine.Count+count);
}
TotalItemCount+=count;
}
}
然后在Context中添加
public DbSet Orders { get; set; }public DbSet OrderItems { get; set; }
public static void ConfigureDDDTest(thisModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));/*Configure your own tables/entities inside here*/
//builder.Entity(b =>//{//b.ToTable(DDDTestConsts.DbTablePrefix + "YourEntities", DDDTestConsts.DbSchema);// //...//});
builder.Entity(b =>{
b.ToTable(DDDTestConsts.DbTablePrefix+ "Orders", DDDTestConsts.DbSchema);
b.ConfigureByConvention();
b.OwnsOne(o=> o.Address, a =>{
a.WithOwner();
});//b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
builder.Entity(b =>{
b.ToTable(DDDTestConsts.DbTablePrefix+ "OrderLines", DDDTestConsts.DbSchema);
b.ConfigureByConvention();
b.HasKey(x=> new{ x.OrderId, x.ProductId });//b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
}
此时我们再执行迁移命令,会发现数据库中多了俩张表,值对象在聚合中
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这时候我们执行个创建的方法是,会出现这个错误
。
这个时候只需要把 base.OnModelCreating(builder);,移到builder.ConfigureDDDTest();下面即可
自定义仓储的话,我们需要把接口定义在领域层,把实现放在基础设施层
public class OrderRepository : EfCoreRepository, IOrderRepository
{public OrderRepository(IDbContextProviderdbContextProvider)
:base(dbContextProvider)
{
}public async TaskGet(Guid orderId)
{var order = awaitDbContext
.Orders
.Include(x=>x.Address)
.FirstOrDefaultAsync(o=> o.Id ==orderId);if (order == null)
{
order=DbContext
.Orders
.Local
.FirstOrDefault(o=> o.Id ==orderId);
}if (order != null)
{awaitDbContext.Entry(order)
.Collection(i=>i.OrderLines).LoadAsync();
}returnorder;
}
}
整体实现下来,不知道有没有点对DDD的感觉呢?
参考:
https://www.cnblogs.com/richieyang/p/5373250.html
https://cn.abp.io/blog/Abp/Abp-vNext-Announcement
https://docs.abp.io/en/abp/latest/
https://github.com/abpframework/abp/issues/2640
https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/microservice-domain-model
https://github.com/dotnet-architecture/eShopOnContainers