浅谈企业软件架构(2)

摘要:
第2章层次结构本章重点介绍如何在开发中实现软件层次结构。许多开发人员谈论的软件层次结构必须是远程调用、远程服务等;应用层、表示层、会话层、传输层、网络层、DataLink层、物理层和经典的TCP/IP四层模型:

第二章分层架构

 

本章我们重点来描述如何实现开发中软件层次结构,通过对第一章的例子的重构,以实例的方式展示一个分层结构是何样子,力求简要说明如何考虑软件开发中的分层问题,建立一个关于软件分层一个初步的印象。在个人以往的项目经历中,遇到了各种各样的软件层次概念,尤其对物理分层与逻辑分层没有清晰的认识,很多开发人员一谈软件分层必然是远程调用、远程服务之类;要不就是过度分层,不管项目和开发环境情况的实际需要,就搞一个三层软件架构,结果呢、层与层之间又没有良好的封装和隔离性,反倒是层与层之间处处是交叉引用,业务逻辑与技术逻辑在层与层之间盘根错节纠缠不清,未能获得分层给项目开发带来的优势的同时,反倒增加了软件开发人员掌握和理解架构的难度、降低了开发效率和系统维护的复杂度。 

2.1 层次演化

 

  关于系统的层次结构我们最常见的例子是:OSI网络结构的七层模型,它们分别为:

 

应用层(Application

表示层(Presentation

会话层(Session

传输层(Transport

网络层(Network

数据链路层(Data Link

物理层(Physical

 以及经典的TCP/IP四层模型:

 

应用层

传输层

网际层

网络接口层

 

  们的分层描述及功能作用,在网上可以搜索到很多,这里就不做详细描述了。总之,用分层的观点来考虑系统时,就是把分层考虑成“多层蛋糕”的形象,上一层基于下一层来实现,使用了下层定义的各种服务(接口),下一层不知道其上有几层,功能是什么。下一层对上一层隐藏自己的实现细节,这样上一层只关心下一层提供了什么样的服务(接口)可供调用,具体的实现技术算法就不是它关心的事情了。每一层专注在自己的功能领域,通过接口方式为上一层提供服务。

分层架构最大的困难就是如何决定建立那些层次以及每一层有哪些相应的职责。不是盲目的为了分层而分层,这方面TCP/IP的四层模型就是很好的例子,根据自己的需要把七层模型合并成4层模型。

企业应用的层次演化是从最早的没有层次——〉C/S2层结构模式——〉表现层、领域层、数据源层的3层结构模式。大部分的企业应用是与数据的存储息息相关的,所以企业应用是伴随着关系数据库的发展而一起兴起和广泛应用。C/S结构模式便成了大家最耳熟能详的企业软件架构。但是我们在讨论企业应用的层次结构时,一定要记住它包含两个层次意思:一个是物理链路的分离,客户端是一台计算机,数据库服务则安装在另一台计算机上,通过远程访问的模式实现两个物理节点的数据交流。同时层次结构还有的另一层含义就是逻辑层次,通过把不同的逻辑封装在不同的软件开发层次上,来实现逻辑意义上的层次结构,从而实现软件功能的封装性和相对独立性。这样,我们就可以根据不同的需要把不同的层次部署到不同或相同的计算机上。关于这一点会在后面的章节中给出具体的例子来继续加以说明。 

2.1.1. 经典的软件三层架 

        随着面向对象开发方式的崛起和广泛应用,企业应用开发从二层结构逐步演进到了三层结构。表现层实现用户界面、在领域层实现业务逻辑、在数据源层存取数据。如下表:

层次

职责

表现层

显示信息、处理用户请求、命令行调用等

业务逻辑层(领域层)

业务逻辑,系统商业价值部分

数据源层

主要与数据库,存储文件等,保存系统产生的信息

 

随着O/R mapping的广泛使用,在实际的软件架构中,根据映射工具的需要出现了一个专门Model模型层,或者不能模型单独叫一层,它其实贯穿三层的数据载体(值对象),本身不包含太多的业务逻辑(少量或没有),形象的说只简单的承载数据在层与层之间的传输的交通工具。

2.2 例子的重构

 

下面我们来看看如何根据三层架构模型把第一章的例子重构成一个三层架构的软件解决方案。三层分别命名为:Dal层、Biz层和界面层。

2.2.1. 添加Model的类库项目 

首先,我们新建一个命名为Model的类库项目把Customer.cs和它的映射文件Customer.hbm.xml移入到该项目中。如下图:

浅谈企业软件架构(2)第1张

浅谈企业软件架构(2)第2张

 

修改Model文件Customer.cs和它的映射文件Customer.hbm.xml

Customer.cs代码如下:

浅谈企业软件架构(2)第3张浅谈企业软件架构(2)第4张Code System;
using System.Collections.Generic;
using System.Text;

namespace Model
{
    
public class Customer
    {
        
public virtual int CustomerId { getset; }
        
public virtual string Firstname { getset; }
        
public virtual string Lastname { getset; } 
        
public virtual string Gender { getset; }
        
public virtual string Address { getset; }
public virtual string Remark { getset; }
    }
}

 

Customer.hbm.xml代码如下:

浅谈企业软件架构(2)第5张浅谈企业软件架构(2)第6张Codexml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model">
  
<class name ="Model.Customer,Model" table="Customer">
    
<id name="CustomerId" column="CustomerId" type="Int32" unsaved-value="0">
      
<generator class ="assigned"></generator>
    
</id>
    
<property name="Firstname" column ="Firstname" type="string" length="50" not-null="false" />
     
<property name ="Lastname" column="Lastname" type="string"  length="50" not-null="false" />
    
<property name="Gender" column ="Gender" type="string" length="2" />
    
<property name ="Address" column="Address" type="string" length="50" />
    
<property name ="Remark" column="Remark" type="string" length="50" />

  
</class>
</hibernate-mapping>

 

  注意:Customer.hbm.xml的编译行为一定要设置成嵌入式资源。 

 

2.2.2.    添加Dal类库项目

解决方案中新建好Dal类库项目后,把Class.cs更名为CustomerDal.cs,根据分层对Dal层的功能定义,其负责领域数据的持久化和和从数据库中装载领域数据,对Biz层和界面层隐藏具体的数据存储细节。在我们例子中该层负责与数据持久相关的产品进行数据交互。于是我们把关于 NHibernate的初始化和交换相关的对象放在Dal层进行初始化。把前面例子相应的AddGetEditDelete移入到CustomerDal.cs中来,实现代码如下:

浅谈企业软件架构(2)第7张浅谈企业软件架构(2)第8张Code System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using Model;

namespace Dal
{
    
public class CustomerDal
    {
        
private Configuration _config;
        
private ISessionFactory _factory;
        
private ISession _session;

        
public CustomerDal()
        {
            _config 
= new Configuration().AddAssembly("Model"); //注意加载的程序集发生了变化
            _factory = _config.BuildSessionFactory();
            _session 
= _factory.OpenSession();

 

 需要给Dal项目添加NHibernate库文件的引用和Model项目引用。如下图:

浅谈企业软件架构(2)第9张 

2.2.2.1.            增加Customer对象函数

 

浅谈企业软件架构(2)第10张浅谈企业软件架构(2)第11张Code Boolean Add(Customer customer)
        {
            ITransaction tran 
= _session.BeginTransaction();
            
try
            {
                _session.Save(customer);
                tran.Commit();
                
return true;
            }
            
catch
            {
                tran.Rollback();
                
if (_session.Contains(customer))
                {
                    _session.Evict(customer);
                }
                
return false;
            }
        }

2.2.2.2.            获取Customer对象函数

浅谈企业软件架构(2)第12张浅谈企业软件架构(2)第13张Codepublic Customer Get(Int32 customerId)
        {
            Customer customer 
= (Customer)_session.Get(typeof(Customer), customerId);
            
if (customer != null)
            {
                
return customer;
            }
            
else
            {
                
return null;
            }
        }

2.2.2.3.            修改Customer对象函数

浅谈企业软件架构(2)第14张浅谈企业软件架构(2)第15张Codepublic Boolean Edit(Customer customer)
        {

            ITransaction tran 
= _session.BeginTransaction();
            
try
            {
                _session.SaveOrUpdate(customer);
                tran.Commit();
                
return true;
            }
            
catch
            {
                tran.Rollback();
                
if (_session.Contains(customer))
                {
                    _session.Evict(customer);
                }

                
return false;
            }

 

2.2.2.4.            删除Customer对象函数

浅谈企业软件架构(2)第16张浅谈企业软件架构(2)第17张Codepublic Boolean Delete(Customer customer)
        {
            ITransaction tran 
= _session.BeginTransaction();
            
try
            {
                _session.Delete(customer);
                tran.Commit();
                
return true;
            }
            
catch
            {
                tran.Rollback();
                
if (_session.Contains(customer))
                {
                    _session.Evict(customer);
                }
                
return false;
            }
        }

2.2.3.    添加Biz类库项目

 

解决方案中新建好Biz类库项目后,把Class.cs更名为CustomerBiz.csBiz层作为业务逻辑层(领域层)其包含系统的核心处理业务,其需要引用Dal层但是不需要知道Dal层的实现细节,所以它无须引用NHibernate.dll文件。这里是比较关键的变化,Biz层只关心Dal层提供的接口,也只跟接口打交道。如果在Biz层引用到了NHibernate提供的接口,我们的分层就没有达到分层封装的目的,这一点我们后面还会谈到它的好处。

   同样,先简单的实现AddGetEditDelete业务操作,实现代码如下:

浅谈企业软件架构(2)第18张浅谈企业软件架构(2)第19张Code System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Model;
using Dal;



namespace Biz
{
    
public class CustomerBiz
    {
        
private CustomerDal _customerDal = null;
        
public CustomerBiz()
        {
            _customerDal 
= new CustomerDal();
        }

 

添加对DalModel的项目引用,如下图:

浅谈企业软件架构(2)第20张 

  2.2.3.1.            实现简单的业务逻辑代码

浅谈企业软件架构(2)第21张浅谈企业软件架构(2)第22张Code Boolean Add(Customer customer)
        {

            
return _customerDal.Add(customer);
        }

        
public Customer Get(Int32 customerId)
        {

            
return _customerDal.Get(customerId);
            
        }
       
        
public Boolean Edit(Customer customer)
        {
            
return _customerDal.Edit(customer);
        }

        
public Boolean Delete(Customer customer)
        {
            
return _customerDal.Delete(customer);
        }

 

 

2.2.4.    修改界面层项目

现在我们来重构原来的Web项目,删除原来的NHibernate引用,添加ModelBiz项目引用。代码如下:

浅谈企业软件架构(2)第23张浅谈企业软件架构(2)第24张Code System;
using System.Collections;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

using Model;
using Biz;

namespace Demo
{
    
public partial class _Default : System.Web.UI.Page
    {
        
private Customer _customer;
        
private CustomerBiz _customerBiz;


        
protected void Page_Load(object sender, EventArgs e)
        {
            _customerBiz 
= new CustomerBiz();

        }

 

界面层只引用ModelBiz这样对于界面层来说Dal层是一个无需关心其是否存在的。它通过调用Biz层来实现系统的业务逻辑,继续重构我们的界面层代码,最终的结果如下:

浅谈企业软件架构(2)第25张浅谈企业软件架构(2)第26张Code void Add_Click(object sender, EventArgs e)
        {
            _customer 
= new Customer();
            _customer.Firstname 
= this.TextBox1.Text.Trim(); 
            _customer.Lastname 
= this.TextBox4.Text.Trim();
            _customer.Gender 
= this.TextBox2.Text.Trim();
            _customer.Address 
= this.TextBox3.Text.Trim();
            _customer.Remark 
= this.TextBox5.Text.Trim();
            _customer.CustomerId 
= Convert.ToInt32( this.Textbox_Id.Text.Trim());
            _customerBiz.Add(_customer);  
          
        }  

        
protected void Get_Click(object sender, EventArgs e)
        {
            _customer 
= _customerBiz.Get( Convert.ToInt32(Textbox_Id.Text.Trim()));
            
if (_customer != null)
            {
                
this.TextBox1.Text = _customer.Firstname;
                
this.TextBox4.Text = _customer.Lastname;
                
this.TextBox2.Text = _customer.Gender;
                
this.TextBox3.Text = _customer.Address;
                
this.TextBox5.Text = _customer.Remark;
            }
        }

        
protected void Edit_Click(object sender, EventArgs e)
        {
            _customer 
= _customerBiz.Get(Convert.ToInt32(Textbox_Id.Text.Trim()));
            _customer.Firstname 
= this.TextBox1.Text.Trim();
            _customer.Lastname 
= this.TextBox4.Text.Trim();
            _customer.Gender 
= this.TextBox2.Text.Trim();
            _customer.Address 
= this.TextBox3.Text.Trim();
            _customer.Remark 
= this.TextBox5.Text.Trim();
            _customerBiz.Edit(_customer);
        }
        
protected void Delete_Click(object sender, EventArgs e)
        {
            _customer 
= _customerBiz.Get(Convert.ToInt32(Textbox_Id.Text.Trim()));
            _customerBiz.Delete(_customer);

        }

注意代码发生的变化,界面层通过调用Biz层接口来实现业务逻辑的操作。到这里可能会有很多疑问,如Biz层还只是增、删、查、改这些操作呀!是的,如果只是简单的业务那么分层只会带来额外的工作量,问题的关键在于企业应用的实际情况就是业务总会越来越复杂、越来越庞大,当这个条件发生变化时,我们就会体会到分层带来的额外的好处,美妙的好处。 

 

2.3 结语

 

本章我们完成了一个三层结构的软件开发例子,从一个简单单层的例子进入到了有三个逻辑层次的软件架构:界面层、业务逻辑层和数据源层。通过面向对象的编程方式来达到分层解耦和的目的,当我们的领域逻辑(商业逻辑)是操作一个又一个的对象,而不是Record Set或者Table的时候,我们就会发现面向对象带来的极大好处,这几年随着各种O/R mapping的广泛使用,如:NHibernate,ADO.NET Entity Framework, Linq等;还有服务器硬件性能的提升,面向对象额外性能开销带来的成本,已经逐步小于其编程效率提高带来的效益。

可是在使用这些工具前,如果没有很好的面向对象的系统思考模式,往往也会把项目带另一极端,而使得项目走进过度设计或者过度使用灵活性带来的复杂度。同样把项目带入一个沼泽。在这里比较推崇“敏捷软件开发”的模式,尽量在开始的时候遵从简单而满足当前需要的方式,只有在需要时通过重构来进化你的软件架构使得满足新的需求。这样的好处是:不是在一开始去假定一个大而全的需求,而是逐步的挖掘需求来推进项目的“进化”,通过若干过迭代开发来完成最终交付的产品。

下一章,通过一个或两个不同的界面层来进一步描述分层给我们带来的好处。

免责声明:文章转载自《浅谈企业软件架构(2)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Oracle中rownum的基本用法Tomcat模型结构下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

将xls/csv数据导入到ES中

一.导入MySQL数据库中 (1)通过Navicat导入向导直接导入即可 (2)代码方式导入 【如果字段太多,这种方式做肯定不现实】 csv导入mysql数据库 -- 按需修改即可 package com.blb.mysql_java_es.util; import org.springframework.context.annotation.Bean...

Clickhouse TTL的那些事

TTL简介 ClickHouse原生支持数据生命周期(TTL)管理的功能 TTL即Time To Live,表示数据的存活时间。在MergeTree中,可以为某个列字段或者整张表设置TTL。当时间达到时,若列字段级别的TTL则会删除这一列的数据;若表级别的TTL则会删除整张表的数据;若同时设置了列级别的和表级别的TTL则以先到期的为准。 无论列级别还是表级...

SQL实现类似于自动刷新数据的功能

有时需要在SQL中,定时刷新某张表,比如说是要定时查询某张表的行数,通常做法就是手动的按F5去执行来刷新数据。但是如果这个定时查询历时较长,10分钟,或半小时,手动的话肯定是要崩溃了。貌似SQL没有像应用程序自动刷新的功能,可能是我还不知道有这样的功能,于是就写了一个类似自动刷新数据的脚本,需要手动中止程序的运行,当然也可以写个条件,自动结束语句的执行。...

SpringCloud 之 Netflix Hystrix 服务监控

本文较大篇幅引用https://www.mrhelloworld.com/hystrix-dashboard-turbine/,相关内容版权归该文章作者所有 引用上篇文章的工程数据   Actuator Hystrix 除了可以实现服务容错之外,还提供了近乎实时的监控功能,将服务执行结果和运行指标,请求数量成功数量等等这些状态通过 Actuator 进行收...

【C#、csharp】HTTPGET,POST请求

HTTP定义了与服务器交互的不同方法,基本方法有GET,POST,PUT,DELETE,分别对于查,该,增,删。一般情况下我们只用到GET和POST,其他两种都也可以用GET和POST来实现,很多浏览器也只实现了GET和POST两种方法   GET:用户获取信息     安全的:不会对服务器上的数据进行修改,和破坏     幂等的:相同的请求所返回的结果相...

【MySQL】MySQL中的锁机制

MySQL锁机制起步 锁是计算机用以协调多个进程间并发访问同一共享资源的一种机制。MySQL中为了保证数据访问的一致性与有效性等功能,实现了锁机制,MySQL中的锁是在服务器层或者存储引擎层实现的。 行锁与表锁 首先我们来了解行锁与表锁的基本概念,从名字中我们就可以了解:表锁就是对整张表进行加锁,而行锁则是锁定某行、某几行数据或者行之间的间隙。 各引擎对锁...