我對大型系統的理解,從數量上面來說,源代碼超過百萬行以上,系統有超過300個以上的功能,從質量上來說系統應該具有良好的可擴展性和可維護性,系統中的功能緊密關聯。除去業務上的複雜性,如何設計這樣的一個協做良好的系統,搭建開發人員基礎平臺,一直是我研究的方向。html
SouceCounter(版本3.3.91.79)對源代碼的統計信息以下:程序員
下面來詳細解析一下這個系統的設計架構,純.NET技術架構方案,C/S WinForms系統。數據庫
系統分爲Framework和Application兩個部分,前者是框架(Framework),包含核心的基礎功能,如公共類庫,許可受權,數據字典,公共控件,公共窗體,多線程組件,通訊基礎,會話管理等基礎部分,後者是應用部分,根據業務邏輯的不一樣,對於ERP系統而言,可分爲進銷存(Distribution),工程(Engineering),生產(Production),生產計劃(Production Planning),財務(Account Receivable,Account Payable,General Ledger),客戶關係(CRM)等模塊。瀏覽器
public const string Major = "3"; public const string Minor = "4"; public const string Build = "0"; public const string Revision = "0";
public enum LicenseType { Internal, Standard, Enterprise }
設計精良的系統應該先預約義好一系列的基礎界面,用於管理框架功能中的元數據,這一節分兩個部分講解,一是框架要定義什麼,二是如何去實現這些功能。先來看一下框架數據庫Framework有哪些基礎的元數據表定義:性能優化
先寫一個基礎查詢語句,用於查詢框架系統數據庫的全部表。服務器
SELECT * FROM sys.tables ORDER BY name
數據表 | 定義 |
Attachment | 系統中全部功能的附件,可直接存儲文件或是存放一個FTP文件路徑 |
Branch / BranchDetail多線程 |
實現多庫存組織 |
Company | 實現多公司賬套 |
Configuration | 系統參數配置 |
ContextFunction ContextFunctionDetail架構 |
系統上下文菜單項定義 |
Dictionary |
可變的數據字典定義,用於可修改的數據字典 |
FormLayout |
自窗義界面(Form Designer設計以後保存的Xml文件) |
FormProfile | 用戶偏號(網格排序,控件位置或大小拖動) |
LanguageTranslation | 多國語言翻譯 |
Component | 系統啓動時讀取的程序集 |
Lookup | 查找數據對話框 |
Message MessageDetail |
消息盒子 |
Report | 報表參數定義 |
ScheduledTask | 系統預置的計劃任務 |
SystemModule SystemFunction |
系統包含的模塊與功能 |
User | 系統用戶 |
UserDefinedQuery | 預約義查詢 |
UserGroup | 用戶組別 |
UserGroupAuthorization | 用戶組別受權 |
UserGroupMenu | 用戶組別菜單 |
UserGroupMenuBitmap | 用戶組別導航圖 |
UserLog | 用戶日誌 |
Workflow | 工做流定義 |
經過上面框架數據庫表的定義便可看到框架的基礎功能,也就是對以上數據進行讀寫。我按照窗體的類別簡單介紹。
工做流實現的四大基礎功能:通知提醒,批覈,計劃任務,調用自定義代碼。
通知提醒:ERP系統中包含大量的提醒功能,每加一個業務功能就寫一遍提醒功能的代碼調用顯得有些繁瑣,在此只作一個設置便可實現通知設置,包括要通知的人員,通知的內容。
批覈:框架應該抽象出全部單據的批覈需求,創建一個獨立的批覈系統,任何功能只須要簡單設置一下便可調用工做流代碼,實現批覈流程。
計劃任務: 系統中有一些按期執行的任務,好比員工生日提醒,庫存餘額報警,待收貨記錄提醒,老闆報表定時發送。
調用自定義代碼:若是系統中的功能存在缺陷defect,軟件公司不肯意修改的狀況下,能夠考慮增長自定義的.NET代碼解決問題。只須要在合理的事件點上插入合適代碼,完成重複的數據修復工做。
基於微軟工做流的解決方案,先看一下包含的基礎組件:
Activities 活動庫,活動是工做流定義中一個基本的代碼執行單元,可固化執行的代碼都可封裝到一個活動中。包含文檔批覈活動,發送報表活動,查詢活動,調用.NET代碼活動等。調用.NET代碼活動的設定方法參考以下:
assembly=Microsoft.Applications.MyReportDll; class=Microsoft.Applications.MyReportDll.EmployeelistingDAL; method=GetEmployeeListing();
熟悉.NET框架反射調用方法的朋友一看就明白上面定義的含義,這個活動的源代碼能夠簡化以下所示:
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { assembly = Assembly.Load(segments[0]); type = assembly.GetType(segments[1]); method = segments[2]; type.GetMethod(method, BindingFlags.Public | BindingFlags.Static).Invoke(null, null); }
Contracts 接口與實現 在第一步活動定義中,大量的調用了WF中的CallExternalMethod活動,這個活動用於調用外部自定義方法,實現接口與實現的分離。在設計活動時只須要指定要調用的接口和方法,具體的實現可根據須要變化。
.NET WF要求在啓動WF Runtime時,須要先註冊要執行的服務,代碼參考以下:
SqlWorkflowPersistenceService persistenceService=new SqlWorkflowPersistenceService (ConnectionString);
runtime.AddService(persistenceService);
在活動的代碼中,可經過如下的方式引用通過註冊的服務:
SqlWorkflowPersistenceService persistenceService=context.GetService<SqlWorkflowPersistenceService>(); //調用 persistenceService服務的方法
這樣,服務中可調用ERP代碼的接口,實現了ERP邏輯與.NET WF工做流的整合。
Workflows 給每種常見的流程定義一個工做流類型,方便作持久化和驗證工做。
Workflow Designer Control re-host工做流定義組件,直接參考借用MSDN中的例子。
Monitor 工做流監控,查看流程的執行狀況,當前執行結點,執行路徑。
從功能上來說,系統應該具有如下四個基礎服務器,實現數據讀寫分離。
Application Server 業務邏輯服務器,.NET Remoting服務器端。
Report Server 報表服務器,用於報表呈現,減低Application Server的壓力。
Workflow Server 工做流服務器 執行工做流。
Scheduling Server 計劃任務服務器,減低Application Server的壓力。
每種服務器都配置Console版和Service版,代碼徹底同樣,Console以控制檯程序呈現,Service以Windows 服務應用形式實現,前者方便開發,後者用於部署和實際使用。
根據ERP項目的功能分類,分爲進銷存(Distribution),工程(Engineering),生產(Production),生產計劃(Production Planning),財務(Account Receivable,Account Payable,General Ledger),客戶關係(CRM)等模塊。每一個模塊獨立爲一個Visual Studio Project,編譯成一個程序集。經過插件式結構,實現使用時只須要在Component表中插入一行記錄,便可讓系統識別到此程序集,運用反射方法調用程序集中的功能。
若是是用LLBL Gen Pro開發系統,則業務實體層具有如下文件夾層次結構。
以銷售單表頭爲例子,數據庫表SalesOrder,生成實體爲SalesOrderEntity,設計讀寫接口文件爲ISalesOrderManager, 接口的實現類爲SalesOrderManager,用簡單的圖表示以下:
SalesOrder –> SalesOrderEntity –> ISalesOrderManager –> SalesOrderManager
系統強制執行以上約定,而且設計了Code Smith模板代碼生成來減小出錯的可能。既提供強制性約束,又提供工具輔助開發人員遵照約定,系統開發效率成倍提高。
業務實體層還實現了數據審計(Audit)功能,記錄表的每一個記錄的修改值。剛畢業參加工做時,經常混淆Audit和Approval的區別,如今一些系統還存在用Audit做爲批覈的意義。
定義系統中不可變的數據字典,雖然用代碼寫死字典的方法值得商議,但它的好處也是很是明顯的。
定義一個勞動合同的枚舉,分固按期限和無固按期限的合同,參考下面的代碼。
public enum ContractType { [StringValue("F")] [DisplayText("Fixed Time")] FixedTime, [StringValue("U")] [DisplayText("Unlimited Time")] UnlimitedTime }
獲取它的值用以下方法,值用於存儲到數據庫中或程序代碼使用:
StringEnum<ContractType>.GetStringValue(ContractType.FixedTime)
獲取它的描述用以下方法,描述用於界面中呈現:
StringEnum<ContractType>.GetDisplayText(ContractType.FixedTime)
一部分邏輯在Business Logic中實現,好比類型初始化值,自動帶值,值驗證等邏輯。複雜的邏輯好比進出倉,涉及到的關聯表會多一些,可能要扣減庫存,修改物料庫存餘額,批號生成,生成出倉平均單價等,複雜的邏輯要單獨放在一個項目中設計源代,這樣方便維護。
從形式上來分,分爲圖形報表和數據報表。圖形報表用微軟.NET 自帶的圖形控件完成,MSDN上包含Samples Environments for Microsoft Chart Controls的大量例子。數據報表用水晶報表完成,600多個水晶報表文件,涵蓋單據的打印,列表查詢打印,主檔數據打印等類別。豐富的水晶報表功能爲系統增色很多。
集成一些經常使用的功能,不須要進入系統便可完成系統維護。好比數據庫升級,數據庫備份,數據庫還原,新賬套建立,系統參數設定,數據庫性能優化(主要是索引重建),這些實用工具程序減輕了系統管理員的負擔。
Distribution 進銷存,包含銷售,採購,倉庫模塊。
模塊 | 功能 |
Sales 銷售 | 銷售合同,銷售訂單,送貨,退貨,銷售包裝,銷售發票 |
Purchasing 採購 | 採購申請,採購訂單,採購收貨,採購退貨,供應商發票 |
Inventory 倉庫 | 進倉(Receipt),出倉(Issue),轉倉(Transfer),盤點(Cycle-count) |
Engineering 工程 物料清單,標準成本設定。
Production 生產 工做單,工做單發料,倒衝與組裝,物料退回,發散料。
Production Planning 生產計劃 主生產計劃MPS,物料需求計劃MRP, 能力需求計劃CRP
Finance 財務 Account Receivable應收,Account Payable應付,General Ledger總賬。
CRM 客戶關係 銷售線索,銷售日報,銷售月報,出差申請與費用報銷。
在個人從業經歷以來,我認爲搭建一套開發框架對企業開發是頗有用處的。前期所花費的精力和時間在後期都會獲得充分的回報。然而搭建框架所花費的時間和精力,值得商榷。公司一直都是對股東負責,能用最少的時間作完項目,收回合同款便可,大費周折的去作產品基礎功能,對於小公司而言生存都是問題,精心設計的框架須要大量的精力去維護和改善,拋開公司因素,學習一個大型系統對我的的職業發展和成長也是至關重要的。