项目架构开发:服务层(下)

之前我们已经完成了服务层,因为当时展现层还没有出来,所以只做了简单介绍。传送门:项目架构开发:服务层(上)

这次我们通过一个维护系统用户的场景来介绍一下服务层真正的设计用意。

1、新增用户场景

新增用户可能会有以下步骤

实现以上需求,开发人员一般情况下可能就是以上 蓝红黑紫绿 几种选择

1、有些写在Controllers、有些写在Application

2、都写在Controllers或都写在Application

3、有些写在DAL层、甚至存储过程

特别是新手以及那些拿来主义的人,他们不会花更多时间去思考。

分不清各层的职责以及界限,迷信那句:不管黑猫白猫,抓到老鼠就是好猫

最后造成劣质的代码满天飞,到处(无论哪个公司或项目)都充斥着这种随心所欲的代码

分层已经失去意义,因为整个系统在局外人看来已经只有一层,那就是业务逻辑层,上至html、下至数据库存储过程

都充满业务逻辑。联想一下,这只是用户注册而已,还有更加复杂的业务场景还没说出来。不知道最后会怎样

这样的系统已经完完全全一团面条了,后续维护已经与个人技能无关了,就算请一线的NB程序员,我想他能做的只是摇头而已

好了,吐槽了一下现状(不管你们同不同意,反正我是不会收回的,哈)

有没有方法解决上面这种窘境呢,当然是有的,比如我就觉得那种蓝色的方案勉强还可以

在UI层解决数据合法性校验,在逻辑层完成功能性问题,分工还算明确,这种方案对付一般的简单业务场景是足够了

但是如果业务场景足够复杂,那就不行了,因为这会造成2个明显的问题

1、UI层不够薄,这层只是调用后台的方法而已,按道理校验的事情是无需管的(我说的是cs层的校验,不是js)

这就像去银行办业务,用户不管钱假不假(因为可能不知道),够不够(可能也不知道,比如他想存一分钱)

这些银行员工告诉他就可以了

2、业务逻辑不能重用,比如场景1要求注册用户需要完成1-4项,场景2要求完成2-5项,场景3、场景4。。。。

这就蛋疼了,我们要为每一个场景实现校验到存储的所有过程,代码冗余很大,到处都是相似的代码。

但是有些小聪明的开发就说了,难道我不能再一个方法里边写if else switch码?那样不就可以兼容其他场景了?

这就是所谓的抓到老鼠就是好猫了,从此以后整个项目就开始堕落,前面一拨人胡乱搞完拍拍屁股走了,

后边接手的也只能往上边扔更多的扭纹柴了,只到开始改动一个逗号“,”,都会影响其他功能,项目已经彻底走不下去了

那怎么办?用老板的方法:很简单嘛,项目停掉暂且用着,然后招一批新人从新开发系统,边上边用。把业务都转移到新系统上

然后新的开发做完系统(可能还没到上线)就拍拍屁股走了,后边的接手的人。。。。然后一个循环的怪圈就开始了。。。

又扯远了。。。

话题转回来,这么样解决这种窘境,比较合适的业界比较推崇的方法就是加入服务层,来看看加入服务层后的逻辑分层

2、加入服务层后的逻辑分层

如何实施呢?我的想法是,UI层只做调用的操作,在构造函数中注入service或直接在Action中调用远程服务方法就好了

当然也可以做一些比如String.IsNullOrEmpty、“value”.ToInt(1);的操作,不过这并不代表服务层就不需要做

服务层还是需要重复校验输入参数是否合法的,所以上边的操作基本没啥卵用,因为服务层从设计角度来说他是不信任任何输入的

不然单元测试都过不去,那服务层到底做些什么呢?可以大概分为以下几个方面

1、解耦UI层与业务逻辑的强耦合

2、较少业务逻辑调用次数

3、层次更加分明,代码简洁

4、部署灵活,以后做集群很方便

如果没有服务层,UI层Controller是怎么写的?大概如下:

RegisterController.cs

 1     public class RegisterController : Controller
 2     {
 3         public RegisterController() { }
 4
 5         public bool Register()
 6         {
 7             var name = "name";
 8             var password = "password";
 9             var app = new UserApplication();
10
11             if (!app.CheckName(name)) return false;
12             if (!app.CheckPassword(password)) return false;
13             if (!app.CheckIP()) return false;
14             if (!app.CheckAuthenticationCode()) return false;
15             if (!app.CheckRoleId()) return false;
16             if (!app.CheckOrganizationId()) return false;
17
18             app.Add(name, password);
19             app.Copy();
20             app.SendMail();
21
22             return true;
23         }
24     }

UserApplication.cs

 1     public class UserApplication
 2     {
 3         public bool CheckName(string name)
 4         {
 5             return true;
 6         }
 7         public bool CheckPassword(string password)
 8         {
 9             return true;
10         }
11         public bool CheckIP()
12         {
13             return true;
14         }
15         public bool CheckAuthenticationCode()
16         {
17             return true;
18         }
19         public bool CheckRoleId()
20         {
21             return true;
22         }
23         public bool CheckOrganizationId()
24         {
25             return true;
26         }
27         public bool Copy()
28         {
29             return true;
30         }
31         public bool SendMail()
32         {
33             return true;
34         }
35         public bool Add(string name, string password)
36         {
37             return true;
38         }
39     }

如上边代码所示,Controller层太重(很多判断我没写出来,实际情况肯定没有那么简洁的),而且如果是客户端远程部署(不一定是WEB,有可能是C/S、手机等),

那性能肯定很差,因为UI层需要穿透壁垒损耗性能,而且携带大量数据远程传输,多次远程访问等等;而Application也很杂乱,

有些方法比如SendMail这个应该是属于功能性的需求,不是业务性的需求,CHeckIp也是,这些不应该写在一起,因为从语义上讲,他们都应该不属于同一个地方

所以要想性能高,Business层要分2层,一层处理业务逻辑(Application),一层处理非业务逻辑(组织相关业务逻辑,即Service),

那就要改了,姿势一定要改帅,动作要快,Controller层和应用层就不能厚,一定要薄,薄才有快感嘛。。

3、加入分层后的代码分布

Controller.cs,控制器变了,直接调用WCF,原来是在构造函数中注入Application的

 1         public ActionResult Add(LoginUserCURequest entity)
 2         {
 3             var userClient = new WCFUserService.UserServiceClient();
 4             var addRequest = new LoginUserCURequest()
 5             {
 6                 Id = Guid.NewGuid(),
 7                 LoginName = entity.LoginName,
 8                 Password = entity.Password,
 9                 IsEnabled = entity.IsEnabled,
10                 Ip = entity.Ip,
11                 Phone = entity.Phone,
12                 Mail = entity.Mail,
13                 Roles = entity.Roles
14             };
15             var result = userClient.Add(addRequest);
16
17             //// 因为是演示,就不出来返回结果了
18             //if (result == "success")
19             //{ }
20
21             return RedirectToAction("Index");
22         }

DTO也变了很多,加了所以注册需要用到的参数属性

 1     public class LoginUserCURequest
 2     {
 3         /// <summary>Id</summary>
 4         public Guid Id { get; set; }
 5
 6         /// <summary>登录账户名</summary>
 7         public string LoginName { get; set; }
 8
 9         /// <summary>登录密码</summary>
10         public string Password { get; set; }
11
12         /// <summary>是否有效</summary>
13         public short? IsEnabled { get; set; }
14
15         /// <summary>Ip</summary>
16         public string Ip { get; set; }
17
18         /// <summary>验证码</summary>
19         public string AuthenticationCode { get; set; }
20
21         /// <summary>Phone</summary>
22         public string Phone { get; set; }
23
24         /// <summary>Mail</summary>
25         public string Mail { get; set; }
26
27         /// <summary>所属角色</summary>
28         public IEnumerable<Guid> Roles { get; set; }
29
30         public override string ToString()
31         {
32             return string.Format("Id:{0},LoginName:{1},Password:{2},IsEnabled:{3}",
33                 Id.ToString(), LoginName, Password, IsEnabled.ToString());
34         }
35     }

我们看看WCF的实现

  1     public class UserService : TestBase, IUserService
  2     {
  3         private ILoginUserApplication loginUserApplication;
  4         private IRoleApplication roleApplication;
  5         private LoginUserCURequest loginUserCURequest;
  6
  7         public UserService()
  8         {
  9             this.loginUserApplication = container.Resolve<ILoginUserApplication>();
 10             this.roleApplication = container.Resolve<IRoleApplication>();
 11         }
 12
 13         public string Add(LoginUserCURequest entity)
 14         {
 15             this.loginUserCURequest = entity;
 16
 17             if (!this.CheckPassword())
 18             {
 19                 Logger.Warn("密码不符合规范!");
 20                 return "warn, password error";
 21             }
 22
 23             if (!this.CheckIP())
 24             {
 25                 Logger.Warn("你所在IP段被禁止注册!");
 26                 return "warn, ip error";
 27             }
 28
 29             if (!this.CheckAuthenticationCode())
 30             {
 31                 Logger.Warn("你填写的验证码不正确!");
 32                 return "warn, code error";
 33             }
 34
 35             if (!this.CheckAuthenticationCode())
 36             {
 37                 Logger.Warn("你填写的验证码不正确!");
 38                 return "warn, code error";
 39             }
 40
 41             //=======================分割线========================
 42
 43             if (this.loginUserApplication.CheckName(this.loginUserCURequest.LoginName))
 44             {
 45                 Logger.Warn("登录名称已被占用!");
 46                 return "warn, user exists";
 47             }
 48
 49             if (this.loginUserApplication.CheckPhoneRepeat(this.loginUserCURequest.Phone))
 50             {
 51                 Logger.Warn("手机已经被注册!");
 52                 return "warn, user exists";
 53             }
 54
 55             if (this.loginUserApplication.CheckMailRepeat(this.loginUserCURequest.Mail))
 56             {
 57                 Logger.Warn("邮箱已经被注册!");
 58                 return "warn, user exists";
 59             }
 60
 61             bool roleNotExists = false;
 62             foreach (var roleId in this.loginUserCURequest.Roles)
 63             {
 64                 if (this.roleApplication.Get(roleId) == null)
 65                 {
 66                     roleNotExists = true;
 67                     break;
 68                 }
 69             }
 70
 71             if (roleNotExists)
 72             {
 73                 Logger.Warn("所选角色已经无效!");
 74                 return "warn, role error";
 75             }
 76
 77             if (this.loginUserApplication.Add(this.loginUserCURequest))
 78             {
 79                 //this.otherLoginUserApplication.Add(this.loginUserCURequest);
 80                 this.SendMail();
 81
 82                 return "success";
 83             }
 84             else
 85             {
 86                 return "fail";
 87             }
 88         }
 89
 90         public bool CheckPassword()
 91         {
 92             return true;
 93         }
 94
 95         public bool CheckIP()
 96         {
 97             return true;
 98         }
 99
100         public bool CheckAuthenticationCode()
101         {
102             return true;
103         }
104
105         public bool SendMail()
106         {
107             return true;
108         }
109
110         public UserList GetAll()
111         {
112             return null;
113         }
114     }

可以看到,分割线上是属于数据合法性校验,之下属于业务逻辑层面的校验

至于LoginUserApplication,之前我们已经实现了,是属于单元(原子)业务层面的功能,只有CRUD

4、看看界面运行效果

好了,自此,服务层功能算是介绍完毕了!

时间: 2024-10-09 22:07:11

项目架构开发:服务层(下)的相关文章

项目架构开发:业务逻辑层之领域驱动失血模型

前边我们构建了个数据访问层,功能虽然简单,但是基本够用了.传送门:项目架构开发:数据访问层 这次我们构建业务逻辑层 业务逻辑是一个项目.产品的核心,也是现实世界某种工作流程在代码层面的体现. 所以,业务逻辑的合理组织构造,或更真实地反映现实业务操作,对项目的成功与否非常重要 现在业界对业务逻辑层的开发,一般会参考Martin Fowler大师提出来的针对业务层开发的四种模式 分别是面向过程的事务脚本.表模块模式,面向对象的活动记录与领域开发模式 我们要做的就是领域驱动开发模式,注意标题中的“失血

项目架构开发:数据访问层之Query

接上文 项目架构开发:数据访问层之Repository 上一章我们讲了IRepository接口,这张我们来讲IQuery 根据字面意思就可以知道,这次主要讲数据查询,上一章我们只针对单表做了查询的操作,多表联查并没有实现 其实对于任何一个项目来说,多表联查都是比较麻烦的地方,因为项目的“读”操作,特别是多表的“读”,至少占据所有“读”的一半以上 然而至今,据我所知还没有哪一款ORM工具可以灵活处理多表联查:想要不写sql语句,又想性能高,还想用强类型的ling查询方法:这对于多表查询来说比较难

项目架构开发:异常处理及日志

上一篇我们完善了多层开发的效率问题,传送门:项目架构开发:展现层(下) 这次我们完成架构的异常处理功能,异常处理一般都与日志分不开的,因为分析及定位问题需要一些详细信息: 稍微正规一点的公司,都会分开发.测试及生产环境.在本地及测试环境出BUG了,问题很好解决 调试跟踪问题,三下五除二就搞完了:但是在生产环境出问题,基本上是不允许直连数据库调试的: 这时候如何没有足够的异常信息参考,那你就悲催了,你等着加班熬夜吧. 为了解决这个问题,所以异常信息的捕捉及记录就显得非常重要了,一个完善的系统,出问

Django (九) 项目开发流程&amp;项目架构

项目开发流程&项目架构 1. 软件开发的一般流程 1. 需求分析及确认: 由需求分析工程师与客户确认甚至挖掘需求.输出需求说明文档. ? 2. 概要设计及详细设计: 开发对需求进行概要设计,包括系统的基本处理流程,组织结构.模块划分.接口设计.数据库结构设计等.然后在概要设计的基础上进行详细设计.详细设计中描述实现具体模块所涉及到的主要算法.数据结构.类的层次结构及调用关系,需要说明软件系统各个层次中的每一个程序(每个模块或子程序)的设计考虑,以便进行编码和测试.基本达到伪代码的层面. (原型图

.Net Core MVC 网站开发(Ninesky) 2.3、项目架构调整(续)-使用配置文件动态注入

上次实现了依赖注入,但是web项目必须要引用业务逻辑层和数据存储层的实现,项目解耦并不完全:另一方面,要同时注入业务逻辑层和数据访问层,注入的服务直接写在Startup中显得非常臃肿.理想的方式是,web项目近引用接口而不引用实现,在配置文件中进行配置实现程序集合类,注入业务逻辑层而不必注入数据访问层. 一.数据访问层 在项目中摒弃数据访问层或者使用EntityFramework作为数据访问层. 在项目中数据访问层主要实现数据的存储,仔细看一下EntityFramework发现DbContext

李洪强iOS开发之 - 项目架构

李洪强iOS开发之 - 项目架构 01 - 在Appdelegate中设置跟控制器 //导入头文件

NET 分布式架构开发项目实战

.NET 分布式架构开发项目实战 从头到尾,一步一步讲述一个真实的项目实战,关注点主要是架构的思考和实现,以及如何解决平时项目遇到的一些问题. 同时也司公布源代码. 如何构建高性能,稳定SOA应用之-负载均衡-Decoupled Invocation 摘要: 当我们在为一个软件设计架构的时候,我们不仅仅要确保所做出来的架构要满足系统的业务需求,更加要确保做出来的架构要满足可维护性,安全,稳定性的非业务行的需求.另外一个非常重要的非功能性需求就是性能.性能涉及到很多方面的关注点,例如吞吐量,延迟等

.Net Core MVC 网站开发(Ninesky) 2.3、项目架构调整-控制反转和依赖注入的使用

再次调整项目架构是因为和群友dezhou的一次聊天,我原来的想法是项目尽量做简单点别搞太复杂了,仅使用了DbContext的注入,其他的也没有写接口耦合度很高.和dezhou聊过之后我仔细考虑了一下,还是解耦吧,本来按照软件设计模式就应该是高内聚低耦合的,低耦合使项目的模块独立于其他模块,增加了可维护性和移植性! 注:前面写的博客详细记录没项目操作的每一步,其实写起博客来很费时间,而且整片博文里很多无用的信息.对MVC来说会添加控制器,添加视图,添加类这些都最基本的要求了,并且前面博文里都写了,

windows开发平台下本地项目如何使用git管理

作为git使用的初级场景,用最简单的方法介绍一下这个优秀的版本控制工具,一般情况下是这样的: 本地有一个项目,比如名称是"hellopro",因为我们需要共享代码,合作开发,所以需要使用github或者其他服务器托管该git库. 这里只介绍Windows下最简单的操作,所以一些操作的提示信息可能看不到,进阶请使用bash. 1.init 进入项目文件夹,最简单方法:右键Git Init Here 操作完成后,右键菜单出现 Git Add all files now 等 2. add 这