.NET应用架构设计—工作单元模式(摆脱过程式代码的重要思想,代替DDD实现轻量级业务)
阅读目录:
- 1.背景介绍
- 2.过程式代码的真正困境
- 3.工作单元模式的简单示例
- 4.总结
1.背景介绍
一直都在谈论面向对象开发,但是开发企业应用系统时,使用面向对象开发最大的问题就是在于,多个对象之间的互操作需要涉及数据库操作。两个业务逻辑对象彼此之间需要互相调用,如果之间的互相操作是在一个业务事务范围内的,很容易完成,但是如果本次业务逻辑操作涉及到多个业务对象一起协作完成时问题就来了。
在以往,我们使用过程式的代码(事务脚本模式),将所有与本次业务事务范围内相关的所有逻辑都写在一个大的代码中,就算你适当的提取重复代码,效果也不大,因为你永远都摆脱不了夸多个对象互相操作的困境。如何确认你是否在这个困境中,你只要看你的所有事务操作的入口都只有一个业务方法。比如当你添加一个订单的时候,你同时将订单跟随的商品都一起在“添加订单”的方法中处理的,而不是在另外一个“添加订单商品”的方法中,这两个方法位于不同的表模块类中。
本章将介绍一个模式,此模式专门用来在开发企业应用系统时,协调多个业务对象在一个业务事务范围内,保证一个完整的事务。
2.过程式代码的困境
其实开发应用系统与开发某个框架或者组件之间的最大区别就是需要考虑数据的持久化,而持久化的逻辑也是和业务逻辑息息相关的,某个方法的最后动作就有可能是添加一行数据或者更新一个字段。而非应用系统的代码往往在最后的时候才去统一刷新最终的持久化文件,而且此类程序很少存在事务性数据操作。就算有,使用内存事务处理也是比较简单的,不需要考虑那么多的服务端的事情。
我之前也写过很多组件、框架,虽然谈不上什么复杂的东西,但是给我的经验和感悟就是,如何将其细致的设计粒度用在企业应用系统中,如何进行复杂而细致的OO设计开发。其实,如果我们不能够打破过程式代码的格局,那么看再多的OO知识也是心有余而力不足,反而会让你产生很多负面的情绪(因为我有过这个经历)。
其实我们还是缺少正确的方法而已,本文中UnitOfWork模式将帮助我们走出过程式的业务逻辑,走向起码的面向对象开发。有了UnitOfWork你可以随意使用Table module 、Activa Record、Domin Driven 模式,而且你可以根据自己的项目需要将其在大的布局上进行SOA划分(CQRS),让各个模式在各自适合的场景中发挥极致。
3.工作单元模式的简单示例
这里我们依然使用简单的订单购物业务作为示例来讲,毕竟大家都懂得这部分的的业务概念。本实例业务层使用Active Record模式。
namespace OrderManager.Business
{
using System.Collections.Generic; public partial class Order
{
public long OId { get; set; } public List<OrderProducts> Products { get; set; }
}
}
Order活动记录对象的字段部分。
namespace OrderManager.Business
{
public partial class Order
{
public bool CheckOrder()
{
//执行部分业务验证工作
if (this.OId <= ) return false; return true;
}
}
}
Order活动记录对象主体,纯粹为了演示而用,包含了一个简单的判断业务逻辑。
namespace OrderManager.Business
{
public partial class OrderProducts
{
public long OrderId { get; set; } public long PId { get; set; } public float Price { get; set; }
}
}
订单商品部分字段。
namespace OrderManager.Business
{
public partial class OrderProducts
{
public bool CheckProducts()
{
//执行部分业务验证工作
if (this.OrderId <= ) return false; return true;
}
}
}
每一个商品都包含了自己的逻辑验证。
我们接着看一下应用层入口方法是如何协调两个活动记录对象之间的业务操作和数据存储的。
namespace OrderManager
{
using OrderManager.Business;
using OrderManager.DataSource; public class OrderManagerController : ControllerBase
{
public bool AddOrder(Order order)
{
using (UnitOfWork unitOfWork = new UnitOfWork())
{
order.CheckOrder();//执行业务检查 order.Products.ForEach(item =>
{
item.CheckProducts();//执行每个活动记录对象的业务检查,这里也可以使用表模块来处理。
}); OrderGateway orderGateway = new OrderGateway(unitOfWork);
var orderDbResult = orderGateway.AddOrder(order);//第一个数据库表操作 OrderProductsGateway productGateway = new OrderProductsGateway(unitOfWork);
var productDbResult = productGateway.AddOrderProducts(order.Products);//第二个数据库表操作 if (orderDbResult && productDbResult)
{
if (unitOfWork.Commit())
{
this.SendOrderIntegrationMssage(order);//发送成功集成订单消息 return true;
} this.PushOrderProcessQueue(order);//将本次订单发送到处理队列中
return false;
} this.LogBusinessException(order);//记录一个业务处理异常LOG,以备排查问题。
return false;
}
}
}
}
为了简单演示示例,我直接使用实例化的方式来构造数据访问对象,实际使用时可以使用IOC工具来动态注入。
我们接着看一下数据层代码,数据层我使用表入口模式。
namespace OrderManager.DataSource
{
public abstract class GatewayBase
{
protected UnitOfWork UnitOfWork { get; private set; } public GatewayBase(UnitOfWork unit)
{
this.UnitOfWork = unit;
} public bool Commit()
{
return this.UnitOfWork.Commit();
} public void Rollback()
{
this.UnitOfWork.Rollback();
}
}
}
这是一个表入口基类。
namespace OrderManager.DataSource
{
using OrderManager.Business; public class OrderGateway : GatewayBase
{
public OrderGateway(UnitOfWork unit) : base(unit) { } public bool AddOrder(Order order)
{
//这里可以使用你所熟悉的拼接SQL的方式直接操作数据库,而不需要ORM。
return true;
}
}
}
namespace OrderManager.DataSource
{
using OrderManager.Business;
using System.Collections.Generic; public class OrderProductsGateway : GatewayBase
{
public OrderProductsGateway(UnitOfWork unit) : base(unit) { } public bool AddOrderProducts(List<OrderProducts> products)
{
//这里可以使用你所熟悉的拼接SQL的方式直接操作数据库,而不需要ORM。
return true;
}
}
}
这是两个表入口对象,其实这部分代码是大家都比较熟悉的,所以我这里省略了,你可以直接拼接SQL语句来插入数据库。
namespace OrderManager.DataSource
{
using System; public class UnitOfWork : IDisposable
{
public void Dispose()
{
throw new NotImplementedException();
} public bool Commit()
{
return true;
} public void Rollback()
{
//
}
}
}
UnitOfWrok对象其实就是对数据库对象的System.Data.Common.DbConnection对象的封装,这里你可以使用你熟悉的方式来构造这个数据库连接对象和开启事务。
其实值得我们去欣赏的是应用控制器中的代码,在这里很协调的处理各个逻辑,最后记录下一些必要的日志和发送一些集成消息。你是不是发现你完全可以不使用DDD也可以处理部分业务系统了。
4.总结
活动记录模式+表入口模式+工作单元模式,其实我觉得可以很好的处理中小型业务逻辑,随着现在SOA化架构,很少再有多大的项目在一个解决方案里面。
最后还是那句话,提供一个参考资料,如果有兴趣可以进一步交流具体的设计,由于时间关系文章就到这里了,谢谢大家。
.NET应用架构设计—工作单元模式(摆脱过程式代码的重要思想,代替DDD实现轻量级业务)的更多相关文章
- .NET应用架构设计—工作单位模式(摆脱程序代码的重要思想,反击DDD)
阅读文件夹: 1.背景介绍 2.过程式代码的真正困境 3.工作单元模式的简单演示样例 4.总结 1.背景介绍 一直都在谈论面向对象开发.可是开发企业应用系统时.使用面向对象开发最大的问题就是在于,多个 ...
- [.NET领域驱动设计实战系列]专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
一.前言 在前面2篇博文中,我分别介绍了规约模式和工作单元模式,有了前面2篇博文的铺垫之后,下面就具体看看如何把这两种模式引入到之前的网上书店案例里. 二.规约模式的引入 在第三专题我们已经详细介绍了 ...
- [.NET领域驱动设计实战系列]专题四:前期准备之工作单元模式(Unit Of Work)
一.前言 在前一专题中介绍了规约模式的实现,然后在仓储实现中,经常会涉及工作单元模式的实现.然而,在我的网上书店案例中也将引入工作单元模式,所以本专题将详细介绍下该模式,为后面案例的实现做一个铺垫. ...
- 仓储(Repository)和工作单元模式(UnitOfWork)
仓储和工作单元模式 仓储模式 为什么要用仓储模式 通常不建议在业务逻辑层直接访问数据库.因为这样可能会导致如下结果: 重复的代码 编程错误的可能性更高 业务数据的弱类型 更难集中处理数据,比如缓存 无 ...
- MVC+EF 理解和实现仓储模式和工作单元模式
MVC+EF 理解和实现仓储模式和工作单元模式 原文:Understanding Repository and Unit of Work Pattern and Implementing Generi ...
- Contoso 大学 - 9 - 实现仓储和工作单元模式
原文 Contoso 大学 - 9 - 实现仓储和工作单元模式 By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Micros ...
- 演练5-8:Contoso大学校园管理系统(实现存储池和工作单元模式)
在上一次的教程中,你已经使用继承来消除在 Student 和 Instructor 实体之间的重复代码.在这个教程中,你将要看到使用存储池和工作单元模式进行增.删.改.查的一些方法.像前面的教程一样, ...
- 关于工作单元模式——工作单元模式与EF结合的使用
工作单元模式往往和仓储模式一起使用,本篇文章讲到的是工作单元模式和仓储模式一起用来在ef外面包一层,其实EF本身就是工作单元模式和仓储模式使用的经典例子,其中DbContext就是工作单元,而每个Db ...
- 重新整理 .net core 实践篇—————工作单元模式[二十六]
前言 简单整理一下工作单元模式. 正文 工作单元模式有3个特性,也算是其功能: 使用同一上下文 跟踪实体的状态 保障事务一致性 工作单元模式 主要关注事务,所以重点在事务上. 在共享层的基础建设类库中 ...
随机推荐
- ZOJ Problem Set - 1240 IBM Minus One
水题不解释,就是注意下格式,没输出一行字符串记得加一个空白行 #include <stdio.h> #include <string.h> int main() { ; ]; ...
- Oracle监控用户索引使用情况,删除无用索引
监控当前业务用户索引 一段时间后查询从未被使用的索引,删除无用索引 停止监控索引 1. 监控当前用户所有索引 得到监控所有索引的语句: select 'alter index ' || index_n ...
- 1Z0-053 争议题目解析470
1Z0-053 争议题目解析470 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 470.Which NLS parameter can be used to change the ...
- 【集合框架】JDK1.8源码分析之IdentityHashMap(四)
一.前言 前面已经分析了HashMap与LinkedHashMap,现在我们来分析不太常用的IdentityHashMap,从它的名字上也可以看出来用于表示唯一的HashMap,仔细分析了其源码,发现 ...
- 常用查找数据结构及算法(Python实现)
目录 一.基本概念 二.无序表查找 三.有序表查找 3.1 二分查找(Binary Search) 3.2 插值查找 3.3 斐波那契查找 四.线性索引查找 4.1 稠密索引 4.2 分块索引 4.3 ...
- Struts2 源码分析——配置管理之PackageProvider接口
本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 ...
- 创建ASP.NET Core MVC应用程序(6)-添加验证
创建ASP.NET Core MVC应用程序(6)-添加验证 DRY原则 DRY("Don't Repeat Yourself")是MVC的设计原则之一.ASP.NET MVC鼓励 ...
- Alg-4:算法可视化tiny库的实现
好吧,准备好好实践一下<算法>这本书的内容的,有点讨厌的是这本书是以Java作为示例语言的(好吧,我承认我有语言偏见,Java就是臭).其中有一个Std库,其中涉及到简化输入输出.随机数生 ...
- 【nodejs笔记2】认识express框架
app.js:启动文件,或者说入口文件package.json:存储着工程的信息及模块依赖,当在 dependencies 中添加依赖的模块时,运行 npm install,npm 会检查当前目录下的 ...
- JavaScript类属性
对象的类属性(class attribute)是一个字符串,用以表示对象的类型信息.ECMAScript3和ECMAScript5都未提供设置这个属性的方法,并只有一个间接的方法可以查询它.默认的to ...