DDD的模型选择
关于DDD的模型选择,应该是在05年的时候,从充血模型转换到贫血模型,那时候的资料太少,自己是通过项目体会出来的,架构经过这些年的升级改进,从模型方面这一块基本应该是不再有大的变化了。至少这些年的这么多项目,用起来非常顺手,从分析、设计、编码一路映射下来,现在又加个工作流、静态图,也只是对框架的完善。
我说说自己的理解。
//---------------------------------------
说DDD,先上标准的图和解释:
1. 用户界面/展现层
负责向用户展现信息以及解释用户命令。更细的方面来讲就是:
a) 请求应用层以获取用户所需要展现的数据;
b) 发送命令给应用层要求其执行某个用户命令。
2. 应用层
很薄的一层,定义软件要完成的所有任务。对外为展现层提供各种应用功能(包括查询或命令),对内调用领域层(领域对象或领域服务)完成各种业务逻辑,应用层不包含业务逻辑。
3. 领域层
负责表达业务概念,业务状态信息以及业务规则,领域模型处于这一层,是业务软件的核心。
4. 基础设施层
本层为其他层提供通用的技术能力;提供了层间的通信;为领域层实现持久化机制;总之,基础设施层可以通过架构和框架来支持其他层的技术需求。
//--------------------------------------
1、界面层:就是获取显示数据,把数据发送到服务端,并告诉服务端这些数据干什么用。
2、应用层?我有些不理解:作用是什么呢?存在价值是什么?如果用的是充血模型,如果对客户端通信的话,可以在此解耦,以值类型实现远程通信,包括SOA的支持等。如果想走SOA,充血模型应该是行不通的,特别是如果用的ORM后,可能有的是动态类,序列化与反序列化,都是一个大问题。如果是贫血模型,这一层好像是多余的,或者也有是为了SOA或者其他之类的服务,或者技术难题,这是可以理解的。如果是作为流程编排层,那么与应用层的定义就有差异了。在我的框架里面,就不存在应用层,但是存在流程编排层,但不是作为独立的层次存在,从有篇随笔上已经有表现了。
3、领域层:我想可以理解为业务逻辑层。如果说一定要纯粹的OO,一个类一定要有属性、方法才是完整意义上的OO,那好像有些太本本主义了。重要是要好用,能够方便的实现问题的解决。 如果以Service对外公开服务也未偿不可,或者说方法,方法分几4种,1是public对客户端公开的粗粒度的服务,相当于SOA用到的服务;2是internal对DLL公开的内部可以访问的服务,对模型内有效;3是protected 对继承的对象有效;4是private只对内部有效。组合起来,应该能够实现领域层的要求,不足就是本来一个对象一个类可以实现的东东,硬生生把人分家。有人提到公开了增删改的操作,没有必要公开,多余了,其实不就是4个操作嘛,也差不多哪里去了。没有十全十美的方法,有缺点,也有优点,可以直接对客户端提供服务,统一模型。
4、基础设施层:可能更多的是ORM,持久化之类的吧。当然会有一些通用服务等等。
还是一句话,没有十全十美的。不同的理解会造就不同的模型,产生可能相当大的差异。
上一段代码,看看我的理解:
1、实体对象。 这是一个主从结构 ,从下面的代码上面已经能够明细看的出来了
Order
namespace eWMS.Data { using System; using System.Collections.Generic; using EES.Common; using EES.Common.Data; using EES.Common.Model; using System.ComponentModel; [EESData( " 移库单 " )] [Contract()] public class MOVHeader : EESObject { #region 字段属性 private String id; private String corpCode; private String code; private String moc; private String orderStatus; #endregion #region 映射属性 /// <summary> /// Id /// </summary> public virtual String Id { get { return id; } set { if ((id != value)) { this .OnPropertyChanging( " Id " ); id = value; this .OnPropertyChanged( " Id " ); } } } /// <summary> /// 企业编码 /// </summary> public virtual String CorpCode { get { return corpCode; } set { if ((corpCode != value)) { this .OnPropertyChanging( " CorpCode " ); corpCode = value; this .OnPropertyChanged( " CorpCode " ); } } } /// <summary> /// 单号 /// </summary> [Key()] public virtual String Code { get { return code; } set { if ((code != value)) { this .OnPropertyChanging( " Code " ); code = value; this .OnPropertyChanged( " Code " ); } } } /// <summary> /// 纸面单据号 /// </summary> public virtual String Moc { get { return moc; } set { if ((moc != value)) { this .OnPropertyChanging( " Moc " ); moc = value; this .OnPropertyChanged( " Moc " ); } } } /// <summary> /// 单据状态 /// </summary> public virtual String OrderStatus { get { return orderStatus; } set { if ((orderStatus != value)) { this .OnPropertyChanging( " OrderStatus " ); orderStatus = value; this .OnPropertyChanged( " OrderStatus " ); } } } #endregion private DataCollection<MOVDetail> detailCollection; public virtual DataCollection<MOVDetail> DetailCollection { get { return detailCollection; } set { detailCollection = value; } } private DataCollection<MOVData> movDataCollection; public virtual DataCollection<MOVData> MOVDataCollection { get { return movDataCollection; } set { movDataCollection = value; } } } }
DTL
1 namespace eWMS.Data 2 { 3 using System; 4 using System.Collections.Generic; 5 using EES.Common; 6 using EES.Common.Data; 7 using EES.Common.Model; 8 using System.ComponentModel; 9 10 11 [EESData( " 单明细 " )] 12 [Contract()] 13 public class MOVDetail : EESObject 14 { 15 16 #region 字段属性 17 private String id; 18 19 private String corpCode; 20 21 private String ordCode; 22 23 private Int32 rowNo; 24 25 private String rowStatus; 26 27 28 #endregion 29 30 #region 映射属性 31 /// <summary> 32 /// Id 33 /// </summary> 34 [Key()] 35 public virtual String Id 36 { 37 get 38 { 39 return id; 40 } 41 set 42 { 43 if ((id != value)) 44 { 45 this .OnPropertyChanging( " Id " ); 46 id = value; 47 this .OnPropertyChanged( " Id " ); 48 } 49 } 50 } 51 52 /// <summary> 53 /// 公司编码 54 /// </summary> 55 public virtual String CorpCode 56 { 57 get 58 { 59 return corpCode; 60 } 61 set 62 { 63 if ((corpCode != value)) 64 { 65 this .OnPropertyChanging( " CorpCode " ); 66 corpCode = value; 67 this .OnPropertyChanged( " CorpCode " ); 68 } 69 } 70 } 71 72 /// <summary> 73 /// 到货单号 74 /// </summary> 75 public virtual String OrdCode 76 { 77 get 78 { 79 return ordCode; 80 } 81 set 82 { 83 if ((ordCode != value)) 84 { 85 this .OnPropertyChanging( " OrdCode " ); 86 ordCode = value; 87 this .OnPropertyChanged( " OrdCode " ); 88 } 89 } 90 } 91 92 /// <summary> 93 /// 行号 94 /// </summary> 95 public virtual Int32 RowNo 96 { 97 get 98 { 99 return rowNo; 100 } 101 set 102 { 103 if ((rowNo != value)) 104 { 105 this .OnPropertyChanging( " RowNo " ); 106 rowNo = value; 107 this .OnPropertyChanged( " RowNo " ); 108 } 109 } 110 } 111 112 /// <summary> 113 /// 行状态 114 /// </summary> 115 public virtual String RowStatus 116 { 117 get 118 { 119 return rowStatus; 120 } 121 set 122 { 123 if ((rowStatus != value)) 124 { 125 this .OnPropertyChanging( " RowStatus " ); 126 rowStatus = value; 127 this .OnPropertyChanged( " RowStatus " ); 128 } 129 } 130 } 131 132 133 #endregion 134 } 135 }
DATA
namespace eWMS.Data { using System; using System.Collections.Generic; using EES.Common; using EES.Common.Data; using EES.Common.Model; using System.ComponentModel; [EESData( " 编码 " )] [Contract()] public class MOVData : EESObject { #region 字段属性 private String id; private String ordCode; private String detailId; private String productCode; private String productSKU; private String code; #endregion #region 映射属性 /// <summary> /// Id /// </summary> [Key()] public virtual String Id { get { return id; } set { if ((id != value)) { this .OnPropertyChanging( " Id " ); id = value; this .OnPropertyChanged( " Id " ); } } } /// <summary> /// 单号 /// </summary> public virtual String OrdCode { get { return ordCode; } set { if ((ordCode != value)) { this .OnPropertyChanging( " OrdCode " ); ordCode = value; this .OnPropertyChanged( " OrdCode " ); } } } /// <summary> /// 明细Id /// </summary> public virtual String DetailId { get { return detailId; } set { if ((detailId != value)) { this .OnPropertyChanging( " DetailId " ); detailId = value; this .OnPropertyChanged( " DetailId " ); } } } /// <summary> /// 产品编码 /// </summary> public virtual String ProductCode { get { return productCode; } set { if ((productCode != value)) { this .OnPropertyChanging( " ProductCode " ); productCode = value; this .OnPropertyChanged( " ProductCode " ); } } } /// <summary> /// 产品SKU /// </summary> public virtual String ProductSKU { get { return productSKU; } set { if ((productSKU != value)) { this .OnPropertyChanging( " ProductSKU " ); productSKU = value; this .OnPropertyChanged( " ProductSKU " ); } } } /// <summary> /// 追踪码 /// </summary> public virtual String Code { get { return code; } set { if ((code != value)) { this .OnPropertyChanging( " Code " ); code = value; this .OnPropertyChanged( " Code " ); } } } #endregion } }
2、服务:或者说方法。
服务/方法
namespace eWMS.Service { using System; using System.Collections.Generic; using EES.Common; using EES.Common.Data; using EES.Common.Model; using EES.Common.Query; using EES.Common.Injectors.Handlers; using eWMS.Data; using T.BC.Service; [EESBO( " 移库单服务 " )] public class MOVHeaderService : MOVHeaderImp<MOVHeader> { [Operation(ScopeOption.Required)] public virtual void WriteTrans(MOVHeader h) { trans.Id = GUID.NewGuid(); trans.OrderCode = h.Code; trans.OrderType = typeof (MOVHeader).ToString(); trans.ProductCode = detail.ProductCode; trans.ProductSKU = detail.ProductSKU; trans.BatchCode = detail.BatchCode; trans.StoreCode = detail.StoreCode; trans.StoreZone = detail.StoreZone; trans.Qty = 0 ; trans.UOM = detail.UOM; trans.RowNo = detail.RowNo; trans.PackCode = detail.PackCode; getProxyTrans().Save(trans); } protected override void OnSaved(MOVHeader t) { getProxyMovDetail().SaveAll(t.DetailCollection); getProxyMovData().SaveAll(t.MOVDataCollection); base .OnSaved(t); } private MOVDetailService proxyMovDetail; private MOVDetailService getProxyMovDetail() { if (proxyMovDetail == null ) proxyMovDetail = Factory.getProxy<MOVDetailService> (); return proxyMovDetail; } private MOVDataService proxyMovData; private MOVDataService getProxyMovData() { if (proxyMovData == null ) proxyMovData = Factory.getProxy<MOVDataService> (); return proxyMovData; } private OrderTransactionService proxyTrans; private OrderTransactionService getProxyTrans() { if (proxyTrans == null ) proxyTrans = Factory.getProxy<OrderTransactionService> (); return proxyTrans; } } }
为什么叫方法或者服务呢,结合框架这些方法可以直接公开出来,可以远程访问的,不需要加额外的编码,另外流程引擎也可以直接调用这些服务的,然后流程引擎会组合成新的服务对客户端公开。对于事务的处理,有[Operation(ScopeOption.Required)]这一句就可以了,也可以通过配置文件加上去。异常可以通过服务直接抛到客户端,如果在事务内则整个参与事务的所有服务的事务全部回滚,可能会涉及到多个服务的嵌套调用以及递归调用,全部会回滚,所涉及到的数据库连接会自动释放掉,缓存清理掉。
3、客户端调用:已经到了界面显示层。对于Web客户端与Form客户端的调用一样,已经到了界面显示层了。
客户端调用
namespace eWMS.Forms { using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Windows.Forms; using EES.Common; using EES.Common.Data; using EES.Controls.Win; using EES.Common.Query; using EES.Common.MVC; using eWMS.Data; using eWMS.Service; [View( " 移动类型管理 " , EntryType.入口, " 移动类型 " , " 移动类型管理 " )] public sealed partial class BillTypeLForm : EForm { private BillTypeService proxy; public BillTypeLForm() { this .InitializeComponent(); // 绑定数据错误不提示 this .gridView.DataError += GrivViewDataError; this .gridView.CellValidated += GrivViewCellValidated; // 数据源 this .gridView.DataSource = this .bindingSource; // this .bindingSource.AddingNew += AddingNew; this .Input = new DataCollection<BillType> (); DataLoad(); } private BillTypeService getProxy() { if (proxy== null ) proxy =Factory.getProxy<BillTypeService> () ; return proxy; } private void AddingNew( object sender, System.ComponentModel.AddingNewEventArgs e) { BillType obj = Factory.Create<BillType> (); // 更多初始化 // e.NewObject = obj; } /// <summary> /// 刷新 /// </summary> [Func( " 刷新 " , Ordinal= 10 )] public void DataLoad() { this .Input = this .getProxy().FindAll(); } [Func( " 删除 " ,Ordinal= 35 )] public void DataDelete() { BillType data = this .Current as BillType; if (data == null ) throw new EESException( " 请选择要删除的数据 " ); if (MessageBox.Show( " 确定要删除数据吗? " , " 删除数据 " , MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == System.Windows.Forms.DialogResult.OK) { if (data.DataState != DataState.Created) { this .getProxy().Delete(data); } data.DirectRemove(); MessageBox.Show( " 删除 成功 " ); } } [Func( " 保存 " , Ordinal= 30 )] public void DataSave() { object data = this .Input; if (data is DataCollection<BillType> ) { this .Input = this .getProxy().SaveAll(((DataCollection<BillType> )(data))); MessageBox.Show( " 保存 成功 " ); } } /// <summary> /// 查询 /// </summary> [Func( " 查询 " , Ordinal = 40 )] public void DataFind() { BillTypeFForm form = new BillTypeFForm(); form.OnOK += delegate ( object sender, TEventArgs<DataCollection<BillType>> e){ this .Input= e.Data;}; form.ShowDialog(); } } }
还没有说完,后面再继续吧
有不到之处,请大家批评指正……
谢谢!
标签: .NET , C# , SOA , DDD , 领域模型 , 架构
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息