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://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息