中文字幕一区二区人妻电影,亚洲av无码一区二区乱子伦as ,亚洲精品无码永久在线观看,亚洲成aⅴ人片久青草影院按摩,亚洲黑人巨大videos

設計應用程序業(yè)務層時的陷阱

發(fā)布于:2021-01-06 17:33:09

0

113

0

編程 陷阱 設計

即使是最簡單的軟件,有時也可能陷入意大利面條式的代碼中,成為瀏覽的噩夢,尤其是在舊系統(tǒng)中。在本文中,從應用程序的業(yè)務層查看一些錯誤的代碼,以及如何使用更好的設計實踐對其進行修復。在這些小小的改進無法處理之前,請注意噩夢代碼。

當我們開始編寫軟件時,我們總是希望有一個好的設計。我們閱讀書籍,運用最佳實踐,最后,最后常常是一團糟。根據(jù)我在一家定制軟件開發(fā)公司的經驗,我必須每天處理此類代碼,尤其是在某些舊系統(tǒng)上工作時。

造成這種情況的原因多種多樣,我將在一系列文章中嘗試涵蓋其中一些,以實際的方式對其進行研究。在我的第一個示例中,我將說明為什么簡單的軟件會演變成一場噩夢,并建議進行一些改進。我將僅專注于處理業(yè)務邏輯的服務層。

首先,我們實際上經??吹剑ê途帉懀┮恍╁e誤的代碼

讓我們從一個簡單的存儲應用程序開始。我們擁有帶有服務,存儲庫的產品資源,并且可以執(zhí)行CRUD操作,這正是我們認為需要的。我們的產品服務如下所示:

public class ProductService { public String create(Product product) { return productRepository.create(product); } public String update(Product product) { return productRepository.update(product); } public Product get(String productId) { return productRepository.get(productId); } public void delete(Product product) { productRepository.delete(product); } }

還會有其他一些東西,例如DTO到實體的映射,控制器等。但是正如我所說的,我們將考慮將它們編寫為簡單起見。我們的產品實體是簡單的Java Bean,我們的存儲庫保存在正確的數(shù)據(jù)庫表中。然后,我們得到另一個要求,即我們還將創(chuàng)建一個在線商店,并且需要一種下訂單的方法。因此,我們添加了快速訂購服務來滿足我們仍然很簡單的要求:

 public class OrderService { public String saveOrder(Order order) { return orderRepository.save(order); } }

它簡單,易讀且有效!然后,下訂單時就需要更新庫存中的產品。我們這樣做:

public class OrderService { public String saveOrder(Order order) { Product product=productService.get(order.getProductId()); product.setAvailableQuantity(product.getAvailableQuantity()-order.getQuantity()); productService.update(product); return orderRepository.save(order); } }

您可能會看到我要去的地方,但這仍然可讀并且可以正常工作。之后,我們又獲得了三個要求。1 /我們需要致電運輸服務將該產品運送到一個地址2 /如果沒有足夠的庫存來履行訂單,則拋出一個錯誤3 /如果產品的可用數(shù)量低于最低數(shù)量以進行重新庫存。結果如下:

public class OrderService { public String saveOrder(Order order) { Product product=productService.get(order.getProductId()); //The order service works more like a product service in the following liness if(product.getAvailableQuantity()<order.getQuantity()){ throw new ProductNotAvailableException(); } product.setAvailableQuantity(product.getAvailableQuantity()-order.getQuantity()); productService.update(product); if(product.getAvailableQuantity()<Product.MINIMUM_STOCK_QUANTITY){ productService.restock(product); } //It also needs to know how shipments are created Shipment shipment=new Shipment(product, order.getQuantity(), order.getAddressTo()); shipmentService.save(shipment); return orderRepository.save(order); } }

我知道這可能是一個極端的例子,但是我確信我們在項目中已經看到了類似的代碼。這樣做有多個問題–責任共擔,與其他領域的邏輯和基礎架構打亂等等。如果這是一個真實的商店,那么接單的人就像總經理–照顧一切,從實際訂購庫存維護和交付。

現(xiàn)在到一個更好的版本

讓我們嘗試以不同的方式處理相同的情況。我將從訂購服務開始。為什么我們調用方法saveOrder?因為我們將其視為開發(fā)人員,而不是從業(yè)務角度來看。我們開發(fā)人員的想法通常是數(shù)據(jù)庫驅動的(或REST驅動的),我們將我們的軟件視為一系列CRUD操作。通常,當我們閱讀有關域驅動設計的書籍時,會提到通用語言(Ubiquitous Language)這一術語,即開發(fā)人員和用戶之間的通用語言。如果我們嘗試在我們的代碼中為業(yè)務建模,那么為什么不使用正確的術語。我們可以將初始代碼更改為:

public class OrderService { public String placeOrder(Order order) { return orderRepository.save(order); } }

進行很小的更改,但即使那樣也會使其更具可讀性。這是業(yè)務層,而不是數(shù)據(jù)庫層-我們去商店時下訂單,但不保存訂單。然后,當其他需求出現(xiàn)時,而不是開始使用帶有CRUD操作的現(xiàn)有服務對它們進行編碼,我們可以嘗試重新創(chuàng)建業(yè)務模型。我們詢問業(yè)務人員,他們告訴我們,下訂單時,接單的人會致電庫存部門,詢問他們產品是否可用,然后進行儲備并致電帶有預定號和地址的交貨人,以便他們裝運它。是什么阻止我們在代碼中執(zhí)行相同的操作?

public class OrderService { public String placeOrder(Order order) { String productReservationId=productService.requestProductReservation(order.getProductId, order.getQuantity()); String shippingId=shipmentService.requestDelivery(productReservationId, order.getAddressTo()); order.addShippingId(shippingId); return orderRepository.save(order); } }

在我看來,它看起來更干凈,代表了實際商店中發(fā)生的事件的順序。訂單服務不需要知道產品的工作方式或運輸方式。它只是使用完成工作所需的方法。我們也需要修改其他服務:

public class ProductService { //Method used in Orders Service public String requestProductReservation(String productId, int quantity){ Product product=productRepository.get(productId); product.reserve(quantity); productRepository.update(product); return createProductReservation(product, quantity); } private String createProductReservation(Product product, int quantity){ ProductReservation reservation=new ProductReservation(product,quantity); reservation.setStatus(ReservationStatus.CREATED); return reservationRepository.save(reservation); } //Method used in Shipment Service public ProductReservation getProductsForDelivery(String reservationId){ ProductReservation reservation=reservationRepository.getProductReservation(reservationId); reservation.getProduct.releaseReserved(reservation.getQuantity()); if(reservation.getProduct().needRestock()){ this.restock(product); } reservation.setStatus(ReservationStatus.PROCESSED); reservationRepository.update(reservation); } }

產品服務提供了其他服務要使用的兩種方法,但對它們的結構一無所知。它不關心訂單,發(fā)貨等。當產品需要補貨以及產品數(shù)量是否足夠時,邏輯就在實際產品內部。

public class Product() { //Fields, getters, setters etc... public void reserve(int quantity){ if(this.availableQuantity - this.reservedQuantity > quantity){ this.reservedQuantity+=quantity; } else throw new ProductReservationException(); } public releaseReserved(int requested){ if(this.reservedQuantity>=requested){ this.reservedQuantity-=requested; this.availableQuantity-=requested; } else  throw new ProductReservationException(); } public boolean needsRestock(){ return this.availableQuantity<MINIMUM_STOCK_QUANTITY; } }

貨運服務可以是這樣的:

public class ShipmentService { public String requestDelivery(String reservationId, Address address){ ProductReservation reservation=productService.getProductForDelivery(reservationId); Shipment shipment=new Shipment(reservation, address); return shipmentRepository.save(shipment); } }

我并不是說這是最好的設計,但我認為它要干凈得多。每個服務都照顧自己的領域,并且對其他服務了解得最少。實際的實體不僅是數(shù)據(jù)持有者,而且還攜帶與之相關的邏輯,因此服務不需要直接修改其內部狀態(tài)。在我看來,最有價值的是代碼真正代表了業(yè)務運作方式。

結論

如果我們從本文的第一部分開始不討論這種情況,則應該嘗試花一些時間并正確地理解我們的模型。即使出現(xiàn)了新的要求并且我們受到時間的壓力,或者重構將花費更多的時間,我們也不應該懶惰。服務和實體中來自不同領域的混合邏輯乍一看似乎是可維護的,但隨著項目規(guī)模的擴大,它變成了意大利面。就像我們在現(xiàn)實生活中的商店示例一樣,小型的在線商店所有者可以處理從接單,進貨,交付和財務等所有事務。但是,當商店發(fā)展壯大時,他將無能為力,這將變得一團糟。