面向领域开发示例
最近面向领域的知识挺火,我也来插一腿。说说我平时做项目时候用到的开发方式,以下代码为伪代码,主要展示一下我现在的开发方式供大家讨论,系统中不考虑持久、UI、AOP和IOC等方面内容。
说到.NET社区的“开发方式”就不得不提一下Petshop了,可以说Petshop真是影响了.NET的一代人。以至于三层成了.NET这边流行的标准的开发方式。但是要说一下Petshop是数据库驱动业务核心的过程式开发,也正是它也毒害了一批人。
下面我们以一个最常见也是大家比较熟的下定单来简单看一下。首先是系统的几个领域类:
应牧章的要求,加上类之间的关系:这里的关系很简单,Order与OrderItem是一对多关系;OrderItem与Product是一对多关系;定单提交业务Order对象是个“根”
我也就不解释了,发下几个类的代码里面有注解。大家看一下就明白了。这里我们要强调的是类关系,还有一个根的概念。每个业务流都会有一个根。而根不是整个系统唯一确定的是而是根据对象在哪个业务范围内而决定的。显然这里面是Order定单对象。
/// 商品
///
public class Product
{
///
/// 键
///
public int SysNo { get ; set ; }
///
/// 商品名称
///
public string ProductName { get ; set ; }
///
/// 商品价格
///
public decimal ProductPrice { get ; set ; }
private Category _category;
///
/// 所属分类
///
public Category Category
{
get { return _category; }
set { _category = value; }
}
}
/// 定单
///
public class Order
{
#region 属性
///
/// 键
///
public int SysNo { get ; set ; }
///
/// 定单ID
///
public string OrderId { get ; set ; }
///
/// 定单金额
///
public decimal OrderAmount { get ; set ; }
///
/// 运费金额
///
public decimal ShipAmount { get ; set ; }
///
/// 支付金额
///
public decimal PayAmount { get ; set ; }
///
/// 定单状态
///
public int OrderStatus { get ; set ; }
///
/// 定单时间
///
public DateTime OrderTime { get ; set ; }
private List < OrderItem > _orderItems = new List < OrderItem > ();
///
/// 定单明细
///
public List < OrderItem > OrderItems
{
get { return _orderItems; }
set { _orderItems = value; }
}
#endregion
#region 方法
///
/// 计算定单金额
///
///
public decimal GetOrderAmount()
{
decimal amount = 0 ;
// 计算商品金额
foreach (var item in OrderItems)
{
amount += item.GetAmount();
}
// 加上运费
amount += ShipAmount;
return amount;
}
#endregion
}
/// 定单明细
///
public class OrderItem
{
#region 属性
///
/// 键
///
public int SysNo { get ; set ; }
private Product _product = new Product();
///
/// 购买商品
///
public Product Product
{
get { return _product; }
set { _product = value; }
}
///
/// 购买价格
///
public decimal ProductPrice { get ; set ; }
///
/// 购买数量
///
public int ProductQty { get ; set ; }
#endregion
#region 方法
///
/// 主算小计金额
///
///
public decimal GetAmount()
{
return this .ProductPrice * Convert.ToDecimal( this .ProductQty);
}
#endregion
}
/// 商品库存
///
public class Inventory
{
#region 属性
///
/// 键
///
public int SysNo { get ; set ; }
///
/// 更新键,用来保证更新的数据之前没有别人更新过。对应表的ModKey列,每更新一次更新Guid值
///
public Guid ModKey { get ; set ; }
private Product _product = new Product();
///
/// 商品
///
public Product Product
{
get { return _product; }
set { _product = value; }
}
///
/// 库存数量
///
public int Qty { get ; set ; }
#endregion
#region 方法
///
/// 验证库存是否有库存
///
///
public bool ValidInventory()
{
if ( this .Qty > 0 )
return true ;
else
return false ;
}
///
/// 减库存
///
///
public void MinusInventory( int qty)
{
if ( this .Qty < qty)
throw new Exception( " 库存不足! " );
this .Qty -= qty;
}
#endregion
}
接下来看看我这个伪代码的项目结构。做的很简单,简单明快的架构就是好的架构,不要把架构搞的很复杂。
Domain:领域层,里面放的是领域对象。这个层对其它层没有依赖
Repository:持久层,对领域对象进行持久。里面可以有级存和DB等。这个层依赖Domain
Services:应用程序服务,这个名字喜好着起吧。我们可以把它理解为业务逻辑层,当然这个业务逻辑是对一个功能更粗粒度的API。里面会调用依赖的领域对象进行业务组合达到业务需求。当然对对象的持久也是这里来做的。这个层依赖Domain和Repository
Web:UI层,调用Services进行业务处理,需要ViewModel(VO)我们可以加,我个人比较喜欢按需要创建ViewModel对象,不需要的没必要增加工作量。
基本流程就是
UI调用Serivices创建定单
Services调用Domain对象进行业务组合处理
最后Services调用Repository进行对象的持久
我们从上到下,首先是UI的控制器里面开始。生成定单
{
// 创建定单,生成定单必要数据收货信息,购物车信息等
Order order = new Order();
OrderService service = new OrderService();
service.OrderBuy(order); // 定单提交
return View();
}
然后是Services里面的方法
/// 定单提交
///
///
public void OrderBuy(Order order)
{
// 事务开始
order.OrderAmount = order.GetOrderAmount(); // 计算订单金额
// 更新库存
foreach (var item in order.OrderItems)
{
Inventory inventory = InventoryRepository.LoadInventory(item.Product); // 取得一个库存对象
inventory.MinusInventory(item.ProductQty); // 调用库存调用方法
InventoryRepository.SaveInventory(inventory); // 持久更新完的库存
}
OrderRepository.SaveOrder(order); // 持久化定单
// 事务结束
}
Repository里面的是伪代码,就不发了。
工作呢,写的很粗略。最后说一点,其实面向领域开发通过这些代码是看不出本质的。我在这总结一下。
把你的开发关注点提到类(对象)上来,不要把关注点放到数据库(表)上。
PS:我也是存储过程(业务)反对派