這幾天園子裡關於軟體架構的討論還是相當激烈,大家都想要一種能最大限度的降低各層之間依賴關係的的架構來適應變化的需求,誰都不喜歡改一點而動全身,盡量降低各層的改動產生的相互影響。
本篇我以理論和實踐(原始碼)兩個方面和大家探討一下我的方案,希望大家多提寶貴意見。
一、軟體架構的概念問題,什麼是軟體的架構?我的理解是:軟體的架構包括兩個方面的內容,一個是軟體的開發架構,一個是軟體的部署架構,所謂部署架構就是指部署時的分布式,叢集等設計問題;開發架構就是我們平常說的軟體分層設計問題,也就是我們今天要談的問題。
二、何謂分層?分層的方式有幾種?分層也就是把一個大的軟體解決方案分成多重專案進行開發,分為三種,一種是按照代碼的功能層次進行分層,分為資料庫訪問層,商務邏輯層,UI層等,一種是按照要實現的功能模組進行分層,例如新聞管理層,部落格管理層等,第三種就是把前兩者結合起來進行分層:先按照代碼功能劃分好層次,然後再在每一層中分層成各個功能模組。
三、面向介面能夠降低各層之間的依賴關係嗎?還需要什嗎?面向介面只是把對對象的直接調用轉到了對介面的調用身上,但對介面的調用也需要對象的執行個體化為前提,因為有對象執行個體化的存在,所以介面並不能屏蔽掉背後的對象來完成功能;在這種情況下,依賴注入/控制反轉等概念應運而生,IOC容器出現了,它能夠讓對象自動執行個體化,並管理對象的生存空間。
四、說了這麼多,到底應該怎樣分?我們不妨從軟體的開發過程上來考慮,做軟體的都知道我們要進行需求分析,要有需求分析文檔,需求分析的任務就是把客戶的需求轉變為對軟體功能的需求,主要內容包括:軟體要實現那些功能模組,每個功能模組下包含哪些業務操作,這些業務操作需要哪些頁面(或者視窗)支援,這些頁面大概都是什麼樣子的,上面都有什嗎?有了這些資訊,我們就可以開始我們的系統設計了。
我的方案中各個項目的參考關聯性如下:
說明:
Web:UI層,Asp.net Web應用程式項目,提供使用者介面。
UnityConfig:依賴注入配置層,類庫項目,對Service層和Business層進行實現配置以提供給Web調用Service層。
Service:為Web層提供服務,類庫項目,裡面全是介面。
Business:商業邏輯層,通過調用DA資料訪問層和Entities層來實現Service層的服務介面。類庫項目。
Entities:實體層,類庫項目,存放業務實體,貧血型實體。
請注意:在上面的參考關聯性中,Web層並沒有直接引用Business層。
現在假設我們有這樣一個需求文檔:它只需要實現一個功能模組,就是尋找並顯示僱員列表。頁面形式
首先我們根據需求,先設計服務,因為只有一個功能模組,所以我們只需要一個介面類,再因為只有一個操作功能(尋找),所以我們只需要一個服務方法,那麼我們在Service層建立一個介面類:IQueryEmployeeService,並添加一個介面方法:QueryEmployee.
根據頁面所示,我們這個服務方法需要兩個對象參數,一個是尋找的條件,一個是尋找的結果;這樣我們就得到了兩個實體類。在Entities建立分別命名為QueryEntry和ListEntry。
代碼如下:
namespace Xiaozhuang.Service
{
public interface IQueryEmployeeService
{
/// <summary>
/// 查詢僱員資訊
/// </summary>
/// <param name="queryentity"></param>
/// <returns></returns>
List<ListEntry> QueryEmployee(QueryEntry queryentry);
}
}
namespace Xiaozhuang.Entities
{
public class QueryEntry
{
public string DepartmentID { get; set; }
public string EmployeeName { get; set; }
public string EmployeeAge { get; set; }
public override string ToString()
{
return "DeaprtmentID:" + DepartmentID + "EmployeeName:" + EmployeeName + "EmployeeAge:" + EmployeeAge;
}
}
}
namespace Xiaozhuang.Entities
{
public class ListEntry
{
public string EmployeeID { get; set; }
public string EmployeeName { get; set; }
public string EmployeeSex { get; set; }
public int EmployeeAge { get; set; }
public string DepartmentName { get; set; }
public string MobilePhone { get; set; }
public override string ToString()
{
return "EmployeeID:" + EmployeeID + "EmployeeName:" + EmployeeName + "EmployeeSex:" + EmployeeSex;
}
}
}
請注意,業務實體的設計完全是參照頁面而來,對實體欄位類型的設計也是參照頁面而來,多數都是String類型,因為這個時候並不知道打算在資料庫中怎麼儲存這些資料。
當然在實際的項目中,因為那個部門列表還需要建立部門的業務實體,這裡簡單其間略去,有了這些業務實體和服務介面,我們就可以設計資料庫了,資料庫設計完成後,系統設計的工作就完成了。
接下來就是在Business層具體的實現這個尋找的方法了:
namespace Xiaozhuang.Business
{
public class QueryEmployeeBusiness :IQueryEmployeeService
{
#region IQueryEmployeeService 成員
public List<ListEntry> QueryEmployee(QueryEntry queryentry)
{
List<ListEntry> listEntry = new List<ListEntry>();
ListEntry entry1 = new ListEntry() { EmployeeID = "1", EmployeeName = "僱員1", EmployeeSex = "男", DepartmentName = "部門1", EmployeeAge = 30, MobilePhone = "123546789" };
ListEntry entry2 = new ListEntry() { EmployeeID = "2", EmployeeName = "僱員2", EmployeeSex = "女", DepartmentName = "部門2", EmployeeAge = 29, MobilePhone = "123546789" };
listEntry.Add(entry1);
listEntry.Add(entry2);
return listEntry;
}
#endregion
}
}
此處略去從資料庫查詢的方法,可以用Linq to sql 實現或者其他Orm工具。
接下來配置一下我們的UnityConfig層,就可以在Web層訪問這個尋找僱員的服務了。
namespace Xiaozhuang.UnityConfig
{
public interface IContainerAccessor
{
IUnityContainer Container { get; }
}
public class UnityContainerConfig
{
public IUnityContainer GetIUnityContainer()
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IQueryEmployeeService, QueryEmployeeBusiness>();
return container;
}
}
}
當然你也可以把這個配置寫到Web。Config中。
接下來的問題就是怎樣在Web層調用這個服務的問題了,這個問題其實還是比較複雜的,文章太長了大家都看煩了,我將在下次再詳細說說這個問題。