1 爲何我要研究領域驅動設計數據庫
1.1 設計方法各樣且代碼沒法反映設計緩存
我大概從2017年10月份開始研究DDD,當時在一家物流信息化的公司任職架構師,研究DDD的初衷在於爲團隊尋找一種軟件設計的方法論。做爲架構師,常常參與設計評審,包括:需求評審、設計評審、代碼評審。在評審過程當中,有一點感覺很是深,就是評審過程很是痛苦且幾乎沒有效率和成果。讓我痛苦的地方有:架構
1.2 代碼質量很難有效提高app
在承擔架構師以前,個人另外一個職責是技術管理,作的工做是與軟件質量相關的。當時加入一個大概2000萬規模的項目,有大約100開發人員參與,開發週期大概1年。加入該團隊在開發的過程當中,發現了兩個問題:編碼
基於第一個問題,我定義了統一的界面規範,這個界面規範經過和公司的PMO合做將其融入到工程過程當中,做爲開發人員必須遵循的規範。第二個問題,我則花費了不少的時間來嘗試解決(大概有2年時間都與代碼質量作鬥爭),最終與尋找統一的設計方法異曲同工。spa
如何讓咱們的代碼變得更加乾淨,我在執行的過程當中,按照如下步驟一步一步的執行。設計
基於以上的代碼質量管理方法,我認爲已是作的至關不錯,可是很是遺憾的是,當我抽樣評審產品的代碼時,我依然感到無比沮喪,軟件的代碼仍是太複雜、太難看懂了,與《代碼整潔之道》的要求相差太遠了,我耗費了1年多的工做幾乎毫無成果可言。所以,我在深深思考,在編碼層面,定義了規範、作了優雅編碼培訓、定義了編寫優秀代碼的相關制度,就爲了讓開發人員把代碼寫好,使代碼看起來更加清晰,軟件更加容易維護,爲何仍是沒法實現?代碼規範
2 軟件複雜性的根源code
貧血模型是軟件複雜性的根源。貧血模型本質是面向數據的設計,面向過程的編碼。基於貧血模型的分層架構,一般分爲UI層、業務邏輯層、數據訪問層、貧血模型層,貧血模型與數據模型一致。對象
業務規則是軟件最核心的代碼,一般只佔整個軟件很小的一部分。在基於貧血模型的架構中,業務規則的實現,一般混雜在上層UI展示邏輯、數據庫訪問、緩存等各類邏輯中,分散在各個層和關聯對象。經過閱讀業務邏輯層的代碼來還原真實的業務規則很困難,很難從代碼反映其業務規則設計,而且隨着軟件需求變動,業務規則更加難以還原,軟件複雜度將不可控。
如下是一段業務邏輯層的實現代碼。
public OrderDto signOrder(Order order) { Assert.notNull(order, "OrderDto can not be null."); OrderDto result = new OrderDto(); result.setIsOperationSuccess(true); if (null == order.getId()) { result.setIsOperationSuccess(false); result.setOperationMassage("id不能爲空。"); return result; } OrderCondition orderCondition = new OrderCondition(); orderCondition.setId(order.getId()); order = orderMapper.selectOne(orderCondition); if (null == order) { result.setIsOperationSuccess(false); result.setOperationMassage("該訂單不存在。"); return result; } if (order.getOrderStatus() != Integer.valueOf(StatusEnum.ORDER_STATUS.ORDER_WAIT_RECEIVE.getCode())) { result.setIsOperationSuccess(false); result.setOperationMassage("訂單號:{" + order.getOrderNo() + "}不是待收貨狀態,不能進行簽收。"); return result; } // 該訂單下的全部商品的實收數(發貨數量)必須都大於0 boolean validDeliveryCount = true; Double orderTotalAmount = 0d; List<OrderGoodsDto> orderGoodsList = orderGoodsBiz.selectOrderGoodsByOrderId(order.getId()); List<OrderGoods> orderGoodsListForUpdate = new ArrayList<>(); if (EmptyUtil.isNotEmpty(orderGoodsList)) { for (OrderGoodsDto orderGoods : orderGoodsList) { if (null == orderGoods.getDeliveredNum() || orderGoods.getDeliveredNum() <= 0) { validDeliveryCount = false; } else { // 根據商品發貨數量從新計算訂單總金額...... Double price = (null == orderGoods.getDiscountPrice() ? orderGoods.getOriginalPrice() : orderGoods.getDiscountPrice()); Integer goodsNum = (null == orderGoods.getDeliveredNum() ? 0 : orderGoods.getDeliveredNum()); orderTotalAmount += price * goodsNum; // 更新orderGoods的收貨數量 orderGoods.setReceivedNum(goodsNum); OrderGoods orderGoodsForUpdate = new OrderGoods(); BeanUtils.copyProperties(orderGoods, orderGoodsForUpdate); orderGoodsListForUpdate.add(orderGoodsForUpdate); } } } if (!validDeliveryCount) { result.setIsOperationSuccess(false); result.setOperationMassage("訂單號:" + order.getOrderNo() + ",訂單下全部商品都已發貨纔可進行簽收操做,請確認。"); return result; } order.setOrderStatus(Integer.valueOf(StatusEnum.ORDER_STATUS.ORDER_SIGN.getCode())); order.setOrderTotalAmount(orderTotalAmount); order.setPaymentAmount(orderTotalAmount); order.setUnpaidAmount(orderTotalAmount); update(order); orderGoodsBiz.batchUpdate(orderGoodsListForUpdate); List<Order> orders = new ArrayList<Order>(); orders.add(order); saveRouteMessage(orders); return result; }
相似這樣的代碼很是常見,經過閱讀這段業務邏輯代碼,能夠發現它處理了如下的任務:
(1)返回結果的處理。
(2)數據庫訪問。
(3)關聯對象的數據庫訪問。
(4)業務規則。
業務規則代碼與數據庫訪問、關聯對象數據庫訪問、結果處理等其它邏輯在一塊兒實現,經過代碼還原業務規則會愈來愈複雜且隨着時間推移,代碼邏輯會愈來愈偏離設計。做爲軟件系統最核心的部分——業務規則,若是咱們僅僅將其從其它任務中剝離,咱們的代碼將演化以下。(注:如下代碼僅演示剝離出來業務邏輯,並不是DDD推薦方式,下篇介紹。)
public void signOrder(Order order) { assertCanBeSigned(order); Double orderTotalAmount = 0d; List<OrderGoods> orderGoodsList = order.getOrderGoods(); for (OrderGoods orderGoods : orderGoodsList) { Double price = (null == orderGoods.getDiscountPrice() ? orderGoods.getOriginalPrice() : orderGoods.getDiscountPrice()); Integer goodsNum = (null == orderGoods.getDeliveredNum() ? 0 : orderGoods.getDeliveredNum()); orderTotalAmount += price * goodsNum; orderGoods.setReceivedNum(goodsNum); } order.setOrderStatus(Integer.valueOf(StatusEnum.ORDER_STATUS.ORDER_SIGN.getCode())); order.setOrderTotalAmount(orderTotalAmount); order.setPaymentAmount(orderTotalAmount); order.setUnpaidAmount(orderTotalAmount); } public void assertCanBeSigned(Order order) { Assert.notNull(order, "OrderDto can not be null."); if (order.getOrderStatus() != Integer.valueOf(StatusEnum.ORDER_STATUS.ORDER_WAIT_RECEIVE.getCode())) { throw new BusinessException("訂單號:{" + order.getOrderNo() + "}不是待收貨狀態,不能進行簽收。"); } List<OrderGoods> orderGoodsList = order.getOrderGoods(); if (!EmptyUtil.isNotEmpty(orderGoodsList)) { throw new BusinessException("訂單號:" + order.getOrderNo() + ",訂單沒有包含商品,是一個空的訂單,沒法簽收。"); } for (OrderGoods orderGoods : orderGoodsList) { // 該訂單下的全部商品的實收數(發貨數量)必須都大於0 if (null == orderGoods.getDeliveredNum() || orderGoods.getDeliveredNum() <= 0) { throw new BusinessException("訂單號:" + order.getOrderNo() + ",訂單下全部商品都已發貨纔可進行簽收操做,請確認。"); } } }
這段代碼反映的業務規則是訂單簽收規則。
(1)若是訂單不是待發貨狀態,不能簽收;
(2)校驗訂單下全部商品的發貨數量都要大於0;
(3)計算訂單總金額,並設置收貨數量爲發貨數量;
(4)設置簽收狀態、總金額、支付金額和未付金額。
你能夠發現這段單純實現業務規則的代碼,會更加的簡單、清晰,也會使軟件更加的容易維護。在DDD的方法論裏面,業務規則是在領域層來實現的,領域層的代碼僅僅是業務規則,這時候,其分層架構的分層邏輯和基於貧血模型的分層邏輯也會不同了。
經過以上代碼的對比咱們發現:
3 DDD如何解決軟件複雜性
DDD解決軟件複雜性的方法核心爲兩點:
軟件實現與領域模型保持一致是本書的核心思想,DDD構建了一套完整的方法論來支持領域模型驅動程序設計。這套方法論簡述以下。
我將在下一篇文章中詳細解釋DDD的核心思想,讓你明白它是如何解決複雜性的。