決定專案成敗的三件事 - 企業工藝

banq發表於2020-04-10

以下三點使您成功完成任何專案的90%的方法(不考慮可能的組織問題):
  • 跟隨YAGNI和KISS
    • YAGNI代表“您將不再需要”,並主張不要花時間在目前不需要的功能上
    • KISS致力於使其餘功能保持簡單
  • 實施域驅動設計(DDD)。尤其是:
    • 專注於核心領域
    • 維護封裝
  • 做單元測試

#1:跟隨YAGNI和KISS
第一個遵循YAGNI和KISS原則。
YAGNI代表“您不會需要它”,並主張不要花時間在目前不需要的功能上。
考慮一下軟體開發人員每天編寫的所有程式碼,並問自己-其中有多少真正符合客戶需求?和真正的我的意思是這樣的,該專案將不無程式碼滿足其功能性和非功能性需求?我敢打賭它不到100%。在某些情況下,明顯更少。
我記得自己在程式設計生涯的初期。對於任何任務,無論多麼簡單,我都試圖預見未來的需求,並儘可能多地佈置“擴充套件點”,以防萬一我們需要它們。結果,該應用程式發展成為一個框架,在該框架中,當前需求是該應用程式能夠提供的功能強大得多的特殊情況。我的目標是構建一個經得起時間考驗並且即使面對可能的需求變更也不需要進行大量修改的應用程式。
當然,這種思路是完全落後的。擴充套件點永遠不會出現在正確的位置;它們阻礙了未來發展。一個真正可擴充套件且易於更改的應用程式是您在其中延遲儘可能多的體系結構(即固定的)決策的應用程式。您不知道未來的需求。您最好的選擇是根本不要做出這樣的預測。
始終針對您的要求構建儘可能具體的解決方案。如此具體,以便於理解和更改(如果需要)。總是問自己這樣的問題:

  • 我真的需要一個基於事件的應用程式,而基於CRUD的簡單應用程式就可以了嗎?
  • 我真的需要具有使用暫停恢復功能非同步處理任何請求的功能嗎?


我們是好奇的生物,經常以“從長遠來看會有所回報”的態​​度合理化嘗試新事物的願望。它幾乎永遠不會。不要落入這個陷阱。
因此,再次,不要花時間在這個特定時刻不需要的功能上。您不應該開發該功能,也不應修改現有程式碼以考慮將來出現這種功能。兩個主要原因是:
  • 機會成本  -如果您花時間在業務人員當前不需要的功能上,那麼您就將時間從他們現在不需要的功能上轉移了。此外,當業務人員最終需要開發功能時,他們對功能的看法很可能已經演變,您仍然需要調整已經編寫的程式碼。這種活動是浪費的。當實際需要該功能時,從頭開始實施該功能將更為有益。
  • 專案中的程式碼越少越好。引入程式碼只是為了以防萬一,而不需要立即使用,從而不必要地增加了程式碼庫的擁有成本。最好將引入新功能的時間推遲到專案的最後階段。解決方案所需的程式碼越少,程式碼越簡單越好。


KISS原則代表“保持簡短和簡單”。它類似於YAGNI,人們經常將兩者混為一談。但是,儘管最好將這兩個原則結合在一起使用(這就是為什麼我將它們都排在第一位的原因),但從技術上講,它們並不是一回事。
  • YAGNI即將切斷不必要的功能;
  • KISS致力於使其餘功能儘可能簡單:

很難高估簡單性的重要性。簡單的程式碼使您輕鬆理解它,這是大多數應用程式最重要的屬性。
聽起來可能有爭議,但是請這樣考慮。更容易理解系統或系統的正確性哪個更重要?
在短期內,正確性當然更重要。但是,只要您繼續進行開發,可讀性就會開始發揮越來越大的作用。沒有錯誤但無法讀取的程式碼的情況是不穩定的。最有可能的是,由於您無法完全理解該程式碼庫,因此您將在以後的更改中引入錯誤。另一方面,使用簡單易懂的原始碼,您可以快速找到並修復這些錯誤。


#2:實施域驅動設計(DDD)
關於DDD中的戰術和戰略模式,有很多話要說。這些模式確實是有幫助的(只要它們不與YAGNI和KISS相矛盾),但是如果我將DDD歸結為絕對必要條件,我會這樣說。DDD最重要的部分是:

  • 專注於核心領域,
  • 保持封裝。


您可以自行決定選擇使用實體,值物件,聚合和其他典型DDD模式。不使用它們也很好。但是,以上兩種實踐是DDD的本質,在任何專案中都必須始終遵循。
那麼,它們到底是什麼意思?
專注於核心領域意味著:
  • 對程式碼庫進行結構設計,使其在域模型中具有眾所周知的,明確定義的位置。該領域模型是領域知識的有關專案是為了解決問題的集合。這是使您的應用程式與其他應用程式區分開來併為組織提供競爭優勢的原因。
    為域模型分配明確的邊界可以幫助您更好地視覺化程式碼並推理出該部分程式碼。邊界本身可以採取單獨的程式集或名稱空間的形式。只要所有域邏輯都放在一個單獨的保護傘下,細節就不那麼重要了。
  • 投入領域建模。通常,在滿足新專案要求時,程式設計師首先要設計資料庫結構,然後開始繪製UI以及與外部系統的互動,然後將所有這些放在一起。領域邏輯(又稱業務邏輯)只瀏覽了一下,並散佈在整個程式碼庫中-介於UI,資料庫和無數“服務”之間。
    這與您應該如何進行專案開發相反。域邏輯是應用程式中最重要的部分。它不僅必須在程式碼庫中擁有自己獨特的位置,還應將其放在所有其他元件之前,放在第一位。


很難高估領域模型周圍顯式邊界的重要性。這不僅涉及為域類提供單獨的目錄。它還涉及將這些域類與程式外依賴關係和所有其他應用程式問題隔離開,從而使其保持純淨和集中。
保持適當的域模型邊界的最佳方法是遵守六邊形體系結構。使用它,您可以使用兩層來表示應用程式:域模型和應用程式服務。

決定專案成敗的三件事 - 企業工藝
應用程式服務層位於域層之上,並協調該層與外部世界之間的通訊。例如,如果您的應用程式是RESTful API,則對此API的所有請求都會首先到達應用程式服務層。然後,該層協調域類和程式外依賴項之間的工作。
應用程式服務層和域層的組合形成一個六邊形,它本身代表您的應用程式。它可以與其他應用程式進行互動,這些應用程式以自己的六邊形表示。這些其他應用程式可以是SMTP服務,第三方系統,訊息匯流排等。一組相互作用的六邊形構成六邊形體系結構:

決定專案成敗的三件事 - 企業工藝
六角形/六邊形架構是一組互動的應用程式。
六邊形架構一詞是由Alistair Cockburn提出的。其目的是強調三個重要準則:

  • 域和應用程式服務層之間的關注點分離  -由於業務邏輯是應用程式中最重要的部分,因此域層應僅對該業務邏輯負責,而無需承擔其他所有責任。這些職責(例如與外部應用程式通訊和從資料庫檢索資料)必須歸因於應用程式服務。相反,應用程式服務不應包含任何業務邏輯。他們的職責是透過將傳入的請求轉換為對域類的操作,然後保留結果或將其返回給呼叫方來適應域層。您可以將域層視為應用程式領域知識的集合(操作方法)和應用服務層為一組業務用例(什麼對的)。
  • 應用程式內部的通訊  —六角體系結構規定了一種單向依賴關係流:從應用程式服務層到域層。域層內部的類僅應相互依賴。它們不應依賴於應用程式服務層中的類。該指南源於上一個指南。應用程式服務層和域層之間的關注點分離意味著前者瞭解後者,但事實並非如此。域層應與外界完全隔離。
  • 應用程式之間的通訊  -外部應用程式透過應用程式服務層維護的公共介面連線到您的應用程式。沒有人可以直接訪問域層。六邊形的每一側代表進入或退出應用程式的連線。


同樣,六邊形體系結構是保持對核心領域關注的最好方法。域模型位於六邊形的中心並不是巧合。

維護封裝
封裝是難題的另一個重要部分。僅定義具有明確邊界的模型層是不夠的。您還需要使該域模型內的所有操作保持內部一致。
這就是封裝起作用的地方。封裝是一種保護程式碼免遭不一致(也稱為不變違規)的行為。一個不變的是,應始終保持正確的條件。
從長遠來看,封裝對於程式碼庫的可維護性至關重要。原因是複雜性。程式碼複雜性是您在軟體開發中將面臨的最大挑戰之一。程式碼庫變得越複雜,使用起來就越困難,從而導致開發速度下降和錯誤數量增加。
沒有封裝,您將沒有實際方法來應對不斷增長的程式碼複雜性。當程式碼的API不能指導您完成該程式碼的內容和不允許執行的操作時,您必須記住很多資訊,以確保不會引起與新程式碼更改的不一致。這給程式設計過程帶來了額外的精神負擔。儘可能減輕您的負擔。您不能一直相信自己會做正確的事-消除了做錯事的可能性。 最好的方法是保持適當的封裝,以使您的程式碼庫甚至不提供錯誤執行任何操作的選項。
封裝可以透過以下方式實現:

  • 減少用於資料修改的API表面積,
  • 仔細檢查所有此類API。


作為程式設計師,您應該兩者兼而有之。您應該消除儘可能多的資料變異操作;並且您還應謹慎維護其餘此類操作,以確保內部一致性。
函數語言程式設計將這兩個指導方針發揮到了極致,並消除了所有可變的操作。使用不可變的類,您不必擔心內部狀態的損壞,因為不可能破壞最初無法更改的內容。因此,無需在函數語言程式設計中進行封裝。建立類的例項時,只需驗證一次類的狀態。之後,您可以隨意傳遞該例項。當您的所有資料都是不可變的時,與缺乏封裝相關的整個問題就會消失。

#3:進行單元測試
單元測試是每個專案為了長期成功而必須實踐的第三件事。
但是,僅進行單元測試是不夠的。只有高價值的測試才值得保留在您的測試套件中。關於如何構建一個非常有價值的測試套件(我在我最近的有關單元測試的書中做了),有很多要說的,我將嘗試在後續文章中介紹最重要的部分。
但是,最有趣的一點是,如果不關注核心領域和維護封裝,幾乎不可能編寫出有價值的測試。
編寫更好的單元測試將迫使您封裝域模型並構建設計良好的API。封裝又迫使您改善單元測試。

相關文章