從產品展示頁面談談Hybris系列之二: DTO, Converter和Populator
文章作者:張健(Zhang Jonathan)
上一篇文章 從產品展示頁面談談Hybris的特有概念和設計結構 我們講解了Hybris一些特有的概念以及大體架構,並且介紹了Facade層裏是如何定義DTO(Data Transfer Object)對象。
一個尚未回答的問題: 為什麽DTO(在上一篇文章的具體例子裏是Java類ProductData)會由Converter來生成?
這篇文章就從此問題開始。
我們再次翻出上一篇文章展示過的這張架構圖。
當我們打開一個Hybris應用網頁,比如某個Product(產品)的明細頁面時, 背後實際上執行了下列的邏輯:
Service層從數據庫裏把數據取出,以Model(又稱為DAO對象)的形式返回給Facade層。
Facade層調用Converter, 在Populator的幫助下,基於Model生成了DTO。
明細頁面的Controller將其對應的JSP路徑返回給Hybris框架。
上述步驟完成之後,我們即可看到數據填充完畢之後的Hybris Product明細頁面。
本文將詳細介紹上述步驟(2), 即DTO的生成邏輯。
DAO
當點擊這個名為DSC-H20 BLUE的產品圖片後,
可以跳轉到它的明細頁面。
其中DAO的生成,也就是下圖137行代碼裏的變量productModel的生成邏輯,會在下一篇文章即這個系列的第三篇文章詳細闡述。 本文我們重點介紹DTO(第138行變量productData的生成邏輯)。
現在我們可以簡單地把DAO對象,即變量productModel理解成它包含了DSC-H20 BLUE這個產品在數據庫裏存儲的明細。
如果有ABAP開發經驗的朋友,可以把這個變量包含的內容類比成ABAP裏通過OPEN SQL從透明表裏取出的數據。 這些數據由於格式原因還不能直接給上層的UI做展示,而需要經過進一步的加工和處理。這些加工由下文的converter和populator來完成。
DTO
之前我們介紹了DTO(productData)是由第138行的convert方法生成的。這個方法的調用者是getProductConverter方法返回的一個Converter的實例,該實例實際上是Spring框架幫我們註入的一個Bean。對Bean這個概念不熟悉的朋友可以用關鍵字"Spring Bean"在百度或者Google上搜索。
那麽ProductConverter這個Bean的Spring相關定義在Hybris項目文件夾的什麽地方呢?
- Converter
前一篇文章從產品展示頁面談談Hybris的特有概念和設計結構介紹過,產品相關的Facade層存在於bin/ext-commerce/commercefacades這個extension。而在其中的resource/commercefacades-spring.xml文件中可以找到productConverter的定義:
第137行到139行表明實際註入的populators屬性是一個列表(List),這個List裏的每一個元素是ProductPopulator。 從List這個數據結構我們可以猜想到,Converter主要是通過調用1個或多個Populator來生成DTO對象的。
- Populator
我們再來看看Populator這個接口的代碼,它定義了一個名為populate(SOURCE,TARGET)的方法。方法的註釋清楚地說明了Populator是用Source變量的字段值去生成(Populate)Target變量的字段值,如下圖所示。
回到我們的Product明細頁面的展示例子。在ProductPopulator中:
Source對應Java類ProductModel
Target對應Java類ProductData
可見, Populator一般用於從Service層的DAO對象生成Facade層的DTO對象。
關於Populator的實際例子, 我們可以看看ProductUrlPopulator這個類, 它是接口Populator的一個具體實現類, 位於packagede.hybris.platform.commercefacades.product.converters.populator下面。從這個package下面我們也能發現很多其他的Populator,這也解釋了本文Converter章節裏介紹的為什麽Populator屬性註入的類型需要選擇為List。
從populate方法中可以看到:
DTO ProductData裏的code和name屬性的值都是直接取自DAO ProductModel裏對應的同名屬性;
DTO ProductData的url屬性則是第47行的resolve方法根據DAO ProductModel計算出來的。
這個resolve方法的使用,表明了Populator不只是簡單的把DAO對象的值設置到DTO對象中。在Hybris的標準實現裏諸如Product url屬性這樣需要調用其它Service來處理後然後再展示到前端的例子還有很多。
比如商品的庫存,多貨幣價格等信息, 在數據庫端本來就沒有和產品信息存在同一張表,自然也不能直接從Product的DAO對象中獲取,而是需要在相關的Populator裏調用單獨的物流處理和價格處理的Service來生成。
這裏我們再回想下Hybris的三層結構圖。
設想下如果沒有Facade層和DTO對象,前端的Controller將不得不調用很多Service,返回很多單獨的Model(如產品,物流和價格信息)給頁面,加重頁面處理的負擔。而Hybris的Facade層包裝了Service層的復雜邏輯,為前端提供了簡明統一的DTO對象,大大降低了前端的處理復雜度。這正是面向對象設計模式中的Facade(外觀)模式的體現,因此我們能夠從Hybris的架構圖中發現Facade層的名字。
希望大家通過這篇文章對Hybris Facade層的Converter和Populator能有比較詳細的了解。下一篇我們將繼續介紹Hybris的Service層。
Jerry註:
優秀的產品總是有著相似的設計思路。本文介紹的DAO和DTO, 不僅僅出現在Hybris裏,在SAP的很多其他產品裏也有用到。
在SAP CRM裏,從ABAP數據庫裏取出的數據因為結構差異無法直接被SAP CRM的BSP UI消費,必須要在圖中的Generic Interaction Layer裏做一個結構和格式的轉換:
下圖是SAP CRM UI上產品長文本字段的一個截圖:
這個長文本字段的值, 從數據庫取出到最後顯示在UI上,也經歷了在Populator(下圖的ABAP類: CL_CRM_PRODIL_LONGTEXT)裏從DAO到DTO的轉換。
至此我們能發現無論是在SAP Hybris還是SAP CRM裏,這種DAO到DTO的映射都體現在具體的代碼裏。
而在SAP Business by Design, SAP Hybris Cloud for Customer和SAP S/4HANA裏,這種DAO到DTO的映射關系則維護在一些模型裏。這樣, 應用開發人員負責維護映射關系,而框架負責統一處理映射關系。即使將來因為業務變化導致這些映射關系也需要發生變化,此時可以僅修改維護映射關系的模型,而無需修改任何代碼。
SAP把底層模型層(Model Layer)和上層消費層(Consumption Layer)之間的存儲及解析映射關系模型的這一中間層稱為SADL(Service Adaptation Definition Layer, L在有的上下文裏也稱為Language)。維護映射關系的模型則成為SADL模型。如下圖所示:
在這個系列的下一篇文章裏,Jonathan將介紹Hybris Commerce的持久層設計原理。
要獲取更多Jerry的原創技術文章,請關註公眾號"汪子熙"或者掃描下面二維碼:
從產品展示頁面談談Hybris系列之二: DTO, Converter和Populator