Project Silk – Mileage Stats 項目架構初步分析(ASP.NET MVC 3)

 如果你正在學習ASP.NET MVC 3HTML5jQuery和瀏覽器客戶端交互技術,推薦你下載Mileage Stats 範例程序,可更好理解如何使用當前技術創建當前的web應用程序,尤其關注如何架構一個企業級的應用程序。關於Mileage Stats項目的初步介紹,請參考《Project Silk  基於ASP.NET MVC 3 的示例應用程序Mileage Stats》。

 

EntLib.com Team 嘗試從架構的角度對MileageStats項目進行分析和解讀,並計劃運用到實際的電子商務系統中,歡迎大家參與交流和分享。

 

MileageStats RI 運行的主要界面:

 


 

 

MileageStats RI 項目的當前架構圖,主要有Web 表示層、業務邏輯層和數據訪問層,如下圖所示。

 

 


 

 

簡要看看MileageStats 包含的一些主要項目:

 


 

 

 

創建數據模型(Data Model

MileageStats.Model 項目包含數據模型(Data Model)。結構化和強類型的類描述了業務數據的數據類型、關係和約束。

 

實現Repository Pattern

Repository模式中,Repository是一組接口,實現了數據訪問相關的方法。接口沒有暴露任何特定數據存儲相關的類型。

MileageStats.Data 項目包含了Repository接口,MileageStats.Data.SqlCe 項目包含了接口的實現。如下是IReminderRepository 接口的示例代碼:

// contained in IReminderRepository.cs

public interface IReminderRepository

{

void Create(int vehicleId, Reminder reminder);

Reminder GetReminder(int reminderId);

void Update(Reminder reminder);

void Delete(int reminderId);

 

IEnumerable<Reminder> GetRemindersForVehicle(int vehicleId);

IEnumerable<Reminder> GetOverdueReminders(int vehicleId,

DateTime forDate, int forOdometer);

 

IEnumerable<Reminder> GetUpcomingReminders(int vehicleId,

DateTime forStartDate, DateTime forEndDate,

int odometer, int warningOdometer);

 

IEnumerable<Reminder> GetFulfilledRemindersForVehicle(int vehicleId);

}

 

分解應用程序代碼到ASP.NET MVC模式

設計良好的MVC應用程序保持ControllerAction方法比較小,View比較簡單。大部分的核心應用程序邏輯存放在Model中。在MVC應用程序創建時,保持DRYDon’t Repeat Yourself)原則比後期試圖清理代碼更容易。

 

因爲大部分應用程序邏輯在Model層中,因此很多MVC 應用程序包含不同類型的Model

View models(視圖模型) - 僅用於視圖的數據綁定,這些Models包含於MVC 應用程序中,一般與ViewsPartial Views保持相同的構成結構。

Application, domain, or service models(業務模型) - 基於實際業務需要建立的數據模型,可添加屬性標註,或擴展支持應用功能,如數據驗證、身份驗證。因爲這些Models易於往返於客戶端瀏覽器,因此它們經常包含於View Models,並直接在HTML 表單進行數據綁定。

Data models(數據模型) - 用於數據服務和存儲,不會暴露在應用程序之外,經常封裝在服務層。

 

如下是MileageStats RI Solution中包含的3Model項目 – 下圖中選中的項目,分別在Web層、業務邏輯層和數據層:

 


 

 

對於比較複雜或長期維護的應用程序,應該分離業務模型和數據模型。如果業務模型和數據模型的層級和接口差異很大,則創建完全分離的類。如業務模型和數據模型有匹配的層級和兼容的接口,則建議業務模型類繼承數據模型類。如業務模型和數據模型有匹配的層級,但接口不兼容(如數據模型類接口不適合於業務模型類),則在業務模型類中通過聚集關係,包含數據模型類實例。

 


 

在編寫Controller Action方法時,應將一些複雜的方法包裝爲modelservice層的輔助方法或類中。優先採用action過濾器屬性,如HttpPostAttribute,避免在每一個action方法中檢測HttpContext,編寫邏輯判斷。此外,使用action過濾器進行橫切關切點(Cross-cutting concern),如認證(AuthorizeAttribute)、錯誤處理(HandleErrorAttribute)等等。處理GET請求的方法應僅包含一些方法調用,而不必包含太多業務判斷邏輯;處理POST 請求的方法應驗證傳入的數據,在數據合法的情況下,執行更新操作,並根據更新結果,返回對應視圖。MileageStats RI 應用程序的如下範例顯示2個版本的Add方法(分別爲GETPOST版本):

        // GET: /Fillups/Add/1

        public ActionResult Add(int vehicleId)

        {

            var vehicles = this.businessServices.GetVehicles(this.User.MileageStatsIdentity().UserId);

            Vehicle vehicle = vehicles.First(v => v.VehicleId == vehicleId);

 

            var newFillupEntry = new FillupEntry()

            {

                Odometer = vehicle.Odometer.HasValue ? vehicle.Odometer.Value : 0

            };

            var fillups = this.GetVehicleFillupsDescending(vehicleId);

 

            var model = new FillupViewModel()

            {

                VehicleList = new VehicleListViewModel(vehicles, vehicle) { IsCollapsed = true },

                Fillups = new SelectedItemList<FillupEntry>(fillups, newFillupEntry),

                FillupEntry = newFillupEntry

            };

 

            this.ViewBag.IsFirstFillup = (fillups.Count == 0);

 

            return this.View(model);

        }

 

        //

        // POST: /Fillups/Add/5

        [HttpPost]

        [ValidateInput(false)]

        [ValidateAntiForgeryToken]

        public ActionResult Add(int vehicleId, FillupEntry model)

        {

            if (this.ModelState.IsValid)

            {

                this.AddModelErrors(this.businessServices.CanAddFillup(this.CurrentUserId, vehicleId, model),

                                    "AddFillup");

 

                if (this.ModelState.IsValid)

                {

                    this.businessServices.AddFillupToVehicle(this.CurrentUserId, vehicleId, model);

                    this.TempData["LastActionMessage"] = Resources.VehicleController_AddFillupSuccessMessage;

                    return this.RedirectToAction("List""Fillup"new { vehicleId = vehicleId });

                }

            }

 

            var vehicles = this.businessServices.GetVehicles(this.CurrentUserId);

            Vehicle vehicle = vehicles.First(v => v.VehicleId == vehicleId);

            var fillups = this.GetVehicleFillupsDescending(vehicleId);

 

            var viewModel = new FillupViewModel()

            {

                VehicleList = new VehicleListViewModel(vehicles, vehicle) { IsCollapsed = true },

                Fillups = new SelectedItemList<FillupEntry>(fillups, model),

                FillupEntry = model

            };

 

            this.ViewBag.IsFirstFillup = (fillups.Count == 0);

 

            return this.View(viewModel);

        }

 

 

依賴注入 - Dependency Injection

解耦應用程序的組件(Decoupling the application components)。關於Unity 2.0 依賴注入容器在ASP.NET MVC 3 項目的具體使用細節,可參考文章 - ASP.NET MVC 項目使用Unity 2.0實現依賴注入

 

創建Business Service 

爲了項目的可維護和支持不同類型的客戶端,大型和複雜的應用程序經常需要額外的架構層 – Business Service Layer,將業務邏輯從數據訪問層分離出來。

 

本文參考和編譯瞭如下文章部分內容:

Project Silk 1.0 – Server-side Architecture

Project Silk - http://silk.codeplex.com/

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章