原文: Applied Domain-Driven Design (DDD), Part 0 - Requirements and Modelling

About a year ago I have written a series of articles about Domain-driven design, you can find the main article here. Looking back I've realised that I've committed the most typical mistake and started to code my business domain without requirements or any draft designs!

大約在一年前 (本文作者寫於 2014 年 12 月), 我寫過一系列有關領域驅動設計的文章 (這個系列將陸續翻譯出), 你可以在這裡找到它的開篇章節. 回顧過去, 我意識到自己犯了最典型的錯誤, 最開始, 在沒有需求設計和任何設計草案的情況下, 就對我的業務領域開始寫程式碼了!

為此, 我打算嘗試修復這個問題, 以下是我準備要做的 (I am going to try and fix this, this is what I am going to do)

  • 識別使用者故事 (Identify User Stories)
  • 識別使用者故事的名詞 (Identify the Nouns in the user stories)
  • 識別使用者故事的動詞 (Identify the Verbs in the user stories)
  • 組合物件互動關係圖 (Put together object interaction diagram)
  • 組合物件職責關係圖 (Put together object responsibilities diagram)
  • 組合類的 UML 圖, 僅僅顯示有趣的互動. (Put together class digram UML showing only interesting interactions)

這是我虛構的一些使用者故事 (So here are made up user stories)

  • As a customer I want to be able to put products that I want to purchase in to the shopping cart so that I can check out quickly later on
  • 作為客戶, 我希望能夠將想要購買的產品放進購物車, 以便以後可以快速結賬.
  • As a customer I want to see the total cost all for all of the items that are in my cart so that I see if I can afford to buy everything
  • 作為客戶, 我希望知道購物車裡 所有 商品的總費用, 以便知道是否有能力購買所有的商品.
  • As a customer I want to see the total cost of each item in the shopping cart so that I can re-check the price for items
  • 作為客戶, 我希望知道購物車裡 每件 商品的總費用, 以便我重新檢查商品的價格.
  • As a customer I want to see the total cost for all of the items in the shopping cart with total tax
  • 作為客戶, 我希望看見購物車裡所有商品的總費用和總稅額.
  • As a customer I want to be able to specify the address of where all of the products are going to be sent to
  • 作為客戶, 我希望能夠指定所有商品將要發貨的收貨地址.
  • As a customer I want to be able to add a note to the delivery address so that I can provide special instructions to the postman
  • 作為客戶, 我希望能夠為交付地址新增備註, 以便於我能提給郵遞員供特殊說明.
  • As a customer I want to be able to specify my credit card information during check out so that I can pay for the items
  • 作為客戶, 我希望能夠在結賬的時候指定我的信用卡資料, 以便於我可以支付商品.
  • As a customer I want system to tell me how many items are in stock so that I know how many items I can purchase
  • 作為客戶, 我希望系統能夠告訴我庫存數量, 這樣我就知道可以購買多少個商品了.
  • As a customer I want shopping cart to check that items are still available for purchase during a check out so that I can still purchase items that are in the cart
  • 作為客戶, 我希望購物車能夠在結賬時檢查商品是否仍舊可以購買, 以便我仍然購買購物車裡的商品了.
  • As a customer I want to receive order confirmation email with order number so that I have proof of purchase
  • 作為客戶, 我希望能夠接收到帶有帶單號的訂單確認郵件, 以便於我獲得購物證明.
  • As a customer I want to specify invoice address for the order so that I can receive invoice for the order
  • 作為客戶, 我希望能夠指定訂單的發票地址, 便於我能接收到訂單的發票.

Now I am going extract nouns and verbs from the stories above. I am looking for the nouns that will become my main objects and not the attributes.

現在, 我將開始從上述故事中提取名字和動詞了. 我在尋找那些將成為主要物件非是屬性的名詞.

名詞 (Nouns)

  • 客戶 (Customer)
  • 商品 (Item)
  • 訂單 (Order)
  • 購物車 (Shopping Cart)
  • 收貨地址 (Address)
  • 發貨單 (Invoice)
  • 交付 (Delivery)
  • 稅費 (Tax)
  • 信用卡資料 (Credit Card Information)

*Note: I've removed duplicates for better, more official names, for example Item = Product, Order = Purchase, etc.

*注意: 為了更好, 更正式的名稱; 我把重複的名詞已經刪除了, 比如 商品 (Item) = 產品 (Product), 訂單 (Order) = 採購 (Purchase), 等等.

動詞 (Verbs)

  • 將產品新增到購物車. (Put products in to the shopping cart)
  • 檢視所有商品的總費用. (See total cost for all of the items)
  • 檢視每個商品的總費用. (See total cost for each item)
  • 檢視在自己國家的總稅金. (See total tax for my country)
  • 設定交付地址. (Specify delivery address)
  • 為送貨地址添加發貨單. (Specify delivery note for delivery address)
  • 設定發票地址. (Specify invoice address)
  • 接收訂單的發票. (Receive invoice for the order)
  • 傳送發票. (Sent invoice)
  • 設定信用卡資料. (Specify credit card information)
  • 為商品付款. (Pay for the items)
  • 告訴我商品有多少商品. (Tell me how many items are in stock)
  • 在結賬的時候檢查商品是否可用. (Check that items are still available during check out)
  • 接收訂單的確認郵件. (Receive order confirmation email)d

By using above nouns and verbs we can put together a diagram such as this:

通過使用以上的名詞和動詞, 我們能夠組合出像下面這樣的一個關係圖:

圖一: 物件互動圖

Once we have object interaction diagram we can start thinking about object responsibilities. One of the most common mistakes is to push responsibilities on to the actor object i.e. Customer. We need to remember that objects must take care of themselves and objects need to be closed for direct communication and that you need go through the functions to communicate with them.

一旦我們有了物件互動關係圖, 我們就能夠開始思考物件職責了. 最常見的錯誤之一是將職責推給參與者即客戶. 我們應該牢記, 物件必須自己照顧自己, 物件需要對直接通訊進行封閉, 而是通過函式與他們通訊.

So let's follow above approach and assign responsibilities:

讓我們遵循上面的方式來分配職責:

圖二 物件職責圖

Now that we have object interaction and responsibilities diagram in place we can start thinking about lower level UML class diagram:

現在已經有了物件互動圖和職責圖, 我們可以開始考慮更低級別的 UML 類圖了:

圖三 UML 類圖

Figure 3 shows methods, class names, dependencies, interfaces and composition. I've took a bit of time and reflected only on the most complex / interesting parts of the model. I will worry about attributes and other details later on, detail will naturally emerge when I start coding. Figure 3 is suppose to be a rough sketch, that is all, teams can whiteboard Figure 3 during a meeting, take a picture and distribute it to everyone in the team and get on with the actual coding. After a week or so picture will be forgotten and the parts of the above model (that have been useful) will live and breath in the actual code.

圖三展示了方法, 類名, 依賴項, 介面和組合. 我花了一些時間, 只考慮了模型中最複雜最有趣的部分. 稍後我將考慮屬性和其它細節. 當我開始編碼時, 細節會自然而然地浮現. 假設圖三是個粗略的草圖, 也就是說, 團隊可以在會議期間, 在白板上畫出圖三, 拍照並分發給團隊中的每個人, 然後開始實際編碼. 大約一週後, 圖片就會被遺忘, 並且上面模型的部分 (有用的部分) 就會在實際的程式碼中起到作用.

Now my made up user stories can be modelled in my many different ways and Figure 3 is just my interpretation of it. Key thing is to think about what you are building first, don't just jump in and start coding and don't get carried away with detail either (attributes, constructors, etc) focus on interesting and complex parts first.

現在, 我可以用多種不同的方式對虛構的使用者故事進行建模, 圖三就是我對它的解釋. 事情的關鍵是, 首先你要思考將要構建什麼, 不要一頭扎進去就開始寫程式碼, 更不要被屬性, 構造方法等細節所迷惑, 先關注複雜且有趣的部分.

總結 (Summary)

  • Don't start doing anything until you have requirements, if you don't have a BA in the company that's fine, you will have to do BA's job and identify requirements first.
  • 在有需求之前, 不要開始做任何事. 如果你在公司不是 BA (Business Analyst) 也沒關係, 你先做好 BA 的工作, 再明確需求.
  • Don't just jump in to the code soon as you have requirements, put together object interaction and responsibilities diagrams first.
  • 切勿一有需求就直接扎進程式碼裡, 先把物件互動和職責圖放在一起.
  • When you have identified your objects, interactions and responsibilities use UML class diagrams to put together a draft model (whiteboard sketch will do).
  • 當你已經確定了你的物件, 互動和職責; 就可以使用 UML 類圖來組合一個模型草稿(白板草圖就可以了)。
  • Don't try to model the reality of the world, model the reality of your organisation. Different companies will have different objects, in one company "address" might be an object and you might have "address type" coming of it (invoice, shipping, etc), in another company there will be "invoice address", "shipping address" and "seller address" object, that company might need these objects as these objects will inherit from the base "address" object. Remember it is all about your business domain and not the actual "reality".
  • 不要試圖模擬現實世界, 而是要根據組織去建模. 不同的公司具有不同的物件, 一家公司 "地址" 可能是一個物件, 而你可能會有 "地址型別" (發票, 運費等). 在另一個公司會有 "發票地址", "送貨地址" 和 "賣方地址" 物件, 此公司可能需要這些物件, 因為這些物件將從基本的 "地址" 物件繼承, 請記住, 這全都與業務領域有關, 而不是實際的 "現實".
  • 淨化的 UML (UML Distilled)
  • 雙排程模式 (Double Dispatch Pattern)
  • 關於 UML 你需要知道的一切 (All UML you need to know)
  • 物件 (Objects)
  • 類的關係 (Class Relationships)
  • 類的職責 (Class Responsibilities)

備註

[1] BA, 業務分析師. 在 IT 公司裡, BA 的角色就是PM (產品經理), 叫 BA 是因為這類 PM 要承接某個很具體的業務或者領域.