关于业务逻辑和对象行为的思考
2014-04-28 22:23 兜兜里没有药 阅读(1162) 评论(3) 编辑 收藏 举报 为什么今天写这篇博客呢?
主要是因为以前使用三层模式时,经常会出现一种问题,就是BLL业务逻辑层没有什么业务逻辑,经常被用作表现层和DAL数据层的过度,总感觉有跟没有都一样......,Model也只是用数据传输的载体,怎么就觉得跟OOP思想扯不上关系,在我理解的OOP思想里,对象应该是有生命力的才对啊!!
后来学习了领域驱动设计,使用EntityFramework CodeFirst重量级ORM,在看资料的过程中,心理可美滋滋的了,应为我觉得这才是我心目中的OOP思想,领域对象有属性,有行为,有事件等等,很具有生命力;随着项目运用,慢慢开始偏离轨道了,如果人生就是一个茶几,我就是上面的杯具,为毛还是和三层时的一样,对象的生命力在哪里,((‵□′)).....
作为一个想成为架构师的码农来说,不行,绝对不行,我必须反思,反省,反.......
下面是经过左思右想的一点点成果,和大家分享下,希望能得到一些建议。
首先先用以前的写法写一个非常简单下单处理,代码如下:
对象模型:
1.User用户模型
1 public class User 2 : EntityBase 3 { 4 public string UserName { get; set; } 5 6 public string Email { get; set; } 7 8 public string Telephone { get; set; } 9 10 public Account Account { get; set; } 11 }
2.Account账户模型
1 public class Account 2 : EntityBase 3 { 4 public Guid UserId { get; set; } 5 6 public double Amount { get; set; } 7 8 public DateTime ActiveLastTime { get; set; } 9 }
3.Product产品模型
1 public class Product 2 : EntityBase 3 { 4 public string Name { get; set; } 5 6 public double Price { get; set; } 7 8 public double Description { get; set; } 9 10 public int Number { get; set; } 11 12 public bool IsPutaway { get; set; } 13 }
4.Order订单模型
public class Order : EntityBase { public string OrderNo { get; set; } public Guid ProductId { get; set; } public string ProductName { get; set; } public double Price { get; set; } public Guid UserId { get; set; } public DateTime CreateTime { get; set; } }
生成订单的处理逻辑
1 public class OrderAppService 2 { 3 public void Create(Order order) 4 { 5 if (order == null) 6 throw new ArgumentNullException("order"); 7 8 order.OrderNo = NewOrderNo(); 9 order.CreateTime = DateTime.Now; 10 11 //进行创建订单 12 } 13 14 private string NewOrderNo() 15 { 16 return ""; 17 } 18 }
表现层进行生成订单
1 public JsonResult CreateOrder(Guid productId) 2 { 3 //这里数据获取就省略了哈... 4 Web.Domain.UserModule.User user = null; 5 Web.Domain.ProductModule.Product product = null; 6 7 if (product.Number > 0) 8 return Json(new { success = false, error = string.Format("\"{0}\" 该产品库存不足,无法购买。", product.Name) }); 9 10 if (!product.IsPutaway) 11 return Json(new { success = false, error = string.Format("\"{0}\" 该产品还没有上架或者暂时下架,如有问题请咨询客服人员。", product.Name) }); 12 13 if (user.Account.Amount < product.Price) 14 return Json(new { success = false, error = "您的金额不足,请先进行充值,再购买。" }); 15 16 Order order = new Order 17 { 18 ProductId = product.Id, 19 ProductName = product.Name, 20 Price = product.Price, 21 UserId = user.Id 22 }; 23 24 try 25 { 26 Web.Application.OrderModule.OrderAppService service = new Web.Application.OrderModule.OrderAppService(); 27 service.Create(order); 28 } 29 catch 30 { 31 return Json(new { success = false, error = "系统出错了,请稍后在进行购买。" }); 32 } 33 return Json(new { success = true }); 34 }
OK,以前的写法就是这样,不知道有没有人和我写的差不多,(●'◡'●)....
下面是我根据自己的思路做的一些改进,同时引进前面一篇《提升Boolean和out相结合的用户体验》中的Can状态对象。
首先意识到的是表现层的CreateOrder下有很多订单处理的业务逻辑,应该放置在业务逻辑OrderAppService中,代码等下贴出。
再观察业务逻辑中的3个if,我觉得应该以拟人的方式进行思考,如下,
1.product.Number < 0 : 我们应该问产品:"你现在还有库存吗?" 产品做出回答:"没有!";产品做出的回答属于产品的一个行为,那我觉得应该给它定义一个行为:Can HasNumber();
2.!product.IsPutaway : 问:"你现在什么状态我能购买吗?" 答:"下架中,不能购买!";虽然这里只有上下架的状态,后期可能会拓展,所有也给他定义一个行为:Can IsEnabled();
3.个人觉得这两个if都是询问产品能不能买,可能以后还会有其他限制,所有我觉得应该让产品直接回答我们能不能购买,让它自己一次性检查:Can CanBuying();
4.User.Account.Amount < product.Price:这个应该问用户:”我需要这么多的钱你有吗?“,用户说:”没有!“,Can HasMoney(double amount);
下面来看看改造后的模型:
public class Product : EntityBase { public string Name { get; set; } public double Price { get; set; } public double Description { get; set; } public int Number { get; set; } public bool IsPutaway { get; set; } /// <summary> /// 有没有库存 /// </summary> /// <returns></returns> public Can HasNumber() { if (this.Number < 0) return string.Format("\"{0}\" 该产品库存不足,无法购买。", this.Name); return true; } /// <summary> /// 当前状态是否支持购买 /// </summary> /// <returns></returns> public Can IsEnabled() { if (!this.IsPutaway) return string.Format("\"{0}\" 该产品还没有上架或者暂时下架,如有问题请咨询客服人员。", this.Name); return true; } /// <summary> /// 当前产品是否可以购买 /// </summary> /// <returns></returns> public Can CanBuying() { var hasNumber = this.HasNumber(); if (!hasNumber) return hasNumber; var isEnabled = this.IsEnabled(); if (!isEnabled) return isEnabled; return true; } } public class User : EntityBase { public string UserName { get; set; } public string Email { get; set; } public string Telephone { get; set; } public Account Account { get; set; } /// <summary> /// 是否有指定额度的钱 /// </summary> /// <param name="amount">指定额度的钱</param> public Can HasMoney(double amount) { if (this.Account == null) throw new MemberAccessException("账户未被实例化"); return this.Account.HasMoney(amount); } } public class Account : EntityBase { public Guid UserId { get; set; } public double Amount { get; set; } public DateTime ActiveLastTime { get; set; } /// <summary> /// 是否有指定额度的钱 /// </summary> /// <param name="amount">指定额度的钱</param> public Can HasMoney(double amount) { if (this.Amount < amount) return "您的金额不足,请先进行充值,再购买。"; return true; } }
public class Order : EntityBase { public Order(Guid productId, string productName, double price, Guid userId) { this.ProductId = productId; this.ProductName = productName; this.Price = price; this.UserId = userId; this.CopyToNewOrder(); } public string OrderNo { get; private set; } public Guid ProductId { get; set; } public string ProductName { get; set; } public double Price { get; set; } public Guid UserId { get; set; } public DateTime CreateTime { get; private set; } /// <summary> /// 拷贝为新创建的订单 /// </summary> public void CopyToNewOrder() { this.OrderNo = this.NewOrderNo(); this.CreateTime = DateTime.Now; } private string NewOrderNo() { return ""; } }
然后我觉得应该学习下CQRS查询与命令分离的思路,为业务逻辑创建判断逻辑的统一存储如下:
1 public static class OrderAppLogic 2 { 3 /// <summary> 4 /// 是否可以创建订单 5 /// </summary> 6 /// <param name="user">用户</param> 7 /// <param name="product">产品</param> 8 /// <returns></returns> 9 public static Can CanCreateOrder(User user, Product product) 10 { 11 Can canCreateOrder = true; 12 13 if(!(canCreateOrder = product.CanBuying())) 14 return canCreateOrder; 15 16 if(!(canCreateOrder = user.HasMoney(product.Price))) 17 return canCreateOrder; 18 19 return canCreateOrder; 20 } 21 }
OrderAppService 就挺简单的了
1 public Can Create(User user, Product product) 2 { 3 if (user == null) 4 throw new ArgumentNullException("user"); 5 6 if (product == null) 7 throw new ArgumentNullException("product"); 8 9 Can flag = true; 10 11 if (!(flag = OrderAppLogic.CanCreateOrder(user, product))) 12 return flag; 13 14 Order order = new Order(product.Id, product.Name, product.Price, user.Id); 15 16 //进行创建 17 18 return flag; 19 }
表现层代码就更简单了
1 public JsonResult CreateOrder(Guid productId) 2 { 3 //这里数据获取就省略了哈... 4 Web.Domain.UserModule.User user = null; 5 Web.Domain.ProductModule.Product product = null; 6 7 try 8 { 9 Web.Application.OrderModule.OrderAppService service = new Web.Application.OrderModule.OrderAppService(); 10 var result = service.Create(user, product); 11 return Json(new { success = (bool)result, error = result.Error }); 12 } 13 catch 14 { 15 return Json(new { success = false, error = "系统出错了,请稍后在进行购买。" }); 16 } 17 }
目录结构:
好了,终于写完了,大家要是如果有什么建议和意见,欢迎积极评论!如果您觉得还不错,帮忙点击下推荐呗!!