1. 程式人生 > >entitybuilder--一個簡單的業務通用框架

entitybuilder--一個簡單的業務通用框架

# 關於業務通用框架的思考 業務系統是千差萬別的,例如,儲存、更新和刪除訂單,或者儲存訂單和儲存客戶,走的根本不是一個流程。但是,它們還是有共同點,它們的流程大致可以分成下面的幾個部分: ![zzs_entitybuilder_01](https://img2020.cnblogs.com/blog/1731892/202103/1731892-20210303095019879-1573233082.png) 1. 拿到增刪改等操作所需的基礎資料; 2. 初始化基礎資料; 2. 對基礎資料進行校驗; 3. 利用基礎資料,構建出要進行增刪改等操作的物件; 4. 持久化或其他操作。 基於這一點,我試著抽取出一套適用於不同業務、不同用例、不同場景的通用業務框架。剛好,去年部門開始重構訂單系統,我試著將自己的想法付諸行動。經過幾次調整後,總算形成了一個簡單的業務通用框架--entitybuilder。 當然,**我更多想表達的,是一種思想、一種規範,而非工具本身**。如果真要說是框架,entitybuilder 就太簡陋了。 # entitybuilder 的結構 entitybuilder 包含三個主要部分,基礎資料 base data、構建器 entity builder 和結果物件 result entity。我拿到了 base data,把它丟進 entity builder,entity builder 就會幫我構建出 result entity,拿到 result entity 後,我要持久化也行,直接返回給更上層呼叫者也行。 entity builder 構建 result entity 的過程被定義為:初始化->校驗->構建。 ![zzs_entitybuilder_02](https://img2020.cnblogs.com/blog/1731892/202103/1731892-20210303095042938-1036644724.png) # entitybuilder1.0--規範流程 基於上面的模型,也就有了 entitybuilder1.0,它的結構如下。 對呼叫者來說,只需要設定好基礎資料,呼叫`build`方法就能完成初始化、校驗、構建,當然,`EntityBuilder`還支援僅作為校驗器使用,因為有時我們並不需要結果物件,只需要校驗基礎資料就行。 對實現者來說,使用者需要繼承`AbstractEntityBuilder`,並實現初始化、校驗和構建方法。 ![zzs_entitybuilder_03](https://img2020.cnblogs.com/blog/1731892/202103/1731892-20210303095058767-1600044341.png) 以訂單儲存為例,下面展示如何使用 entitybuilder。程式碼的呼叫非常簡單,這裡需要注意,`EntityBuilder`物件必須是多例的。 ```java public String save(DefaultOrderSaveCmd cmd) { // 獲取構建器(多例的) EntityBuilder entityBuilder = getSaveEntityBuilder(); // 設定基礎資料 entityBuilder.setBaseData(cmd); // 構建儲存實體 OrderSaveE entity = entityBuilder.build(); // 持久化操作 orderDao.save(entity); return entity.getId(); } ``` # entitybuilder2.0--多場景支援 entitybuilder1.0 只是規範了業務流程,在多場景方面還是存在問題。 一個業務用例可能會有不同的場景,例如,客戶儲存可能就不只一個入口,按照 entitybuilder1.0 的設計,我們需要將所有場景的邏輯都堆積到構建器中。顯然,這是不合理的。 參考 spring 的 postprocessor,我在構建器中引入了校驗器和處理器的支援。構建器中定義了用例的主流程,不同場景可以通過註冊校驗器和處理器來對主流程進行修飾。在 entitybuilder1.0 的基礎上修改,得到以下結構: ![zzs_entitybuilder_05](https://img2020.cnblogs.com/blog/1731892/202103/1731892-20210303095532841-1272571365.png) 和 entitybuilder 1.0 相比,對呼叫者來說,可以通過註冊驗器和處理器來影響構建器的主流程,對構建器實現者來說,改為繼承`AbstractFlexibleEntityBuilder`。 那麼校驗器和處理器如何影響主流程呢?下面通過一張圖來說明。在 entitybuilder1.0 中,呼叫者無需知道構建器中的邏輯,現在卻需要知道(有好有壞吧)。 ![zzs_entitybuilder_04](https://img2020.cnblogs.com/blog/1731892/202103/1731892-20210303095133790-172422034.png) 以訂單儲存為例,程式碼示例如下。 ```java public String save(DefaultOrderSaveCmd cmd) { // 獲取構建器 AbstractFlexibleEntityBuilder entityBuilder = getSaveEntityBuilder(); // 設定基礎資料 entityBuilder.setBaseData(cmd); // 對構建器進行部分更改,例如註冊處理器或檢驗器 entityBuilder.registerValidator(myOrderSaveValidator); entityBuilder.registerEntityBuilderPostProcessor(myOrderSavePostProcessor); // 構建儲存實體 OrderSaveE entity = entityBuilder.build(); // 持久化操作 orderDao.save(entity); return entity.getId(); } ``` # 基礎資料的組成 entitybuilder 的可用性極大依賴於基礎資料的規範。**在 entitybuilder 中,基礎資料的成員屬性應該包含兩個部分:主體屬性和關聯物件**。例如,訂單的基礎資料就包括了訂單本身以及它的關聯物件,如客戶、操作人等。 ![zzs_entitybuilder_06](https://img2020.cnblogs.com/blog/1731892/202103/1731892-20210303095150232-17188069.png) 為什麼要包含這兩個部分呢? 構建器中包含了業務的大部分邏輯,我們需要呼叫各種通用方法,這些方法的入參物件無非就是主體屬性或關聯物件屬性。基礎資料物件將在 EntityBuilder 的整個生命週期中傳遞,通過它來傳遞關聯物件,可以保證關聯物件只需要初始化一次,從而減少重複 IO。 如果一開始放入構建器的基礎資料物件中已經有關聯物件了,那麼,構建器也不會再去初始化它。這一點在批量構建時將非常有用。 # 事務控制 在 entitybuilder 的規範中,結果物件的持久化是在一個事務/方法中完成主體物件和關聯物件的持久化,但是,在某個場景下,我們需要在事務中進行某些自定義操作,例如,訂單儲存完成後,向某個外部系統推送資料,推送失敗,事務跟著回滾。 針對這種場景,也是可以支援的。事務中的自定義操作將作為函式的形式傳遞,在基礎資料中設定好,持久化時就會執行它。 ```java @Override public String save(DefaultOrderSaveCmd cmd) { // 獲取構建器 AbstractFlexibleEntityBuilder entityBuilder = getSaveEntityBuilder(); // 設定基礎資料 entityBuilder.setBaseData(cmd); // 對基礎資料進行部分更改,例如設定儲存事務中需要進行的操作 cmd.addSaveConsumer(order -> { // 有的自定義操作需要放入儲存事務,如果失敗,訂單資料也會回滾 // 省略程式碼······ }); // 構建儲存實體 OrderSaveE entity = entityBuilder.build(); // 持久化操作 orderDao.save(entity); return entity.getId(); } // @Transactional public String save(OrderSaveE entity) { // 儲存訂單 // 省略程式碼······ // 儲存產品 // 省略程式碼······ // 儲存附件 // 省略程式碼······ // 執行儲存事務中的函式 entity.getSaveConsumers().forEach(x -> x.accept(entity)); return entity.getId(); } ``` 以上基本介紹完 entitybuilder。這裡還是強調一點,我更多的是想表達一種思想、一種規範,因為作為工具,entitybuilder 還有很多需要改進的地方。 最後,感謝閱讀。 # 參考資料 > 相關原始碼請移步:[https://github.com/ZhangZiSheng001/zzs-code-thought/01-entitybuilder-demo](https://github.com/ZhangZiSheng001/zzs-code-thought/01-entitybuilder-demo) >本文為原創文章,轉載請附上原文出處連結:[https://www.cnblogs.com/ZhangZiSheng001/p/14472782.html](https://www.cnblogs.com/ZhangZiSheng001/p/14472782.html)