若是你正在學習ASP.NET MVC 3,HTML5,jQuery和瀏覽器客戶端交互技術,推薦你下載Mileage Stats 範例程序,可更好理解如何使用當前技術建立當前的web應用程序,尤爲關注如何架構一個企業級的應用程序。關於Mileage Stats項目的初步介紹,請參考《Project Silk – 基於ASP.NET MVC 3 的示例應用程序Mileage Stats》。web
EntLib.com Team 嘗試從架構的角度對MileageStats項目進行分析和解讀,並計劃運用到實際的電子商務系統中,歡迎你們參與交流和分享。瀏覽器
MileageStats RI 運行的主要界面:架構
MileageStats RI 項目的當前架構圖,主要有Web 表示層、業務邏輯層和數據訪問層,以下圖所示。app
簡要看看MileageStats 包含的一些主要項目:dom
建立數據模型(Data Model)ide
MileageStats.Model 項目包含數據模型(Data Model)。結構化和強類型的類描述了業務數據的數據類型、關係和約束。學習
實現Repository Patternthis
在Repository模式中,Repository是一組接口,實現了數據訪問相關的方法。接口沒有暴露任何特定數據存儲相關的類型。spa
MileageStats.Data 項目包含了Repository接口,MileageStats.Data.SqlCe 項目包含了接口的實現。以下是IReminderRepository 接口的示例代碼:.net
// 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應用程序保持Controller和Action方法比較小,View比較簡單。大部分的核心應用程序邏輯存放在Model中。在MVC應用程序建立時,保持DRY(Don’t Repeat Yourself)原則比後期試圖清理代碼更容易。
由於大部分應用程序邏輯在Model層中,所以不少MVC 應用程序包含不一樣類型的Model:
l View models(視圖模型) - 僅用於視圖的數據綁定,這些Models包含於MVC 應用程序中,通常與Views和Partial Views保持相同的構成結構。
l Application, domain, or service models(業務模型) - 基於實際業務須要創建的數據模型,可添加屬性標註,或擴展支持應用功能,如數據驗證、身份驗證。由於這些Models易於往返於客戶端瀏覽器,所以它們常常包含於View Models,並直接在HTML 表單進行數據綁定。
l Data models(數據模型) - 用於數據服務和存儲,不會暴露在應用程序以外,常常封裝在服務層。
以下是MileageStats RI Solution中包含的3個Model項目 – 下圖中選中的項目,分別在Web層、業務邏輯層和數據層:
對於比較複雜或長期維護的應用程序,應該分離業務模型和數據模型。若是業務模型和數據模型的層級和接口差別很大,則建立徹底分離的類。如業務模型和數據模型有匹配的層級和兼容的接口,則建議業務模型類繼承數據模型類。如業務模型和數據模型有匹配的層級,但接口不兼容(如數據模型類接口不適合於業務模型類),則在業務模型類中經過彙集關係,包含數據模型類實例。
在編寫Controller Action方法時,應將一些複雜的方法包裝爲model和service層的輔助方法或類中。優先採用action過濾器屬性,如HttpPostAttribute,避免在每個action方法中檢測HttpContext,編寫邏輯判斷。此外,使用action過濾器進行橫切關切點(Cross-cutting concern),如認證(AuthorizeAttribute)、錯誤處理(HandleErrorAttribute)等等。處理GET請求的方法應僅包含一些方法調用,而沒必要包含太多業務判斷邏輯;處理POST 請求的方法應驗證傳入的數據,在數據合法的狀況下,執行更新操做,並根據更新結果,返回對應視圖。MileageStats RI 應用程序的以下範例顯示2個版本的Add方法(分別爲GET和POST版本):
// 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/