.NET应用架构设计—工作单元模式(摆脱过程式代码的重要思想,代替DDD实现轻量级业务)

简介: 阅读目录: 1.背景介绍 2.过程式代码的真正困境 3.工作单元模式的简单示例 4.总结 1.背景介绍 一直都在谈论面向对象开发,但是开发企业应用系统时,使用面向对象开发最大的问题就是在于,多个对象之间的互操作需要涉及数据库操作。

阅读目录:

  • 1.背景介绍
  • 2.过程式代码的真正困境
  • 3.工作单元模式的简单示例
  • 4.总结

1.背景介绍

一直都在谈论面向对象开发,但是开发企业应用系统时,使用面向对象开发最大的问题就是在于,多个对象之间的互操作需要涉及数据库操作。两个业务逻辑对象彼此之间需要互相调用,如果之间的互相操作是在一个业务事务范围内的,很容易完成,但是如果本次业务逻辑操作涉及到多个业务对象一起协作完成时问题就来了。

在以往,我们使用过程式的代码(事务脚本模式),将所有与本次业务事务范围内相关的所有逻辑都写在一个大的代码中,就算你适当的提取重复代码,效果也不大,因为你永远都摆脱不了夸多个对象互相操作的困境。如何确认你是否在这个困境中,你只要看你的所有事务操作的入口都只有一个业务方法。比如当你添加一个订单的时候,你同时将订单跟随的商品都一起在“添加订单”的方法中处理的,而不是在另外一个“添加订单商品”的方法中,这两个方法位于不同的表模块类中。

本章将介绍一个模式,此模式专门用来在开发企业应用系统时,协调多个业务对象在一个业务事务范围内,保证一个完整的事务。

2.过程式代码的困境

其实开发应用系统与开发某个框架或者组件之间的最大区别就是需要考虑数据的持久化,而持久化的逻辑也是和业务逻辑息息相关的,某个方法的最后动作就有可能是添加一行数据或者更新一个字段。而非应用系统的代码往往在最后的时候才去统一刷新最终的持久化文件,而且此类程序很少存在事务性数据操作。就算有,使用内存事务处理也是比较简单的,不需要考虑那么多的服务端的事情。

我之前也写过很多组件、框架,虽然谈不上什么复杂的东西,但是给我的经验和感悟就是,如何将其细致的设计粒度用在企业应用系统中,如何进行复杂而细致的OO设计开发。其实,如果我们不能够打破过程式代码的格局,那么看再多的OO知识也是心有余而力不足,反而会让你产生很多负面的情绪(因为我有过这个经历)。

其实我们还是缺少正确的方法而已,本文中UnitOfWork模式将帮助我们走出过程式的业务逻辑,走向起码的面向对象开发。有了UnitOfWork你可以随意使用Table module 、Activa Record、Domin Driven 模式,而且你可以根据自己的项目需要将其在大的布局上进行SOA划分(CQRS),让各个模式在各自适合的场景中发挥极致。

3.工作单元模式的简单示例

这里我们依然使用简单的订单购物业务作为示例来讲,毕竟大家都懂得这部分的的业务概念。本实例业务层使用Active Record模式。

 1 namespace OrderManager.Business
 2 {
 3     using System.Collections.Generic; 
 4 
 5     public partial class Order
 6     {
 7         public long OId { get; set; } 
 8 
 9         public List<OrderProducts> Products { get; set; }
10     }
11 }

Order活动记录对象的字段部分。

 1 namespace OrderManager.Business
 2 {
 3     public partial class Order
 4     {
 5         public bool CheckOrder()
 6         {
 7             //执行部分业务验证工作
 8             if (this.OId <= 0) return false; 
 9 
10             return true;
11         }
12     }
13 }

Order活动记录对象主体,纯粹为了演示而用,包含了一个简单的判断业务逻辑。

 1 namespace OrderManager.Business
 2 {
 3     public partial class OrderProducts
 4     {
 5         public long OrderId { get; set; } 
 6 
 7         public long PId { get; set; } 
 8 
 9         public float Price { get; set; }
10     }
11 }

订单商品部分字段。

 1 namespace OrderManager.Business
 2 {
 3     public partial class OrderProducts
 4     {
 5         public bool CheckProducts()
 6         {
 7             //执行部分业务验证工作
 8             if (this.OrderId <= 0) return false; 
 9 
10             return true;
11         }
12     }
13 }

每一个商品都包含了自己的逻辑验证。

我们接着看一下应用层入口方法是如何协调两个活动记录对象之间的业务操作和数据存储的。

 1 namespace OrderManager
 2 {
 3     using OrderManager.Business;
 4     using OrderManager.DataSource; 
 5 
 6     public class OrderManagerController : ControllerBase
 7     {
 8         public bool AddOrder(Order order)
 9         {
10             using (UnitOfWork unitOfWork = new UnitOfWork())
11             {
12                 order.CheckOrder();//执行业务检查 
13 
14                 order.Products.ForEach(item =>
15                 {
16                     item.CheckProducts();//执行每个活动记录对象的业务检查,这里也可以使用表模块来处理。
17                 }); 
18 
19                 OrderGateway orderGateway = new OrderGateway(unitOfWork);
20                 var orderDbResult = orderGateway.AddOrder(order);//第一个数据库表操作 
21 
22                 OrderProductsGateway productGateway = new OrderProductsGateway(unitOfWork);
23                 var productDbResult = productGateway.AddOrderProducts(order.Products);//第二个数据库表操作 
24 
25                 if (orderDbResult && productDbResult)
26                 {
27                     if (unitOfWork.Commit())
28                     {
29                         this.SendOrderIntegrationMssage(order);//发送成功集成订单消息 
30 
31                         return true;
32                     } 
33 
34                     this.PushOrderProcessQueue(order);//将本次订单发送到处理队列中
35                     return false;
36                 } 
37 
38                 this.LogBusinessException(order);//记录一个业务处理异常LOG,以备排查问题。
39                 return false;
40             }
41         }
42     }
43 }

为了简单演示示例,我直接使用实例化的方式来构造数据访问对象,实际使用时可以使用IOC工具来动态注入。

我们接着看一下数据层代码,数据层我使用表入口模式。

 1 namespace OrderManager.DataSource
 2 {
 3     public abstract class GatewayBase
 4     {
 5         protected UnitOfWork UnitOfWork { get; private set; } 
 6 
 7         public GatewayBase(UnitOfWork unit)
 8         {
 9             this.UnitOfWork = unit;
10         } 
11 
12         public bool Commit()
13         {
14             return this.UnitOfWork.Commit();
15         } 
16 
17         public void Rollback()
18         {
19             this.UnitOfWork.Rollback();
20         }
21     }
22 }

这是一个表入口基类。

 1 namespace OrderManager.DataSource
 2 {
 3     using OrderManager.Business; 
 4 
 5     public class OrderGateway : GatewayBase
 6     {
 7         public OrderGateway(UnitOfWork unit) : base(unit) { } 
 8 
 9         public bool AddOrder(Order order)
10         {
11             //这里可以使用你所熟悉的拼接SQL的方式直接操作数据库,而不需要ORM。
12             return true;
13         }
14     }
15 }
 1 namespace OrderManager.DataSource
 2 {
 3     using OrderManager.Business;
 4     using System.Collections.Generic; 
 5 
 6     public class OrderProductsGateway : GatewayBase
 7     {
 8         public OrderProductsGateway(UnitOfWork unit) : base(unit) { } 
 9 
10         public bool AddOrderProducts(List<OrderProducts> products)
11         {
12             //这里可以使用你所熟悉的拼接SQL的方式直接操作数据库,而不需要ORM。
13             return true;
14         }
15     }
16 }

这是两个表入口对象,其实这部分代码是大家都比较熟悉的,所以我这里省略了,你可以直接拼接SQL语句来插入数据库。

 1 namespace OrderManager.DataSource
 2 {
 3     using System; 
 4 
 5     public class UnitOfWork : IDisposable
 6     {
 7         public void Dispose()
 8         {
 9             throw new NotImplementedException();
10         } 
11 
12         public bool Commit()
13         {
14             return true;
15         } 
16 
17         public void Rollback()
18         {
19             //
20         }
21     }
22 }

 

UnitOfWrok对象其实就是对数据库对象的System.Data.Common.DbConnection对象的封装,这里你可以使用你熟悉的方式来构造这个数据库连接对象和开启事务。

其实值得我们去欣赏的是应用控制器中的代码,在这里很协调的处理各个逻辑,最后记录下一些必要的日志和发送一些集成消息。你是不是发现你完全可以不使用DDD也可以处理部分业务系统了。

4.总结

活动记录模式+表入口模式+工作单元模式,其实我觉得可以很好的处理中小型业务逻辑,随着现在SOA化架构,很少再有多大的项目在一个解决方案里面。

最后还是那句话,提供一个参考资料,如果有兴趣可以进一步交流具体的设计,由于时间关系文章就到这里了,谢谢大家。

 

作者:王清培

出处:http://www.cnblogs.com/wangiqngpei557/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面

目录
相关文章
|
14天前
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
50 10
|
3天前
|
数据采集 JSON API
.NET 3.5 中 HttpWebRequest 的核心用法及应用
【9月更文挑战第7天】在.NET 3.5环境下,HttpWebRequest 类是处理HTTP请求的一个核心组件,它封装了HTTP协议的细节,使得开发者可以方便地发送HTTP请求并接收响应。本文将详细介绍HttpWebRequest的核心用法及其实战应用。
26 6
|
19天前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
35 0
|
19天前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
43 0
|
13天前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
21 7
|
11天前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
29 0
|
1月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
31 0
|
1月前
|
开发框架 前端开发 安全
ASP.NET MVC 如何使用 Form Authentication?
ASP.NET MVC 如何使用 Form Authentication?
|
1月前
|
开发框架 .NET
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
79 0
|
4月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
148 0

热门文章

最新文章