.NET应用架构设计—表模块模式与事务脚本模式的代码编写
阅读目录:
- 1.背景介绍
- 2.简单介绍表模块模式、事务脚本模式
- 3.正确的编写表模块模式、事务脚本模式的代码
- 4.总结
1.背景介绍
要想正确的设计系统架构就必须能正确的搞懂每个架构模式的用意,而不是胡子眉毛一把抓。现在有一个现象是什么呢,项目的结构从表面上看是很不错,层分的很合理,其实对业务系统来说也就那么几种层设计方法,但是现在很多项目的逻辑架构的设计不是理想,有很多概念大家并不是很了解,当然也许每个人对技术的追求不同罢了。不管你追求不追求,事实我们还是要去往正确的方向努力才对的。
很多人包括我自己在内,都是写过很多年的过程式的代码,层对我当年来说就是个摆设而已,最典型的问题就是我们总是将表模块模式和事物脚本模式一起混着使用,什么意思呢,就是说我们都会使用一些代码生成器来根据数据库中的表来生成三层架构中的业务层和数据层,有些比较好的代码生成器也可以帮你把UI层中的部分视图也生成好,确实很强大,有些场合下这是一中最合适的过程。
但是现在的系统已经不在是那样的了,其中重要的一点就是业务复杂了,如果我们还稀里糊涂的编写着代码,最后只会成为你或者团队的技术债务。
2.简单介绍表模块模式、事务脚本模式
我们简单了解一下这里所谓的“表模块模式、”事务脚本模式“到底是什么样子的模式,最关键是你也许就知道了你目前所使用的业务层架构风格是什么模式,强调一下“表模块模式”、“事物脚本模式”都是业务层的构架模式。
表模块模式:
简单讲就是你数据库中的每个表对应着业务层中的一个对象定义,如果你有一个Product表,那么你在Business Layer中就有一个Product.cs文件,当然这不是绝对的,你也可以将库中的视图也定义一个类型,如,OrderProduct.cs,也是可以的。然后每个类中的所处理的逻辑都是跟这张表相关的,你在Product.cs中的代码就不要包含Order.cs中的代码了。目前来看是有的,因为现在大部分的项目都是在写着使用过程式的代码,也就是事物脚本模式,这样难免会将其他类型中的代码写到本类中。
事物脚本模式:
事务脚本模式就是过程式的代码,只不过它的指标就是每一个代码段独立完成一个业务单元,而不是到处都是过程代码,事物脚本模式还是很强调逻辑的统一性的。
使用此模式进行写代码时,你就不要单纯的使用数据库中的表名来定义业务类了,因为你是使用事务脚本模式的,你需要站在业务角度来规划你的业务层中大概包含哪些业务概念,然后使用这个业务概念来命名你的类。比如:UserOrder,UserOrder,类似这样的定义,当然这里只是个假设而已,你不要站在数据库的脚本来设计业务层就行了,这样你就起码不会使用到很细粒度的类型,哪怕下一次迭代进行重构也是可以的。
3.正确的编写表模块模式、事务脚本模式的代码
这篇文章的重点就是本节,我们将了解一下这两种模式的代码到底该如何编写。
说代码如何编写似乎有点让人觉得不是很礼貌,其实我们大部分的代码写的都是正确的,但是如果我们将有些代码稍微的罗一下位置就会明显不一样。
我们来看一个小例子,例子很简单,有两个类型Order、Product,用来完成一般的业务逻辑处理,我们通过不同的模式来看到底如何使用。
现在的事务脚本模式的代码:
namespace Business
{
public class Order
{
[Serializable]
public class OrderField
{
public string OId { get; set; } [NonSerialized]
public List<Product.ProductField> Products { get; set; }
} public OrderField Field { get; set; } public void AddOrder(OrderField orderField)
{
var sendMQOrders = new List<string>();//合格的产品ID集合 orderField.Products.ForEach(product =>
{
if (product.Price <= )//不满足条件
return; sendMQOrders.Add(product.PId);//满足条件
}); MqHelper.SendOrder(orderField, sendMQOrders);//发送订单数据实体到队列中
}
}
}
Order业务类中有一个添加Order的方法,在该方法中是一些简单的业务逻辑处理,判断了要添加的这个商品的价格是否大于20块钱。最后使用过滤下来的商品ID作为本订单的有效产品。
这就是我们目前使用的代码风格,这里有两个问题,第一:类的命名,Order的概念太大了,没有进行细化,显然不是按照事务脚本模式来进行设计的而是按照表模块方式进行划分的,还有如果我就算你是按照事物脚本模式来设计的,我就喜欢定义大的概念难道不对吗?也可以,但是在Order的定义里面明确使用了Product类型下的子类型,也就说这里出现了两个独立的业务范围,正常的理解肯定是按照表模块模式来设计的。第二:如果是按照表模块模式来设计的,因为根据这个对象的命名很明显是此模式,但是仔细阅读一下代码发现职责还是有点不清晰,在Order.AddOrder(OrderField orderField)方法中,有Product的逻辑在里面if (product.Price <= 20),当然这里是业务逻辑比较简单的情况下的,如果是业务比较复杂的时候,就会出现大量的代码在Order类中,到最后就会发现我们已经分不清此业务架构到底是使用何种模式来设计的。
我们有两个做法,第一个做法是:将其改成事务脚本模式,让类的命名和设计泛化,也就是说不要定义那么明显的数据库中的表名字,不要清晰的区分Order和Product两个职责。第二个做法是:将其改成表模块模式,将每个类型中的业务逻辑完全清晰化,将if (product.Price <= 20)提取到Product业务类中去,然后在应用控制器中先处理此逻辑后在调用Order.AddOrder(OrderField orderField)方法进行处理,这个方法里面只做跟当前类型相关的逻辑,参考的依据就是一旦你发现你所写的代码里出现了别的类型时,此时你应该告诉自己有可能这个地方需要重构职责。
两个方法来移动此逻辑:
1:(将if (product.Price <= 20)封进Price属性中)
namespace Business
{
public class Order
{
[Serializable]
public class OrderField
{
public string OId { get; set; } [NonSerialized]
public List<Product.ProductField> Products { get; set; }
} public OrderField Field { get; set; } public void AddOrder(OrderField orderField)
{
var sendMQOrders = new List<string>();//合格的产品ID集合 orderField.Products.ForEach(product =>
{
if (product.Price.IsValid()/*执行价格判断*/) return; sendMQOrders.Add(product.PId);//满足条件
}); MqHelper.SendOrder(orderField, sendMQOrders);//发送订单数据实体到队列中
}
}
}
此方法有个问题就是商品的价格没有收到订单的控制,这看具体的业务需要了,我只是个假设。
2:完全独立的过滤无效产品的方法
namespace Business
{
public class OrderApplicationController
{
public void SubmitOrder(Order.OrderField field)
{
field.Products = new Product().FilterProduct(field.Products);//先过滤 new Order().AddOrder(field);//在添加
}
}
}
我们先调用Product中的业务方法过滤无效的商品,然后在进行订单添加操作,这样我们就将各自的职责放到自己的位置去。
4.总结
还是那句话,这只是我学习过程中的一点小小的领悟,给大家一个参考的资料,希望对你有用,谢谢。
.NET应用架构设计—表模块模式与事务脚本模式的代码编写的更多相关文章
- Swing程序最佳架构设计—以业务对象为中心的MVC模式(转)
前言: 我打算写一系列关于Swing程序开发的文章.这是由于最近我在做一个Swing产品的开发.长期做JavaEE程序,让我有些麻木了.Swing是设计模式的典范,是一件优雅的艺术品,是一件超越时代的 ...
- 架构设计 | 基于Seata中间件,微服务模式下事务管理
源码地址:GitHub·点这里 || GitEE·点这里 一.Seata简介 1.Seata组件 Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata将为用 ...
- 框架计划随笔 三.EntityFramework在传统事务脚本模式下的使用
某个朋友问为什么不推首页或者允许评论,我说一直没怎么写博客,也习惯了先随便乱画再开始写文档,担心公开后一些不经意的"呓语“中得出的错误的结论会给别人错误的观点,所以这个系列只是当做熟悉写博客 ...
- .NET架构设计、框架设计系列文章总结
从事.NET开发到现在已经有七个年头了.慢慢的可能会很少写.NET文章了.不知不觉竟然走了这么多年,热爱.NET热爱c#.突然想对这一路的经历进行一个总结. 是时候开始下一阶段的旅途,希望这些文章可以 ...
- .NET应用架构设计—重新认识分层架构(现代企业级应用分层架构核心设计要素)
阅读目录: 1.背景介绍 2.简要回顾下传统三层架构 3.企业级应用分层架构(现代分层架构的基本演变过程) 3.1.服务层中应用契约式设计来解决动态条件不匹配错误(通过契约式设计模式来将问题在线下暴露 ...
- .NET应用架构设计—再次了解分层架构(现代企业应用分层架构核心设计元素)
阅读文件夹: 1.背景介绍 2.简要回想下传统三层架构 3.企业级应用分层架构(现代分层架构的基本演变过程) 3.1.服务层中应用契约式设计来解决动态条件不匹配错误(通过契约式设计模式来将问题在线下暴 ...
- .net架构设计读书笔记--第二章 第7节 神化般的业务层
一.编排业务逻辑的模式1. 事务脚本模式TS(The Transaction Script pattern ) TS模式概述 TS 鼓励你跳过任何的面向对象的设计,你直接到所需的用户操作的业务 ...
- .NET应用架构设计—工作单元模式(摆脱过程式代码的重要思想,代替DDD实现轻量级业务)
阅读目录: 1.背景介绍 2.过程式代码的真正困境 3.工作单元模式的简单示例 4.总结 1.背景介绍 一直都在谈论面向对象开发,但是开发企业应用系统时,使用面向对象开发最大的问题就是在于,多个对象之 ...
- .NET应用架构设计—工作单位模式(摆脱程序代码的重要思想,反击DDD)
阅读文件夹: 1.背景介绍 2.过程式代码的真正困境 3.工作单元模式的简单演示样例 4.总结 1.背景介绍 一直都在谈论面向对象开发.可是开发企业应用系统时.使用面向对象开发最大的问题就是在于,多个 ...
随机推荐
- SSH框架入门知识总结
chap01 领略清晰的MVC流程----Struts讲解技术资料.pdf 提取码:wwi5 chap02 体验控制反转的理念----Spring介绍技术文档.pdf 提取码:0mp3 ch ...
- 性能测试工具Locust
An open source load testing tool. 一个开源性能测试工具. define user behaviour with python code, and swarm your ...
- GIS项目中数据开源、工具开源、开发开源的解决方案
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 摆脱免费地图开发包的约束,拒绝商业地图软件的费用,高效.精确.完备是我 ...
- PHP中的魔术方法(2)
1.__get.__set这两个方法是为在类和他们的父类中没有声明的属性而设计的__get( $property ) 当调用一个未定义的属性时访问此方法__set( $property, $value ...
- DX9入门笔记1-D3D初始化
对3D编程期待已久,却一直叶公好龙浅尝辄止.近期在公司实习却无具体的工作安排,琢磨着学习个新的手艺,就又想起了3D Programming.这次从大名鼎鼎的龙书(Introduction to 3D ...
- 我的runtime学习笔记
0.简介: OC方法不同于C语言函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用. 至于其他理论上的东西不必讲太多,编程讲的就 ...
- 【原创】kafka admin源代码分析
admin包定义了命令行的一些实现 一.AdminOperationException.scala 一个异常类,表示执行admin命令时候抛出的异常 二.AdminUtils.scala admin一 ...
- Windows 10 密钥分享
Windows 10 Technical Preview for Enterprise:KEY:PBHCJ-Q2NYD-2PX34-T2TD6-233PKhttp://technet.microsof ...
- Oracle 英文 非标准格式 日期 格式化
最近在处理一张表的时候,需要按照日期排序,日期字段中日期的格式有两种. 格式一:07-Aug-2015 格式二:10/28/16 日期转化及格式化sql语句: select to_date('07-A ...
- linq lambda left join
//var list = table1.Join(table2, ee => ee.Id, ff => ff.table1_Id, (ee, ff) => new { ee, ff ...