DDD实战进阶第一波(六):开发一般业务的大健康行业直销系统(实现产品上下文仓储与应用服务层)
前一篇文章我们完成了产品上下文的领域层,我们已经有了关于产品方面的简单领域逻辑,我们接着来实现产品上下文关于仓储持久化与应用层的用例如何来协调
领域逻辑与仓储持久化。
首先大家需要明确的是,产品上下文的领域逻辑是系统的核心,它不应该依赖仓储,而仓储应该要依赖领域层,这样仓储才可以把领域逻辑执行完后,才可能将
领域对象持久化到数据库中,这一点与传统的架构有本质的区别。
一般我们会在解决方案中建立一个项目,这个项目就是包含了所有聚合的仓储实现,具体不同上下文的仓储实现,可以在这个项目下建立不同的文件夹。
1.产品上下文仓储实现:
public class ProductEFCoreRepository : IProductRepository { private readonly DbContext context; public ProductEFCoreRepository(DbContext context) { this.context = context; } public void CreateProduct<T>(T productspu) where T:class,IAggregationRoot { var productdbcontext = this.context as ProductEFCoreContext; var productspunew = productspu as ProductSPU; try { productdbcontext.ProductSPU.Add(productspunew); } catch(Exception error) { throw error; } } }
上面的代码有几个要注意的方面:
a.首先会从产品的仓储接口做继承,通过EF Core的机制,实现了仓储接口的CreateProduct方法。
b.使用了产品上下文的EF Core数据访问上下文ProductEFCoreContext完成了Productspu的数据库预添加。
c.上一个说法中,可能大家有两个疑惑,一是为什么不使用productdbcontext标记ProductSPU为Added状态,而是使用.Add方法,二是为什么只是完成了添加状态,
而不再后续调用Commit或SaveChange方法真正持久化到数据库中?首先,因为未来持久化要将这个聚合中的ProductSPU聚合根与ProductSKU实体作为一个整体持久化到数据库中,
而Added状态只能将当前聚合根作为添加状态,而不能同时将引用的ProductSKU对象作为添加状态,所以不能使用Added状态而使用.Add方法;其次仓储实现聚合提交时,只进行数
据库预添,是因为协调领域逻辑与仓储的应用服务层用例可能涉及到多个聚合,所以可能要同时调用多个领域对象的业务逻辑,多个仓储,完成后,将多
聚合作为一个整体事务做提交,所以真正的提交应该放到应用服务层更合适,而不是仓储层。
2.产品上架应用服务层实现:
应用服务层实际就是完成用例,通过应用服务层调用领域逻辑,然后通过应用服务层调用仓储,最后应用服务层做真正的提交,这样就把职责分的非常清楚,也在领域逻辑不依赖
仓储的前提下,完成了整个用例和持久化。
a.首先我们在产品上下文的应用服务层项目中,建立需要添加的产品SPU与对应产品SKU的DTO对象
public class AddProductSPUDTO { public string SPUName { get; set; } public string SPUDesc { get; set; } public List<string> SKUSpecs { get; set; } public List<string> SKUUnits { get; set; } public List<decimal> SKUDealerPrices { get; set; } public List<byte[]> SKUImages { get; set; } public List<decimal> SKUPvs { get; set; } }
b.建立一个上架产品的用例服务,协调领域逻辑与仓储完成用例
public class AddProductSPUUseCase:BaseAppSrv { private readonly IRepository irepositorycontext; private readonly IProductRepository iproductrepository; public AddProductSPUUseCase(IRepository irepositorycontext,IProductRepository iproductrepository) { this.irepositorycontext = irepositorycontext; this.iproductrepository = iproductrepository; } public ResultEntity<bool> AddProduct(AddProductSPUDTO addproductspudto) { var productspuid = Guid.NewGuid(); var productskus = new List<ProductSKU>(); for(int i = 0; i < addproductspudto.SKUSpecs.Count; i++) { var productsku = new ProductSKU().CreateProductSKU(addproductspudto.SPUName, productspuid, addproductspudto.SKUImages[i], addproductspudto.SKUDealerPrices[i], addproductspudto.SKUPvs[i], addproductspudto.SKUUnits[i], addproductspudto.SKUSpecs[i]); productskus.Add(productsku); } var productspu = new ProductSPU().CreateProductSPU(productspuid, addproductspudto.SPUName, addproductspudto.SPUDesc, productskus); try { using (irepositorycontext) { iproductrepository.CreateProduct(productspu); irepositorycontext.Commit(); } return GetResultEntity(true); } catch(Exception error) { throw error; } } }
BaseAppSrv是你要定义的一个类,它的GetResultEntity方法功能是完成用例后后,返回接口层的数据格式,这个数据格式会进一步通过接口层返回给前端,返回
的数据格式就是ResultEntity<T>,这两个部分大家可以自己去实现,也可以参考我的微信公众号中的课程。
QQ讨论群:309287205
DDD实战进阶视频请关注微信公众号: