精华内容
下载资源
问答
  • 解决方案架构,顾名思义,解决方案就是解决某一类共例的痛点问题,解决方案和系统是一个包含的关系,为了解决这个共性问题,我们应该提供哪些功能,这些功能应该由哪些系统提供,因此解决方案也是一个行业的标准,...

      在网上也看了很多关于架构方面的文章,林林总总,总感觉没有说的太清楚,可能是每个人的理解不一样,我自己也在繁杂的文章中总结一些架构方面的划分,记录一下。

    解决方案架构:解决方案架构,顾名思义,解决方案就是解决某一类共例的痛点问题,解决方案和系统是一个包含的关系,为了解决这个共性问题,我们应该提供哪些功能,这些功能应该由哪些系统提供,因此解决方案也是一个行业的标准,通过这套解决方案,可以解决行业内某一个共性问题。比如行业内的OA系统、ERP系统。

    业务架构:业务架构是以流程为驱动,重点是关注流程中的对象,对象的操作(业务功能),以及对象的目标。

      比如:我们有一个分期付款的app

       用户端关注的重点是,用户如何通过分期付款app,实现分期付款这个需求。

       商户端关注的重点是,商户端交易情况,收益情况。

    数据架构:数据架构可以是以业务架构中的业务实现为依托,构建满足业务实现所需要的数据条件。

      比如:要满足用户的分期付款的需求,我们需要做个人信息的采集,用于信贷评估,需要用户绑定信用卡或者银行卡,用于定时扣款,如何保证用户信息的安全性、完整性、可靠性、一致性等目标和要求

    应用架构:应用提供的功能和系统组件,应用架构可以从两个方面看,一是技术维度,二是功能维度

    功能维度的应用架构

     技术维度的应用架构

    集成架构:集成架构描述的是系统和系统间,组件和组件之间的通讯设计 

    安全架构:这里主要指的是网络安全,通过什么技术或架构,核实用户的身份和权限,如何保证数据的完整性、一致性、可靠性,保证数据不被窃取或未经授权访问。

    技术架构:技术架构指通过哪些技术/框架实现了系统/系统功能,包括比如spring cloud,本身就是技术架构。

    软件架构:软件架构的主要关注点是定义和文档化软件结构和行为,为了使软件工程和交付基于已知的功能性和非功能性需求。这与解决方案架构的目标完全不同,解决方案架构是定义应用、数据、之后架构构建模块、相关项,并处理所有相关的涉众关注的问题。软件架构师通常也是一种技术SME,它将使用架构风格、面向对象的分析和软件设计模式来设计客户端和服务器端软件组件,实现web/手机客户体验(CX)、进程管理、功能和数据管理应用程序架构模块,这些模块在解决方案架构文档中定义。软件架构的关注点是支持应用程序开发和交付。

     

    展开全文
  • Saas系统架构的思考,多租户Saas架构设计分析

    万次阅读 多人点赞 2019-06-14 13:39:35
    很多创业公司都在尝试创建企业级别的应用cRM, HR,销售, Desk Saas系统。很多Saas创业公司也拿了大额风投。毕竟Saas相对传统软件的优势非常明显。 最近一年,有幸架构一个Crm saas 系统,上线了几个月来,各方面都...

            ToB Saas系统最近几年都很火。很多创业公司都在尝试创建企业级别的应用 cRM, HR,销售, Desk Saas系统。很多Saas创业公司也拿了大额风投。毕竟Saas相对传统软件的优势非常明显。   

    最近一年,有幸架构一个Crm saas 系统,上线了几个月来,各方面都比满意。整个系统创建过程,踩了很多坑,收获也比较多。总结一下Saas系统架构一些特点:

    Saas系统分级

    SaaS系统架构成熟度模型的5个级别——从“混乱”到“乌托邦“

    第0级(混乱):每次新增一个客户,都会新增软件的一个实例。
    第1级(受控的混乱):所有客户都运行在软件的同一个版本上,而且任何的定制化都通过修改配置来实现。
    第2级(多租户[multi-tenant]、高层建筑[Highrise]):所有的客户都已经可以在软件的同一个版本上运行了,而且他们都在同一个“实例”上运行。
    第3级(多租户, 扩建[Build-Out]):此时你已经拥有了多租户、单一版本的软件模型。不过你还是可以通过硬件扩展(scale-out)的方式来进行扩充。
    第4级(乌托邦):如同第3级,除非你可以找出有效的方式,以在不同的“实例”上运行不同版本的软件

    应用程序必须支持多租户:

        多租户可以分为几个不同的类别(如列表下方的图所示):
        1.1,云中的简单虚拟化,其中只对硬件进行共享。
        1.2,共享应用程序,对每个租户使用不同的数据库。
        1.3,共享应用程序和数据库(效率最高,真正的多租户)。

    1.分层设计

    Saas系统分层大概是:

    Saas系统分层
    Saas系统分层

     

    Saas系统分层:租户识别>应用层>数据访问层>缓存层>数据库

    业务代码都是写在应用层。

    租户识别可以用spring拦截器实现,然后使用ThreadLocal传递给后端

    数据库和缓存层对应用层应该是透明的。程序员在写代码的时候,只关心业务逻辑,不应该担心多租户的问题。

     

    2.数据隔离要透明

    saas系统说起来很简单,任何系统似乎加个tenant_id(租户id)就变成saas系统了。比如原来的用户登录是:

    select username,password from users where email='abc@qq.com'

    改成

    select username,password from users where email='abc@qq.com' and tenant_id =1;

    对于复杂业务的saas系统,这样做法非常危险,而且开发效率很低。你想想如果那个程序员写sql时候忘了加 “ and tenant_id =1” . 结果不堪设想。

    比较好做法是在数据库访问层对SQL进行改写。

    TenantContext.exec("select username,password from users where email='abc@qq.com' ");

    在连接池根据TenatnContext改写Sql. 

    这样做好处是,一来程序猿最多把系统搞down了,也不至于信息串了互相泄露。二来将来做分表分库也很方便,上层应用不用修改。

    3. 租户识别方案

    比较好做法是通过url识别租户。系统是给租户生成一个随机的三级域名,比如 abc.crm.baidu.com.   如果客户想使用自己的域名,可以在cname到我们生成的三级域名,并在管理系统里面做绑定。

    这样一个租户可以有两个域名,访问saas,一个随机生成的三级域名,另外一个租户自己的域名.代码里面可以根据过来的域名,判断是那个租户然后初始化TenantContext.

    如果不想通过域名来做,也可以通过登录名来判断。这种方式要涉及到租户切换问题。

    4. 智能DNS

    以后补充。

    5. 租户管理系统(计费,订购,定制,充值,催缴)

    Saas系统是必须考虑计费系统和租户控制系统。这个系统需要都是独立设计。比如那个租户购买了那些模块,一个月多少钱。租户可以创建最多的用户数。计费到期邮件提醒等功能。

    计费方式一般有两种,周期性计费,类似月租方案,和使用量计费,用多少付多少。 周期性计费比较简单。也可以两者结合起来。

    6. 定制化开发

    SAAS的优势在于一套系统多人使用,似乎和定制化开发有冲突。比如A客户想要A功能,B客户不想要。但定制化开发是无法避免的,比如CRM系统这样复杂的系统,不可能一套系统满足所有公司的要求。定制化开发尽可能分系统,分模块去做。然后通过控制台中配置不同租户订购不同模块,那些模块可以在前端页面上显示。不同的子系统需要分开部署。前端可通过nginx根据url分发,比如 abc.crm.baidu.com/bi/xxx/xx这个地址,就分发到BI子系统。不要尝试OSGI去搞模块化,这个是个大坑。

    还有开发和产品,现有需求一定要分析清楚,不要一上线发现后患无穷。新功能尽量做的独立可以配置。

    7. 灰度升级

    SAAS付费企业客户对系统问题都特别敏感。 为了减少升级可能出现问题的影响范围,一般都采用灰度升级策略。如果使用了url来区分不同租户,灰度升级配置就会很方便。可以配置nginx 来根据域名做分发,比如租户A(aaa.com)到实例1(版本1.0),租户B(bbb.com)到实例2(版本). 当需要域名配置非常多的时候,nginx配置文档会乱。这块时候可以考虑使用nignx_lua来写一些扩展模块。

    8. 容量估计

     

    9. Saas平台架构分层分析

    Saas平台架构需要完成从用户申请链接saas到用户对自己购买的功能模块的应用整个过程,用户用起saas看似简单快捷,但这个过程却需要saas平台架构默默完成的非常复杂的处理过程。通过对saas平台架构的了解,可以清晰的分化数据的处理过程,让用户也可以明白saas平台架构处理数据的优势。下面介绍:saas平台架构分为哪几部分。

     

    saas平台架构之呈现层:

    saas平台架构的呈现层可以使用的客户端可能都浏览器或本地客户端。如果是浏览器则需要Web界面技术、交互技术等技术(如:HTMl5技术、CSS3技术、Ajax技术等)的支持,如果是软件客户端则需要远程桌面技术、软件交互技术等技术支持。

    saas平台架构之调度层:

    saas平台架构的调度层体现分布式系统的特性之一。调度层首先负责识别并通过AAA认证每个用户请求,然后根据业务处理器的负载、业务特征进行合理的调度。通过应用这样的架构SaaS平台可以横向扩展。此外在存储、缓存等方面为了满足平台的横向扩展需求,调度层也必须具有良好的可扩展性。

    saas平台架构之业务层:

    saas平台架构的业务层负责接收调度层转发过来的请求,而且还要通过对接受到的请求执行真正的业务逻辑。一般来说业务逻辑的执行使用一台服务器就够了。因此业务层实际是由一排对等的服务器组成的,每台服务器都执行相同的业务逻辑。

    saas平台架构之数据层:

    saas平台架构的数据库集群用于处理存储关系性很强并且对事务性要求很高的业务数据,这类数据目前还要用传统的数据库集群技术来解决,saas平台架构的数据库集群主要是根据业务特征制定数据拆分方案。同时分布式数据库用于存放海量但关系性不强的数据(如:用户的操作日志等)。

    以上是对“Saas系统架构的思考,多租户Saas架构设计分析”的介绍,从saas平台架构处理数据可以看出saas平台的应用有很强的优势,如用户使用saas非常方便简单只要浏览器或本地客户端接口,saas平台处理数据要经过层层认证saas产品安全可靠,saas平台优化处理数据提高saas性能。

    多租户Saas系统架构还应该满足以下需求:

    编号需求描述
    1软件授权云平台付费授权机制,可按时间、功能、数量等进行付费授权
    2组织入驻允许组织主动申请加入平台
    3实名认证个人实名认证、组织实名认证
    4资质审核个人和组织的资质审核,如对获得的证书或荣誉进行审核
    5组织绑定个人账户绑定组织,与组织建立关联关系
    6组织解绑个人账户与组织进行解绑
    7账户注销个人账户注销,并销毁所有个人资料和档案
    8统一登录即 SSO
    9统一注册提供统一的用户注册页面

    部分资料整理自:

    http://www.ruanally.com
    ​​​​​​​http://qk.gam7.com
    ​​​​​​​​​​​​​​http://www.ruanbe.com

     

    展开全文
  • 关系,本篇可能主要是简单的介绍下企业应用的几类模式,结合这几个分层直接的交互来完成系统功能的构建。我们还是先对我们学习的四个分层的职责和功能做个大 概的回顾,我们先来看看下图来回顾下我们讲述的内容。
    一、上篇回顾

          我们先来回顾下上篇讲解的内容,我们前面的几节分别讲述了,业务逻辑层、数据访问层、服务层、表现层,我们了解了这些分层的职责和分层之间的大概的关联

    关系,本篇可能主要是简单的介绍下企业应用的几类模式,结合这几个分层直接的交互来完成系统功能的构建。我们还是先对我们学习的四个分层的职责和功能做个大

    概的回顾,我们先来看看下图来回顾下我们讲述的内容。

          image

          我想通过上图,大家能回忆起我们讲述的相关内容,然后整理好自己的思路,我们本文将会针对这几个分层进行相应的模式的讲解,并且会结合实例来说明企业应

    用架构的简单应用。我想这也是大家关心的内容,我也希望大家能多提出宝贵意见,大家共同提高。

          之前说是提供PDF文件的下载的,以提供给需要学习的朋友更方便的形式,但是由于最近时间有限,没能整理好,等过阵子提供一个完整的整合好的PDF版本,提

    供给大家下载,还望大家见谅。

    二、开篇

          本篇我们将针对我们前面讲述的几个分层做个简单的整合,就是通过一个简单的实例代码来说明下,我们给出的一个可能的企业应用架构模式去完成企业应用,并

    且顺带分析下,企业应用中可能存在的瓶颈等,当然可能本章只是点出一些概念,然后在后面的章节中去完成,比如我们的性能优化,性能瓶颈等,这些我们后面通过

    这篇:系统架构师-基础到企业应用架构-性能优化(架构瓶颈)(后续篇)详细的分析。当然由于本章主要是基于前面讲述的内容的一个整合和分析,可能部分观点

    还有更好的改进方案,还希望大家多提出宝贵意见和您的建议,谢谢!那么我们先来看看我们本章讲述的内容吧:

          image

          本章主要讲述下各分层之间的交互方式及可行的设计方案,并且分析我们在每个分层采用什么样的模式,及给出相应的示例代码,当然这里可能讲述的不会是最好

    的实现方案,还期待大家提出更好的改进方案。

    三、本文提纲

          1、内容回顾

          2、开篇

          3、本文提纲

          4、企业应用架构实例

                4.1、企业应用架构中的物理分层

                4.2、数据访问层分析

                4.3、业务逻辑层分析

                4.4、服务层分析

                4.5、表现层分析

                4.6、分析总结

          5、结束语

          6、系列进度

          7、下篇预告

    四、企业应用架构实例
             4.1、企业应用架构的物理分层

          本节将会分层的相关介绍,并且分析分层的原因,为下篇:系统架构师-基础到企业应用架构-分层[上篇] 做铺垫。

         分层我想对大家来说都不会太陌生。因为我们平时在开发的过程中一般都是采用分层架构的方式,通过分层将系统的功能进行划分,我们将系

    统中的若干有共同特征的部分放在一个分层中,然后通过分层之间的交互去完成系统的功能。

         我们看看企业应用的几种应用程序模式:

          image

        1、单击应用程序就是一些本地服务的应用程序,这个应该没啥特别的难度,例如Office应用程序,当然现在有在线版本的Office,这个应用的太多了。

        2、客户端/服务器的形式,特别是在WinForm、WPF、SilverLight方面的应用等,例如我们的常用的OutLook,虽然不是采用.NET平台开发的,但是原理一样就

    是通过客户端通过通信服务来访问服务器,然后取回相应的邮件信息返回给客户端,相当于我们通过客户端的形式来访问Web服务。

        3、Web服务:通过将开发的Web服务器部署在服务器上,然后我们就可以通过输入HTTP网址的形式来访问web服务,我们这里是通过浏览器来完成的,其实这个

    模式也是客户端/服务器的形式。只不过这时的客户端变成浏览器+服务页面,然后通过浏览器来完成Web服务的访问。

        4、其他服务:我们这里主要是针对一些其他的设备,例如通信设备等方面的访问,比如说我们现在提供一个卫星定位的服务,那么通过设备调用服务来完成,告知

    用户的GPS定位信息等。很多这个方面的应用。这个方向应该是未来的一个主流吧。

        我们上面简单的讲述了系统的物理分层的形式,那么我们来看看如何对系统的功能进行分层,也就是我们下面要详细讲解的实例分析。

           4.2、数据访问层分析

         数据访问层我们知道是唯一一个可以与数据库之间直接进行交互的分层,数据访问层必须满足的几个功能和职责,我们这里就不复述了,我们本篇可能就是直接给

    出数据访问层的相关实现,并且分析出数据访问层与其他层之间的交互。

          image 大家可能一看就知道是什么意思,可能在您的眼中应该就和你们平时项目中采用的分层结构有点类似吧。

          我这里的数据访问层提供所有的数据库访问服务。我们来看看基本的服务功能吧

          我们这里定义了一个DDL语句操作的枚举,CUD的枚举,因为我们提供了一个统一的数据访问服务,通过Excute来完成。

    ?
    /// <summary>
    /// 数据访问操作类型
    /// </summary>
    public  enum  DDLType
    {
         Create,
         Update,
         Delete
    }

         这里用枚举来定义数据库字段与查询条件值之间的关系

    ?
    /// <summary>
    /// 数据字段与字段值之间的关系表达式
    /// </summary>
    public  enum  FieldExpressionType
    {
         EquleTo,
         BeginThen,
         LessThen,
         BetweenAnd,
         Like
    }

        我们再来看看我们定义的查询条件的接口

    ?
    /// <summary>
    /// 定义子查询接口
    /// </summary>
    public  interface  ICondition
    {
         int  Add( string  fieldName, object  value,FieldExpressionType fetType);
         int  Add(ICondition condition);
     
         string  WhereCondition;
         string  OrderCondition;
    }

        数据访问层接口代码

    ?
    /// <summary>
    /// 定义统一的数据访问服务
    /// </summary>
    public  interface  IDataAccess
    {
         #region CUD
     
         int  Create<T>(T item);
     
          int  Update<T>(T item);
     
          int  Delete<T>(T item);
     
          int  Execute<T>(T item,DDLType ddlType);
     
         #endregion
     
         #region Read服务
     
          List<T> Query<T>(ICondition condition);
     
          T GetModelByPrimaryKey<T>( object  key);
     
          List<T> GetAll<T>();
     
          #endregion
     
         #region 事务操作
     
          void  BeginTransaction();
     
          bool  Commit();
     
          bool  RollBack();
     
          bool  IsTransaction;
     
          #endregion
    }

        我们知道具体的代码我们是通过反射+特性的形式来完成数据库操作语句的生成的,我们来看看可行的代码,属性项特性的定义。

    ?
    /// <summary>
    /// Model中的字段属性特性
    /// </summary>
    [AttributeUsage(AttributeTargets.All, AllowMultiple = false )]
    public  class  PropertyAttribute : Attribute
    {
         private  string  dbColumnName;
         private  bool  isPrimary;
         private  DbType dbType;
         private  object  defaultValue;
         private  bool  isIdentify;
         private  int  length;
     
         public  string  DbColumnName
         {
             get
             {
                 return  this .dbColumnName;
             }
             set
             {
                 this .dbColumnName = value;
             }
         }
     
         public  bool  IsPrimary
         {
             get
             {
                 return  this .isPrimary;
             }
             set
             {
                 this .isPrimary = value;
             }
         }
     
         public  bool  IsIdentify
         {
             get
             {
                 return  this .isIdentify;
             }
             set
             {
                 this .isIdentify = value;
             }
         }
     
         public  DbType DbType
         {
             get
             {
                 return  this .dbType;
             }
             set
             {
                 this .dbType = value;
             }
         }
     
         public  object  DefaultValue
         {
             get
             {
                 return  this .defaultValue;
             }
             set
             {
                 this .defaultValue = value;
             }
         }
     
         public  int  DbLength
         {
             get
             {
                 return  this .length;
             }
             set
             {
                 this .length = value;
             }
         }
         public  PropertyAttribute( string  dbName, bool  isPrimery, DbType type, object  dValue)
         {
             this .dbColumnName = dbName;
             this .isPrimary = isPrimery;
             this .dbType = type;
             this .defaultValue = this .GetDefaultValue();
         }
     
         private  object  GetDefaultValue()
         {
             return  new  object ();
         }
     
         public  PropertyAttribute( string  dbName)
         {
             this .dbColumnName = dbName;
             this .isPrimary = false ;
             this .dbType = DbType.String;
             this .defaultValue = this .GetDefaultValue();
         }
     
         public  PropertyAttribute( string  dbName, bool  isPrimery)
         {
             this .dbColumnName = dbName;
             this .isPrimary = isPrimery;
             this .dbType = DbType.String;
             this .defaultValue = this .GetDefaultValue();
         }
     
         public  PropertyAttribute( string  dbName, bool  isPrimery, DbType type)
         {
             this .dbColumnName = dbName;
             this .isPrimary = isPrimery;
             this .dbType = type;
             this .defaultValue = null ;
         }
    }

        我们再来看看基于表上的特性

    ?
    /// <summary>
      /// 基于表的自定义特性类
      /// </summary>
    [AttributeUsage(AttributeTargets.All, AllowMultiple = false )]
    public  class  TableAttribute : Attribute
      {
          private  string  dbTableName;
          public  TableAttribute( string  dbName)
          {
              this .dbTableName = dbName;
          }
     
          public  string  TableName
          {
              get
              {
                  return  this .dbTableName;
              }
              set
              {
                  this .dbTableName = value;
              }
          }
      }
     
          根据反射取出表名
     
          /// <summary>
          /// 返回Model对应的数据库表名
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="model"></param>
          /// <returns></returns>
          public  string  DbTableName<T>(T model)
          {
              string  dbName = string .Empty;
              DPM.Common.TableAttribute attr = null ;
     
              object [] attributes = model.GetType().GetCustomAttributes( typeof (DPM.Common.TableAttribute), true );
     
              if  (attributes.Length > 0)
              {
                  attr = (DPM.Common.TableAttribute)attributes[0];
              }
     
              if  (attr != null )
                  dbName = attr.TableName;
     
              return  dbName;
          }
     
          根据反射取出表中的列
     
         /// <summary>
         /// 动态创建表中字段列表
         /// </summary>
         /// <typeparam name="T"></typeparam>
         /// <param name="model"></param>
         /// <returns></returns>
         public  string  InitDbColumns<T>(T model)
         {
             StringBuilder commandBuilder = new  StringBuilder();
     
             DPM.Common.PropertyAttribute attr = null ;
     
             foreach  (PropertyInfo property in  model.GetType().GetProperties())
             {
                 object [] attributes = property.GetCustomAttributes( typeof (DPM.Common.PropertyAttribute), true );
                 if  (attributes.Length > 0)
                 {
                     attr = (DPM.Common.PropertyAttribute)attributes[0];
                 }
     
                 if (commandBuilder.length>0)
     
                       commandBuilder.Append(“,”);
     
                 commandBuilder.Append(attr.DbColumnName);
             }
     
             return  commandBuilder.ToString();
         }

           当然这里只是给出了简单的示例,我们来看看生成的Insert 语句的格式吧

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
       /// <summary>
       /// 动态创建表中字段更新列表
    /// </summary>
      /// <typeparam name="T"></typeparam>
      /// <param name="model"></param>
      /// <returns></returns>
      public  string  InitInsertColumns<T>(T model)
      {
          StringBuilder filedBuilder = new  StringBuilder();
          StringBuilder valueBuilder = new  StringBuilder();
          StringBuilder commandBuilder = new  StringBuilder();
     
          DPM.Common.PropertyAttribute attr = null ;
     
          foreach  (PropertyInfo property in  model.GetType().GetProperties())
          {
              object [] attributes = property.GetCustomAttributes( typeof (DPM.Common.PropertyAttribute), true );
              if  (attributes.Length > 0)
              {
                  attr = (DPM.Common.PropertyAttribute)attributes[0];
              }
              if  (attr.DbColumnName == "" )
                  continue ;
              if  (attr.IsIdentify)
                  continue ;
              if  (filedBuilder.Length > 0)
              {
                  filedBuilder.Append( ","  + attr.DbColumnName);
                  valueBuilder.Append( ","  + property.GetValue(model, null ));
              }
              else
              {
                  filedBuilder.Append(attr.DbColumnName);
                  valueBuilder.Append(property.GetValue(model, null ));
              }
     
          }
     
          commandBuilder.Append(SqlDMLText.LKH);
          commandBuilder.Append(filedBuilder.ToString());
          commandBuilder.Append(SqlDMLText.RKH);
          commandBuilder.Append(SqlDMLText.VALUES);
          commandBuilder.Append(SqlDMLText.LKH);
          commandBuilder.Append(valueBuilder.ToString());
          commandBuilder.Append(SqlDMLText.RKH);
     
          return  commandBuilder.ToString();
      }

           其他的语句类似了,这部分详细的代码我们会在ORM篇详细的讲述,并且对反射的性能通过优化来提供性能。

           我们来总结下数据访问层应该有的功能吧。我认为应该提供以下的功能

           image 这样的一个数据访问层基本上能够满足功能的需要。当然我这里就不把代码全部贴出来了,太多

    了,我们接下来讲讲业务逻辑层吧。

              4.3、业务逻辑层分析

          上篇我们贴出来了一些比较典型的数据访问层的代码,当然没有上全部的代码,本文将会提供demo实例下载,大家可以参考其中的部分代码。我们来看看业务层

    中的每个业务对象应该具有什么样的智能呢?我们知道我们这里不建议业务对象直接来访问数据访问层,那么我们如何实现持久化透明的方式呢?我记得之前我讲述业

    务逻辑层的时候也提到这个方面的要求了,还有不少热心的园友问我如何实现,我这里给出几个可能的办法,当然如果有更好的办法,还请大家多多提出。我这里给出

    个简单的业务对象的职责,当然我这里给出的职责是我理解的在我的这个实例中业务对象应该具有的职责。

          接下来我们看看业务逻辑层的职责吧,然后我们给出示例代码

          image

          我们来举例说明一个业务对象可能具有的功能吧?我们这里以还是以B2C系统为例吧,我们来说说订单的可能行为吧:

    ?
    /// <summary>
    /// 订单
    /// </summary>
      public  class  Order
    {
          /// <summary>
          /// 产品列表
          /// </summary>
          private  List<Product> products = new  List<Product>();
     
          /// <summary>
          /// 获取订单的金额
          /// </summary>
          /// <param name="order"></param>
          /// <returns></returns>
          public  decimal  GetOrderCount(Model.Order order)
          {
              decimal  totalCount = 0;
              foreach  (Product product in  products)
              {
                  totalCount += product.Cash;
              }
     
              return  totalCount;
          }
     
          /// <summary>
          /// 获取该订单下的所有产品
          /// </summary>
          /// <param name="order"></param>
          /// <returns></returns>
          public  List<Product> GetProduct(Model.Order order)
          {
              return  new  List<Product>();
          }
    }

         当然这里面的取得与订单相关的产品列表,因为我们没有设计在DTO中默认包含这个属性,那么我们这里其实可以考虑增加延迟加载的形式,一旦加载后,也可以

    第一次加载后,缓存起来,下次使用的过程中直接取出来。

          我们来看看与这个订单业务逻辑相关的服务层代码

    ?
    /// <summary>
    /// 订单服务
    /// </summary>
    public  class  OrderService
    {
         private  List<Model.Product> products = new  List<Model.Product>();
     
         /// <summary>
         /// 持久化透明的方式
         /// </summary>
         /// <param name="order"></param>
         /// <param name="type"></param>
         /// <returns></returns>
         public  int  Execute(Model.Order order,DAL.DDLType type)
         {
             return  DAL.SQLServer.Instance.Execute(order, type);
         }
     
         /// <summary>
         /// 获取订单的总金额
         /// </summary>
         /// <param name="order"></param>
         /// <returns></returns>
         public  decimal  GetOrderCash(Model.Order order)
         {
             BLL.Order = new  BLL.Order(order);
     
             return  order.GetOrderCount();
         }
     
         /// <summary>
         /// 保存订单信息
         /// </summary>
         /// <param name="order"></param>
         /// <param name="type"></param>
         /// <returns></returns>
         public  int  SaveOrder(Model.Order order, DAL.DDLType type)
         {
             int  iAff = 0;
             //将与订单相关的产品信息也同步进行保存。
             //我们这里通过事务的方式进行处理
             try
             {
                 DAL.SQLServer.Instance.BeginTransaction();
     
                 //具体的逻辑
                 //插入产品信息
                 foreach  (Model.Product product in  products)
                 {
                     DAL.SQLServer.Instance.Create(product);
                     iAff++;
                 }
     
                 //插入订单信息
                iAff+= DAL.SQLServer.Instance.Create(order);
     
                 DAL.SQLServer.Instance.Commit();
                 return  iAff;
     
             }
             catch
             {
                 DAL.SQLServer.Instance.RollBack();
                 return  0;
             }
             finally
             {
                 DAL.SQLServer.Instance.Dispose();
             }
         }
    }

          通过上面的代码我们可以看出,目前的业务逻辑层中的业务对象只是处理自身的相关业务逻辑,通过服务层与数据层进行交互,然后业务对象并不关系自身的

    CRUD的业务,而是通过服务层来完成的,那么对于CUD我想大家应该没有什么争议,因为持久化透明只是说把业务逻辑层原本通过继承或者是实现接口,或者依赖注

    入的形式来完成持久化的操作。

          我们这里则是将这样的持久化操作提出来放在服务层来完成,这样可以降低业务层与数据访问层的依赖,那么大家肯定关心如何实现业务逻辑对象的查询服务呢?

    我的思路是这样的,大家请看看是否合理,或者您有好的思路或者想法一定要告知我,不甚感激!

          image

          不知道我们上面的上图是否表述的很清楚,还请大家多多的批评指出。当然上述的模式,可能在实际的企业应用中还是有点困难的,还有一种业务逻辑层的持久化

    透明实现。就是通过AOP来完成,或者是通过代理类来实现。这里说心里话,我对AOP的具体实现并没有研究过,但是通过动态注入的方式的确可以实现这个功能。具

    体的实现方式就交给大家来完成了。

           4.4服务层分析

          服务层作为协调业务对象进行相应的业务流的控制,当然服务层只是协调业务对象之间的交互,并不是负责具体的业务逻辑,这里的服务层应该是只负责业务对象

    之间的交互。当然我这里还把部分对数据完整性,数据类型等方面的内容放在服务层来做。下面来说明服务层可能包含的功能:

          image

          我们在业务逻辑层中也贴出了部分代码。当然服务层中其实还可以有很多的内容,比如通过我们在前面的服务层讲解时介绍的几个模式,我们也都是可以在服务层

    中应用的。当然我们这里的实例代码可能就不会贴出这几个模式了,我们这里举例来说明服务层中的部分服务代码。例如我们前面经常举例说明的订单管理中的提醒功

    能。

          比如我们有时候我们的提醒功能,需要邮件提醒或者是短信提醒的几种服务方式,这时候我们可以通过提供统一的服务接口,然后不需要单独的在界面层去单独的

    定义,我们通过接口的形式为后期的变化方便扩展,我们来给出部分代码:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
         /// <summary>
         /// 提醒服务
         /// </summary>
         public  interface  IAlarmService
         {
             /// <summary>
             /// 发送消息的服务
             /// </summary>
             /// <param name="reciveObject">接收方</param>
             /// <param name="title">标题</param>
             /// <param name="content">消息内容</param>
             /// <returns></returns>
             bool  SendMessage( string  reciveObject, string  title, string  content);
         }
     
         我们来看看邮件提醒服务的简单实现代码
     
    public  class  EmailAlarm : IAlarmService
         {
             #region IAlarmService 成员
     
             /// <param name="strSmtpServer">邮件服务器(如果是163邮箱就写smtp.163.com)</param>
             /// strSmtpServer
             /// <param name="strFrom">发件人的帐号</param>
             /// strFrom
             /// <param name="strFromPass">发件人密码</param>
             /// strFromPass
             public  bool  SendMessage( string  reciveObject, string  title, string  content)
             {
                 bool  isSuccess = true ;
     
                 MailSetting model = DAL.SQLServer.Instance.GetModelByPrimaryKey<MailSetting>();
     
                 try
                 {
                     System.Net.Mail.SmtpClient client = new  SmtpClient(model.strSmtpServer);
     
                     client.UseDefaultCredentials = false ;
     
                     client.Credentials = new  System.Net.NetworkCredential(model.strFrom, model.strFromPass);
     
                     client.DeliveryMethod = SmtpDeliveryMethod.Network;
     
                     System.Net.Mail.MailMessage message = new  System.Net.Mail.MailMessage(model.strFrom, reciveObject, title, content);
     
                     message.BodyEncoding = System.Text.Encoding.UTF8;
     
                     message.IsBodyHtml = true ;
     
                     client.Send(message);
                 }
                 catch
                 {
                     isSuccess = false ;
                 }
                 return  isSuccess;
             }
     
             #endregion
         }

          短信服务也和邮件服务类似,通过实现接口来提供服务,那么我们在比如说订单,或者其他的可能会提醒的地方,就可以通过自定义实现不同的格式服务,来完成

    调用。

           4.5、表现层分析

         我们都知道表现层是最终显示与用户交互的功能的,那么我们的表现层是怎么来调用服务层的呢?我们这里的实例是采用服务层调用的方式来完成。我们这里通过

    定义接口的形式,然后通过实现不同的用户UI,然后我们通过展示器来调用服务层,然后将服务层中的处理结果返回给展示器,展示器与视图之间进行交互,我们来看

    看表现层的层级关系。

          image 大家看到这个图请不要诧异,这里的展示器层可能还会直接访问业务逻辑层,也可能直接访问数据访问层,这

    些都是有可能的,我们需要根据项目具体的情况去决定。

         我们来看看UI层的视图代码:

         我们在视图中保持展示器的引用。

    ?
    /// <summary>
    /// 定义统一的视图接口代码
    /// </summary>