首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WCF RIA Services 企業級架構模式實戰:從 Smart UI 到 Bounded Context

     編輯:關於.NET

前言:跨越「資料鴻溝」的銀彈?

Silverlight 4 帶來印表、提權、WebCam 等豐富能力,但業務開發核心痛點仍是:**如何將伺服器端業務邏輯與資料,安全、高效地投射到客戶端?** WCF RIA Services (舊稱 .NET RIA Services) 正是為此而生。本文以 KharaPOS (銷售點系統) 為例,展示 RIA Services 如何自然承載 Smart UI / Transaction Script / Domain Model / Application Service Layer / Bounded Context 五大企業應用架構模式 (參考 Fowler《企業應用架構模式》)。

KharaPOS 子系統:目錄管理

目錄管理實體模型

  • 核心實體:Product、CatalogEntry、BusinessUnit、Manufacturer。
  • 技術棧:Silverlight 4 + EF 4 + RIA Services + SQL Server 2008 (目標 Azure)。
  • 原始碼:CodePlex。

模式一:Smart UI (Forms and Controls) —— RAD 極致

「把邏輯寫在 UI 裡」聽起來是反模式,但對「查詢/編輯字典表」極其高效。RIA Services + VS 設計器 5 步驟產出可編輯 DataGrid:

  1. 新建 Silverlight Business Application。
  2. Web 專案加 ADO.NET Entity Data Model (Database First)。
  3. Web 專案加 Domain Service Class (勾選實體)。
  4. Silverlight 專案 Data Sources 面板拖曳實體到頁面 → 自動生成 DomainDataSource + DataGrid。
  5. 加按鈕 + 一行代碼:this.categoryDomainDataSource.SubmitChanges();

適用:簡單管理介面 (產品目錄、員工字典)。建議:僅用於低複雜度 CRUD,避免邏輯外洩。

模式二:Transaction Script —— 協調多實體的服務端腳本

當業務涉及多實體協調 (如「為業務單元建立目錄條目」),UI 直接操作 CatalogEntry 過於繁瑣且易出錯。解法:在 Domain Service 暴露粗粒度方法:


public void CatalogProductForBusinessUnit(Product product, int businessUnitId)
{
    var entry = ObjectContext.CreateObject();
    entry.BusinessUnitId = businessUnitId;
    entry.ProductId = product.Id;
    entry.DateAdded = DateTime.Now;
    ObjectContext.CatalogEntries.AddObject(entry);
    ObjectContext.SaveChanges();
}
  • 客戶端呼叫:context.CatalogProductForBusinessUnit(product, buId); context.SubmitChanges();
  • RIA 自動在實體產生對應呼叫方法,執行於**伺服器端**,保護核心邏輯與資料完整性。
  • 不公開 CatalogEntry 給客戶端,強制走腳本,杜絕邏輯碎片化。

進階技巧:自定義服務方法僅能接受**一個實體參數**,需傳多實體時,可用 HttpContext.Current.Session 暫存:


public void StoreBusinessUnit(BusinessUnit bu)
    => HttpContext.Current.Session[typeof(BusinessUnit).FullName + bu.Id] = bu;

public void CatalogProductForBusinessUnit(Product product, int businessUnitId)
{
    var bu = (BusinessUnit)HttpContext.Current.Session[typeof(BusinessUnit).FullName + businessUnitId];
    // 使用 bu...
}

模式三:Domain Model —— 富行為實體與領域邏輯封裝

當規則複雜 (如「依歷史銷售比例自動選擇季節性產品」),Transaction Script 會淪為巨大的 if-else 堆砌。轉向 Rich Domain Model:將邏輯封裝於實體內部。

實體擴充 (shared.cs 讓 RIA 同步至客戶端)


public partial class BusinessUnit
{
    public void SelectSeasonalProductsForBusinessUnit(DateTime seasonStart, DateTime seasonEnd)
    {
        // 1. 計算季度總銷售額
        var totalSales = (from s in Sales
                          where s.DateOfSale > seasonStart && s.DateOfSale < seasonEnd
                          select s.LineItems.Sum(l => l.Cost)).Sum();

        // 2. 取得該業務單元涉及的廠商
        var manufacturers = Catalogs.Select(c => c.Product.Manufacturer).Distinct();

        // 3. 廠商分組統計銷售佔比
        var salesByMfr = (from s in Sales
                          where s.DateOfSale > seasonStart && s.DateOfSale < seasonEnd
                          from li in s.LineItems
                          join m in manufacturers on li.Product.ManufacturerId equals m.Id
                          select new { Manufacturer = m, Amount = li.Cost })
                         .GroupBy(x => x.Manufacturer);

        // 4. 依佔比決定目錄項目
        foreach (var g in salesByMfr)
            SelectCatalogItemsBasedOnPercentage(g.Key, g.Sum(x => x.Amount) / totalSales);
    }

    private void SelectCatalogItemsBasedOnPercentage(Manufacturer m, decimal pct) { /* ... */ }
}
  • 客戶端呼叫:bu.SelectSeasonalProductsForBusinessUnit(start, end); context.SubmitChanges();
  • 邏輯**客戶端執行**,提交時持久化變更集。
  • 命名即文檔:方法名清楚表達意圖,新成員無需讀實現即懂用途 (Evans《領域驅動設計》)。

模式四:Application Service Layer —— 對外門面,內藏領域

若不想讓客戶端直接操作領域模型 (保護不變量、隱藏依賴),在 Domain Service 再包一層薄外觀:


// Domain Service (Application Service Layer)
public void ExecuteSeasonalSelection(int businessUnitId, DateTime start, DateTime end)
{
    var bu = ObjectContext.BusinessUnits.Single(b => b.Id == businessUnitId);
    bu.SelectSeasonalProductsForBusinessUnit(start, end); // 委派給領域模型
    ObjectContext.SaveChanges();
}
  • 客戶端只見 ExecuteSeasonalSelection,不知內部領域結構。
  • 領域實體可引用內部服務/值物件,客戶端無需知曉。
  • 警惕:外觀過厚會退化為 Transaction Script,失去領域封裝優勢。

模式五:Bounded Context —— 大系統拆解的邊界

KharaPOS 同時擁有 Catalog Context (目錄) 與 Sales Context (銷售),共享 Product/BusinessUnit/LineItem 等實體,但規則不同:

  • Catalog Context:Sale/LineItem 為**唯讀**參考。
  • Sales Context:完整讀寫銷售流程。
  • 跨 Context **禁止分散式事務** (Silverlight 不支援),操作不跨邊界。

對應 RIA Services:建立兩個 DomainService (CatalogDomainService, SalesDomainService),各自引用對應 EF Model,共享實體類放在共用組件 (或 shared.cs)。

模式對照與選型指南

模式適用場景RIA 實現關鍵邏輯執行位置
Smart UI簡單字典/主檔維護DomainDataSource + DataGrid客戶端 (UI 代碼)
Transaction Script多實體協調、伺服器端強控制DomainService 公開方法、Session 暫存伺服器端
Domain Model複雜規則、頻繁變更、需封裝partial class + shared.cs、虛擬屬性客戶端 (提交時持久化)
Application Service Layer對外 API 穩定、隱藏領域細節DomainService 門面方法委派領域伺服器端
Bounded Context大系統多子域、共享實體規則異多 DomainService + 共用實體組件視具體模式而定

成功的陷阱與結語

RIA Services 讓從 Smart UI 到 Bounded Context 的演進極為平滑,**但也正因太容易,團隊易陷入「Smart UI 滿天飛」的成功陷阱** (Brad Abrams)。架構師需把關:依複雜度選擇適當模式,嚴守邊界,守護領域純粹性。KharaPOS 原始碼開源,歡迎實地觀摩!

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved