.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/

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

目录
相关文章
|
10天前
|
C# Windows
一款.NET开源、简洁易用的Windows桌面小说阅读应用
一款.NET开源、简洁易用的Windows桌面小说阅读应用
|
12天前
|
开发框架 物联网 测试技术
【专栏】.NET 开发:打造领先应用的基石
【4月更文挑战第29天】本文探讨了.NET开发框架为何成为构建领先应用的首选。高性能与稳定性是.NET的核心优势,它采用先进的技术和优化策略,如.NET Core的轻量级设计和JIT/AOT编译模式。跨平台兼容性让开发者能用相同代码库在不同操作系统上构建应用。现代化的开发体验,如C#语言的创新特性和Visual Studio的强大工具,提升了开发者生产力。丰富的生态系统和广泛支持,包括庞大的开发者社区和微软的持续投入,为.NET提供了坚实后盾。
|
12天前
|
机器学习/深度学习 人工智能 Cloud Native
【专栏】洞察.NET 技术的前沿应用
【4月更文挑战第29天】本文探讨了.NET技术的前沿应用,包括.NET Core的跨平台崛起、云原生及AI/机器学习领域的整合。.NET Core支持多平台运行,开源社区的参与促进了其快速发展和性能优化。Xamarin与.NET MAUI助力跨平台移动应用和统一界面开发,而云原生应用借助.NET Core与Azure云服务得以轻松构建和部署。此外,ML.NET和TensorFlow.NET为.NET开发者提供了机器学习和深度学习工具,推动智能应用和边缘计算的创新。.NET技术正持续演进,引领软件开发新趋势。
|
12天前
|
人工智能 物联网 开发者
【专栏】探究.NET 技术的创新应用
【4月更文挑战第29天】本文探讨了.NET技术的最新进展和创新应用,包括.NET 5及后续版本的统一平台、性能提升、跨平台支持、云集成优化和开源社区的贡献。在创新应用场景中,重点介绍了微服务架构、物联网、AI、游戏开发和移动应用。未来,.NET将持续优化性能,深化云原生应用,集成新兴技术,扩大社区生态,并促进相关教育和培训。开发者应把握.NET技术的潜力,积极参与其发展,创造更多创新软件产品。
|
12天前
|
安全 Linux API
【专栏】.NET 开发:打造卓越应用的秘诀
【4月更文挑战第29天】本文介绍了.NET技术的起源、核心特性和应用场景,揭示了其打造卓越应用的秘诀。自2002年推出,.NET历经发展,现支持跨平台,包括.NET Core和.NET 5。其核心特性包括:跨平台兼容性、面向对象编程、内置安全性和高效性能。丰富的类库、强大的开发工具、简洁的语言语法以及活跃的社区支持,使.NET成为构建高效、安全应用的理想选择。随着技术进步,.NET将持续赋能开发者创造更多可能性。
|
4月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
46 0
|
16天前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
22 0
|
2月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
32 0
|
2月前
mvc.net分页查询案例——mvc-paper.css
mvc.net分页查询案例——mvc-paper.css
5 0
|
2月前
|
开发框架 前端开发 .NET
C# .NET面试系列六:ASP.NET MVC
<h2>ASP.NET MVC #### 1. MVC 中的 TempData\ViewBag\ViewData 区别? 在ASP.NET MVC中,TempData、ViewBag 和 ViewData 都是用于在控制器和视图之间传递数据的机制,但它们有一些区别。 <b>TempData:</b> 1、生命周期 ```c# TempData 的生命周期是短暂的,数据只在当前请求和下一次请求之间有效。一旦数据被读取,它就会被标记为已读,下一次请求时就会被清除。 ``` 2、用途 ```c# 主要用于在两个动作之间传递数据,例如在一个动作中设置 TempData,然后在重定向到另
110 5