在上一部分,分層架構的目的是爲了將業務規則剝離出來在單獨的領域層中進行實現。再回顧一下領域驅動設計的分層中應用層代碼的實現。數據庫
@Override public void pay(int orderId, float amount) { DesignerOrder order = designerOrderRepository.selectByKey(orderId); // 領域對象的加載 if (order == null) { AppException.throwAppException(AppExceptionMessage.DESIGNER_ORDER_NOT_EXIST_CODE, AppExceptionMessage.DESIGNER_ORDER_NOT_EXIST, orderId); } order.pay(amount); // 領域對象業務規則實現 designerOrderRepository.update(order); // 領域對象狀態持久化 }
全部的業務規則都抽象到領域對象,好比「order.pay(amount)」抽象了付款的業務規則。領域對象由狀態(對象的字段、屬性)和操做(對象的方法)構成,領域對象的操做用於實現業務規則,業務規則執行完成後更改領域對象的狀態。領域對象的持久化交給了基礎設施層,這裏,Repository目的是持久化領域對象狀態。編程
領域驅動設計,即領域模型驅動程序設計,它的核心是保證系統的實現與實際的業務規則一致,完整實現了領域模型。它包含了兩個部分:領域模型、領域模型的編程實現。架構
在軟件設計和實現過程當中要充分利用領域模型,設計過程當中,領域模型做爲與業務專家的溝通語言;實現過程當中,領域模型做爲與開發人員溝通的語言。領域模型在軟件生命週期過程做爲通用語言。app
1 領域模型ide
領域建模(這裏不重點介紹如何建模)方法論產出領域模型。咱們可使用UML建模,使用最簡單、最容易理解的名詞-形容詞-動詞法對領域知識進行建模,使用該模型做爲與業務、技術團隊溝通的通用語言。ui
在名詞-形容詞-動詞法建模方法中,領域知識中的名詞通常對應模型、形容詞對應模型屬性、動詞對應模型方法。模型之間的關係有:組合、聚合、關聯、依賴,四者關係由強到弱。this
依賴(Dependency)關係是類與類之間的聯接。依賴關係表示一個類依賴於另外一個類的定義。通常而言,依賴關係在Java語言中體現爲局域變量、方法的形參,或者對靜態方法的調用。 編碼
關聯(Association)關係是類與類之間的聯接,它使一個類知道另外一個類的屬性和方法。關聯能夠是雙向的,也能夠是單向的。在Java語言中,關聯關係通常使用成員變量來實現。 spa
聚合(Aggregation) 關係是關聯關係的一種,是強的關聯關係。聚合是總體和個體之間的關係。例如,汽車類與引擎類、輪胎類,以及其它的零件類之間的關係便總體和個體的關係。與關聯關係同樣,聚合關係也是經過實例變量實現的。可是關聯關係所涉及的兩個類是處在同一層次上的,而在聚合關係中,兩個類是處在不平等層次上的,一個表明總體,另外一個表明部分。 設計
組合(Composition) 關係是關聯關係的一種,是比聚合關係強的關係。它要求普通的聚合關係中表明總體的對象負責表明部分對象的生命週期,組合關係是不能共享的。表明總體的對象須要負責保持部分對象和存活,在一些狀況下將負責表明部分的對象湮滅掉。表明總體的對象能夠將表明部分的對象傳遞給另外一個對象,由後者負責此對象的生命週期。換言之,表明部分的對象在每個時刻只能與一個對象發生組合關係,由後者排他地負責生命週期。部分和總體的生命週期同樣。
簡而言之,組合關係表示部分與總體關係,部分不能單獨存在;聚合關係表示稍弱的部分與總體關係,部分能夠單獨存在;關聯關係是一個模型和另外一個模型的聯接,好比一個訂單有一個顧客而一個顧客有多個訂單;依賴是最弱的關係,表示一個模型的實現使用到另外一個模型的功能。
舉個例子,咱們與業務專家溝通,梳理了以下業務知識,而後咱們使用名詞-形容詞-動詞法來進行建模。
=====================
領域知識:裝修設計預定平臺
1 客戶經過系統預定設計師進行裝修設計,客戶只能預定一個設計師訂單,不能預定多個同時進行設計。
2 預定後,設計師上門進行量房,根據面積進行報價和預估設計時間。設計師訂單按照4個節點預估交付時間,在不一樣節點交付不一樣成果,這四個節點分別爲平面圖、效果圖、施工
圖、交底,四個節點的付款比率分別爲10%、40%、40%、10%。
3 客戶接受報價方案後,進行付款,設計師開始設計;若是拒絕,則設計師能夠進行再次報價和預估設計時間。
4 客戶在付款以前,均可以進行終止。
5 客戶付款後,正式進入設計階段。設計師按階段推動設計並按階段更新進度。在每個階段,設計師完成任務後,客戶進行階段成果確認,客戶肯定後全部階段後,訂單自動完成。
6 客戶能夠對完成的訂單進行評價。
7 客戶對已付款但未完成的訂單能夠提出退款申請,退款計算方法依據當前設計進度,若是當前進度已經達到設計師請求施工圖設計確認進度或超過該進度,則不容許退款。若是容許退款,退款金額最多爲(總額 - 已完成的各階段付款之和),最少爲未完成交付節點的待付款總額。
8 申請經過的退款訂單再也不容許更新進度。
=====================
在這裏咱們能夠梳理出來的名詞有:客戶、設計師訂單、設計師、訂單交付進度與交付節點、退款訂單。
和設計師訂單有關的動詞有:量房、報價、接受(拒絕)報價、取消、付款、確認進度、退款、評價等。
設計師訂單有關的屬性有:訂單金額、支付金額、面積、取消緣由、評價、狀態等。
所以,咱們經過使用名詞-形容詞-動詞法構建的模型圖以下所示。
這裏,模型有:客戶Customer,設計師Designer,設計師訂單DesignerOrder,退款單RefundOrder,設計進度DesigningProgressReport,設計進度節點DesigningProgressNode。模型中組合關係爲:設計進度DesigningProgressReport,設計進度節點DesigningProgressNode;其它模型之間的關係爲關聯關係。
這個模型就做爲軟件開發和維護過程的通用語言。接下來,咱們將介紹如何來實現領域模型。
2 領域模型實現
在上一節,咱們介紹了經過領域建模來構建了領域模型。接下來咱們要介紹如何實現模型驅動程序設計,即咱們如何經過代碼來實現領域模型對應的業務邏輯。領域模型的實現代碼在領域層,它完整實現了領域模型的內部結構和模型之間的關係。
領域模型的實現代碼由如下幾個部分構成:
• 領域模型關係的實現:組合、聚合、關聯、依賴。
• 領域模型的實現:實體和值對象。
• 跨領域模型的業務規則的實現:領域服務。
2.1 領域模型關係的實現
聚合、組合、關聯關係在實現上的表現基本上是一個類(或者類的標識)做爲另外一個類的屬性;而依賴關係則是一個類做爲另外一個類在方法的實現上的參數、變量,爲另外一個類提供功能實現。
下面咱們簡單看一下如何經過編碼來實現類關聯關係,好比在模型上客戶和設計師訂單是關聯關係,一個客戶能夠有多個設計師訂單,可是每個設計師訂單隻能有一個客戶和一個設計師而且最多隻有一個退款訂單。
(1)聚合、組合、關聯
表如今一個類持有另外一個類的引用,引用能夠是實例的引用或者標識的引用,具體實現爲屬性。這種關係是雙向關係,爲了簡化編碼,可能只須要一方持有另外一方的引用便可,這依賴於具體要實現的業務邏輯。以下代碼實現了DesignerOrder對設計師、進度報告的關係。
public class DesignerOrder implements Entity<DesignerOrder> { private int id; private int designerId; private DesigningProgressReport progressReport; …… public Designer getDesigner() { return designerRepository.getDesignerById(this.designerId); } public DesigningProgressReport getProgressReport() { return this.progressReport; } …… }
(2)依賴
依賴表如今一個類的實現使用到另外一個類的功能,依賴的類可能做爲方法的參數、方法局部變量或者靜態引用等。以下代碼體現了對DesignerOrderWorkflowService的功能依賴。
public class DesignerOrder implements Entity<DesignerOrder> { public void pay(float amount) { Assert.isTrue(amount > 0, "The amount must be bigger than 0."); if (!DesignerOrderWorkflowService.canChangeState(state, DesignerOrderState.PAID)) { BusinessException.throwException(DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE_CODE, DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE, this.id, this.state); } if (Math.abs(amount - this.expectedAmount) > 0.01) { BusinessException.throwException(DomainExceptionMessage.PAYMENT_NOT_MATCHED_CODE, DomainExceptionMessage.PAYMENT_NOT_MATCHED, this.id, this.expectedAmount, amount); } this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.PAID); this.actualPaidAmount = amount; // 付款完成後,自動啓動進度跟蹤 this.progressReport.startup(); } }
2.2 領域模型的實現
領域模型在實現上表現爲兩類:(1)實體(Entity):這個領域模型有特定的標識,可是其內部狀態會隨着一序列的事件(對應業務規則的執行)發生變化,咱們把這類模型的實現稱爲實體;(2)值對象(Value Object):這個領域模型由屬性來定義,實例建立後不會發生變動,變動也意味着從新建立一個實例,咱們把這類模型的實現稱爲值對象。
(1)實體
在裝修設計預定平臺的領域模型裏面,咱們很容易能夠發現設計師訂單就是一個實體,在建立後,每個設計師訂單有一個惟一的訂單號,後續有量房、報價、付款、退款等系列動做的發生,從而訂單的內部狀態(字段值)會發生變化,可是都表明的是同一個訂單。每個實體的實現都有一個標識。以下所示,這裏的id字段表示了訂單的惟一標識,並實現了Entity接口,Entity接口sameIdentityAs方法,判斷實體的Id是否相同。
實體的屬性和操做,對應着模型的狀態和狀態的變動,他們與模型的定義使一致的。
@Data @EqualsAndHashCode(of = {"id"}) public class DesignerOrder implements Entity<DesignerOrder> { private int id; private DesignerOrderState state; private int customerId; private int designerId; private float area; private float expectedAmount; private int estimatedDays; private DesigningProgressReport progressReport; private String abortCause; private float actualPaidAmount; private int feedbackStar; private String feedbackDescription; private Date createdTime; private Date updatedTime; @Override public boolean sameIdentityAs(DesignerOrder other) { return this.equals(other); } public void measure(float area) { Assert.isTrue(area > 0, "The area must be bigger than 0."); this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.MEASURED); this.area = area; } public void quote(float amount, int[] estimatedDaysList) { Assert.isTrue(amount > 0, "The price must be bigger than 0."); this.assertEstimatedDaysList(estimatedDaysList); this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.QUOTED); this.expectedAmount = amount; this.progressReport = DesigningProgressReportFactory.newReport(this, estimatedDaysList); this.estimatedDays = this.progressReport.getEstimatedCompletionDays(); } private void assertEstimatedDaysList(int[] estimatedDaysList) { if (null == estimatedDaysList || estimatedDaysList.length != 4) { throw new IllegalArgumentException("The size of estimatedDaysList must be 4."); } for (int days : estimatedDaysList) { if (days <= 0) { throw new IllegalArgumentException("Each element of estimatedDaysList must be bigger than 0."); } } } public void pay(float amount) { Assert.isTrue(amount > 0, "The amount must be bigger than 0."); if (!DesignerOrderWorkflowService.canChangeState(state, DesignerOrderState.PAID)) { BusinessException.throwException(DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE_CODE, DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE, this.id, this.state); } if (Math.abs(amount - this.expectedAmount) > 0.01) { BusinessException.throwException(DomainExceptionMessage.PAYMENT_NOT_MATCHED_CODE, DomainExceptionMessage.PAYMENT_NOT_MATCHED, this.id, this.expectedAmount, amount); } this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.PAID); this.actualPaidAmount = amount; // 付款完成後,自動啓動進度跟蹤 this.progressReport.startup(); } public RefundOrder refund(String cause) { this.assertCanRefund(); this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.REFUND); return RefundOrderFactory.newRefundOrder(this, cause); } }
DDD對於實體有一段重要描述:當一個對象由其標識而不是屬性區分時,那麼在模型中應該主要經過標識來肯定該對象的定義。使類定義變得簡單,並集中關注生命週期的連續性和標識。定義一種區分每一個對象的方式,這種方式應該與其形式和歷史無關。要格外注意那些須要經過屬性來匹配對象的需求。在定義標識操做時,要確保這種操做做爲每一個對象生成惟一的結果,這能夠經過附加一個保證惟一性的符號來實現。這種定義標識的方法可能來自外部,也多是由系統建立的任意標識符,但它在模型中必須是惟一的標識。模型必須定義出「符合什麼條件纔算是相同的事務」。
(2)值對象
在貨物運輸系統中,當咱們爲一個貨物的運輸執行一條路線以後,那麼這條路線不能發生變動,咱們傾向於把路由線路看作一個值對象。以下圖所示。對於值對象,經過屬性值便可標識。
public class RouteSpecification extends AbstractSpecification<Itinerary> implements ValueObject<RouteSpecification> { private Location origin; private Location destination; private Date arrivalDeadline; public RouteSpecification(final Location origin, final Location destination, final Date arrivalDeadline) { Validate.notNull(origin, "Origin is required"); Validate.notNull(destination, "Destination is required"); Validate.notNull(arrivalDeadline, "Arrival deadline is required"); Validate.isTrue(!origin.sameIdentityAs(destination), "Origin and destination can't be the same: " + origin); this.origin = origin; this.destination = destination; this.arrivalDeadline = (Date) arrivalDeadline.clone(); } public Location origin() { return origin; } public Location destination() { return destination; } public Date arrivalDeadline() { return new Date(arrivalDeadline.getTime()); } @Override public boolean isSatisfiedBy(final Itinerary itinerary) { return itinerary != null && origin().sameIdentityAs(itinerary.initialDepartureLocation()) && destination().sameIdentityAs(itinerary.finalArrivalLocation()) && arrivalDeadline().after(itinerary.finalArrivalDate()); } @Override public boolean sameValueAs(final RouteSpecification other) { return other != null && new EqualsBuilder(). append(this.origin, other.origin). append(this.destination, other.destination). append(this.arrivalDeadline, other.arrivalDeadline). isEquals(); } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final RouteSpecification that = (RouteSpecification) o; return sameValueAs(that); } @Override public int hashCode() { return new HashCodeBuilder(). append(this.origin). append(this.destination). append(this.arrivalDeadline). toHashCode(); } }
值對象(Value Object)所包含的屬性應該行程一個概念總體。當咱們只關心一個模型元素的屬性時,應該把它歸類爲Value Object。咱們應該使這個模型元素可以表示出其屬性的意義,併爲它提供相關功能。Value Object應該是不可變的。不要爲它分配粉盒標識,並且不要把它設計成像Entity那麼複雜。
2.3 跨領域模型的業務規則的實現
咱們使用領域服務來封裝不屬於領域模型或者領域模型公共的業務規則。領域服務的方法通常是靜態的,而且不會更改內部狀態。在裝修設計預定平臺裏面,咱們使用狀態機工做流服務實現訂單狀態流轉,它能夠在設計師訂單和退款單中共用。在《領域驅動設計》裏面有一個示例,展現了轉帳服務的實現,轉帳動做實現的是從一個帳戶到另外一個帳戶的資金流轉,所以將轉帳設計到領域服務TransferService裏面。關於服務的描述是:當領域中的某個重要的過程或轉換操做不屬於實體或值對象的天然職責時,應該在模型中添加一個做爲獨立接口的操做,並將其聲明爲Service。定義接口時要使用模型語言,並確保操做名稱是領域模型的術語。此外,應該將Service定義爲無狀態的。
如下是服務示例。
public class DesignerOrderWorkflowService { private DesignerOrderWorkflowService() { } private static Map<DesignerOrderState, DesignerOrderState[]> states = new HashMap<>(); static { states.put(DesignerOrderState.NEW, new DesignerOrderState[]{ DesignerOrderState.MEASURED, DesignerOrderState.ABORTED }); states.put(DesignerOrderState.MEASURED, new DesignerOrderState[]{ DesignerOrderState.QUOTED, DesignerOrderState.ABORTED }); states.put(DesignerOrderState.QUOTED, new DesignerOrderState[]{ DesignerOrderState.ACCEPT_QUOTE, DesignerOrderState.REJECT_QUOTE, DesignerOrderState.ABORTED }); states.put(DesignerOrderState.REJECT_QUOTE, new DesignerOrderState[]{ DesignerOrderState.QUOTED, DesignerOrderState.ABORTED }); states.put(DesignerOrderState.ACCEPT_QUOTE, new DesignerOrderState[]{ DesignerOrderState.PAID, DesignerOrderState.ABORTED }); states.put(DesignerOrderState.PAID, new DesignerOrderState[]{ DesignerOrderState.REFUND, DesignerOrderState.COMPLETION }); states.put(DesignerOrderState.COMPLETION, new DesignerOrderState[]{ DesignerOrderState.FEEDBACK }); states.put(DesignerOrderState.ABORTED, new DesignerOrderState[]{ DesignerOrderState.FEEDBACK }); states.put(DesignerOrderState.REFUND, new DesignerOrderState[]{ DesignerOrderState.FEEDBACK }); states.put(DesignerOrderState.FEEDBACK, new DesignerOrderState[]{ DesignerOrderState.FEEDBACK }); // 容許屢次評價 } public static boolean canChangeState(DesignerOrderState state, DesignerOrderState nextState) { Assert.notNull(state, "The state can not be null."); Assert.notNull(nextState, "The nextState can not be null."); DesignerOrderState[] nextStates = states.get(state); for (DesignerOrderState possibleNextState : nextStates) { if (possibleNextState.equals(nextState)) { return true; } } return false; } public static boolean canAbort(DesignerOrder order) { return canChangeState(order.getState(), DesignerOrderState.ABORTED); } public static DesignerOrderState changeState(long orderId, DesignerOrderState state, DesignerOrderState nextState) { if (!canChangeState(state, nextState)) { BusinessException.throwException(DomainExceptionMessage.STATE_CHANGE_ILLEGAL_CODE, DomainExceptionMessage.STATE_CHANGE_ILLEGAL, orderId, state, nextState); } return nextState; } public static boolean isCompleted(DesignerOrder order) { return order.getState() == DesignerOrderState.ABORTED || order.getState() == DesignerOrderState.REFUND || order.getState() == DesignerOrderState.COMPLETION || order.getState() == DesignerOrderState.FEEDBACK; } }
3 領域模型生命週期管理
領域模型的建立會包含業務規則,咱們應該將這些業務規則封裝起來,使建立過程對應用層透明,這裏引入Factory來實現建立。此外,對於實體,發生一系列事件後,其內部狀態發生了變動,這些狀態變動須要持久化,以使得應用程序可以恢復實體狀態。對於值對象,咱們可能也須要持久化相應的屬性。這裏,咱們引入Repository來實現持久化管理。對於一些關聯很緊密的對象,好比採購訂單和商品,他們須要共同的知足一個規則(好比採購訂單裏面的商品的總額不能超過採購訂單的限額),若是多個用戶同時變動採購訂單或者其包含的商品,就須要引入很複雜的鎖。爲了使關聯緊密的對象在整個生命週期都保持一致性,咱們引入了聚合Aggregate,經過它來實現一致性。
總結一句話:建立階段——Factory用於封裝實現領域對象建立的業務規則;建立、修改——Aggregate用於封裝緊密關聯領域對象在生命週期內的數據一致性;存儲——Repository用於封裝領域對象持久化的邏輯。
3.1 緊密關聯的領域對象的一致性維護—Aggregate
首先,咱們先看一下爲何要引入Aggregate。這裏以採購訂單爲例子,採購員建立採購訂單時須要指定限額,而後增長採購項目,所以可能存在兩個採購員對同一個建立的採購訂單進行操做,來更改訂單。
以下所示,對於採購訂單0012946,當前的商品金額爲700,限額爲1000。採購員A可能更改商品項1的數量爲5,其總額爲900,知足限額;採購員B可能更改商品項2的數量爲3,其總額也爲900,知足限額。
當採購員A、B同時提交更新後,採購訂單的總額爲1100,超過了1000元限額,破壞了業務規則。
在傳統的方法,當咱們採用如下方式更新採購訂單商品,就會出現剛纔破壞業務規則的狀況發生。
PurchaseOrder purchaseOrder = purchaseOrderBiz.getByKey(「0012946」); List<PurchaseOrderItem> purchaseOrderItems = purchaseOrderItemBiz.getByOrderId(「0012946」); changePurchaseOrderItems(purchaseOrderItems); if (new PurchaseOrderApprovedLimitSpecify(purchaseOrderItems, purchaseOrder).isSatisfied()) { purchaseOrderItemBiz.updateBatch(purchaseOrderItems); }
爲了不發生採購訂單限額的業務規則被破壞,對採購訂單項的變動,須要對採購訂單加排它鎖。
在DDD裏面,引入了聚合(Aggregate)來解決這個問題。Aggregate時一組相關對象的集合,做爲數據修改的單元,在整個生命週期中知足固定的業務規則。每一個Aggregate都有一個根(root)和一個邊界(boundary)。邊界定義了Aggregate的內部都有什麼,根則是Aggregate中所包含的一個特定Entity。在Aggregate中,根是惟一容許外部對象保持對它的引用的元素,而邊界內部的對象則能夠互相引用。基於聚合,咱們來實現一致的採購訂單業務規則以下。
(1)應用層經過如下方式來更新聚合根裏面的內容,這裏必須知足一致性規則:對聚合內部實體的狀態變動,只能經過聚合根來實現,經過聚合根來維持業務一致性。
PurchaseOrder order = purchaseOrderRepository.load(id); order.addItem(…)/removeItem(…)/updateItem(…); // 注意:這裏是重點,對聚合根內部的變動,只能經過聚合根,不能經過獲取內部對象進行操做 purchaseOrderRepository.save(order);
(2)聚合根對內部實體的狀態變動以下。
public class PurchaseOrder { private PurchaseOrderItemRepository orderItemRepository; private List<PurchaseOrderItem> orderItems; // …… public void addItem(int itemId, int count) { PurchaseOrderItem orderItem = PurchaseOrderItemFactory.create(this, itemId, count); orderItems.add(orderItem); if (!new PurchaseOrderApprovedLimitSpecification(this).isSatisfied()) { BusinessException.throwException(…); return; } orderItemRepository.save(orderItem); this.updateTimestamp(); } // …… }
聚合根定義的規則以下:
• 根Entity具備全局標識,它最終負責檢查固定規則。
• 根Entity具備全局標識。邊界內的Entity具備本地標識,這些標識只有在Aggregate內部纔是惟一的。
• Aggregate外部的對象不能引用除根Entity以外的任何內部對象。根Entity能夠把對內部Entity的引用傳遞給它們,但這些對象只能臨時使用這些引用,而不能保持引用。根能夠把一個Value Object的副班傳遞給另外一個對象,而沒必要關心它發生什麼變化,由於它只是一個Value,再也不與Aggregate有任何關聯。
• 做爲上一條規則的推論,只有Aggregate的根才能直接經過數據庫查詢獲取。全部其餘對象必須經過關聯的遍歷才能找到。
• Aggregate內部的對象能夠保持對其餘Aggregate根的引用。
• 刪除操做必須一次刪除Aggregate以內的全部對象。
• 當提交對Aggregate彬姐內部的任何對象的修改時,整個Aggregate中的全部固定規則都必須被知足。
3.2 領域模型的建立—Factory
當建立一個對象或建立整個Aggregate時,若是建立工做很負責,或者暴露了過多的內部結構,則可使用Factory進行封裝。領域模型的建立也可能隱含了業務規則,Factory能夠嚮應用層屏蔽業務規則。如下是一個設計師訂單的Factory類。
public class DesignerOrderFactory { private DesignerOrderFactory() {} public static DesignerOrder createOrder(int customerId, int designerId) { DesignerOrder designerOrder = new DesignerOrder(); designerOrder.setCustomerId(customerId); designerOrder.setDesignerId(designerId); designerOrder.setState(DesignerOrderState.NEW); return designerOrder; } }
結論:應該將建立複雜對象的實例和聚合的職責轉移給一個單獨的對象,這個對象自己在領域模型中可能沒有職責,但它還是領域設計的一部分。提供一個封裝全部複雜裝配操做的接口,並且這個接口應該不須要上層引用要被實例化的對象的具體類。在建立Aggregate時,要把它做爲一個總體,並確保它知足固定規則。
3.3 領域模型的持久化—Repository
Repository的目的是實現領域對象的持久化,用於領域對象關聯查詢、重建、添加和刪除。咱們只爲那些確實須要直接訪問的Aggregate提供Repository,將全部對象的存儲和訪問操做交給Repository。以下是一個實例。
@Repository public class DesignerOrderRepositoryImpl implements DesignerOrderRepository { private static final String DESIGNER_ORDER_TABLE = "designer_order"; @Autowired private DesignerOrderMapper designerOrderMapper; @Override public void create(DesignerOrder order) { if (designerOrderMapper.create(order) == 0) { TableException.throwTableException(DESIGNER_ORDER_TABLE, TableOperation.CREATE); } } @Override public DesignerOrder selectByKey(int id) { DesignerOrder order = designerOrderMapper.selectByKey(id); buildConnection(order); return order; } @Override public DesignerOrder selectOneBySpecification(DesignerOrder example) { DesignerOrder designerOrder = designerOrderMapper.selectOneBySpecification(example); buildConnection(designerOrder); return designerOrder; } @Override public List<DesignerOrder> selectBySpecification(DesignerOrder example) { List<DesignerOrder> designerOrders = designerOrderMapper.selectBySpecification(example); buildConnection(designerOrders); return designerOrders; } @Override public void update(DesignerOrder order) { if (designerOrderMapper.update(order) == 0) { TableException.throwTableException(DESIGNER_ORDER_TABLE, TableOperation.UPDATE); } } }
4 結論
領域驅動設計的模式以下所示。
綜上,領域層的實現由聚合構成,每個聚合一般包含了聚合根和領域模型實現、Service、工廠、Repository、領域異常等。
最終裝修設計預定平臺的領域模型以下所示。