Software Design - Architecture - Clean Architecture

以下 Clean Architecture 簡稱 CA

這裡還是先引用 Uncle Bob 的分層圖 The Clean Code Blog - The Clean Architecture CA Layers

筆者認爲可擴展性是系統架構的重要考量。畢竟應用程式會演化,必須不斷更新與修改系統以滿足新的需求,而 CA 是其中一種實現方針。筆者在這裡不會寫出詳細的介紹,想了解的可以參考 Ref 整理的連結或是 CA 相關書籍。

本文著重於促使筆者思考方式改變的幾個重要觀念。

依賴關係

  • 相依性: 向內圈依賴,且盡量避免跨層依賴 (有些例外之後說明)。
  • Dependency Inversion Principle (DIP): 內圈定義介面,外圈實作。

基於這兩個規則所帶來的是

  • 單向依賴流 : 紊亂的依賴流可能造成牽一髮動全身的窘境。尤其是當 Domain 去依賴到細節時。
  • 延後實作 : UseCase/Adapter 都是依賴於應用層所開出的介面。因此業務/畫面能獨立開發,不用互相等待(理想狀態)。

而筆者在實作時的基本型架構通常如下圖(比較接近 CA 書中的另外一張圖,我這裡做了簡化),Adapter 只分成 Input/Output Port。 Clean Architecture Basic

  • Domain: 領域邏輯
  • UseCase: 應用邏輯
  • Domain + UseCase: 業務邏輯
  • InputPort: 用例功能使用方介面
  • OutputPort: 用例功能支援方介面
  • Adapter: 將外部與用例功能接合的膠水代碼

不過架構會針對不同情況做調整,細節參考另一篇文章

架構設計 - Clean Architecture and Modularization

此外在原著中的定義描述

  • Entities 使用 enterprise wide business rules
  • UseCases 使用 application specific business rules

業務邏輯 | 商業邏輯 | 領域邏輯,如果你常在爬文,這幾個詞彙被混用的非常厲害,不同專業領域都有自己的一套說法。筆者的分法是參考 Link

依賴關係 (跨層)

Clean Architecture DataAccess

發生於 Data Access 的部分,DataAccess 介面簽名上是看的到 Entity 的。筆者參考很多評論覺得這是可以接受的,因為 “上下文單純”,Data Access Impl 只處理持久化物件與 Entity 之間的轉換,不做其他操作。

Use Case

用例是應用程式的說明書,可以很清楚的看到整個應用程式能做那些事,設種設計風格也稱作 尖叫的架構 (Screaming Architecture)

筆者直接感受到的好處是當業務發生變化時可以很快知道要改哪裡。

  • 定義 做什麼 而不是 如何做,透過介面隱藏實作細節
  • 業務流集中於一個地方,決定輸入/輸出

以下有一些 FAQ

  • 用例可以依賴其他用例嗎?

    • 可以但盡量避免,共用的用例筆者會盡量保持 Internal。
  • 用例與領域的界線開始變得很模糊,且彼此滲透。

    • 隨者應用需求不斷演化,也需重新評估 Domain(分割或合併)。此外當業務複雜度足夠高時,可以考慮導入 DDD(Domain-Driven Design),領域事件/聚合也許能幫助 Domain 更加內聚。
  • InputPort 有必要嗎? 依賴方向本來就向內

    • 當用例需要被抽象時是需要的,如果沒有那種需求不做也無妨。但為了統一般筆者基本上還是會寫。
  • 用例似乎有很多種風格的寫法

    • 確實常見的有 2 種: Service方法集 / CQRS風格

Adapter

筆者之前對套件更換這件事是感到非常棘手的,好像每次更換都有劇烈的不適,直到知道透過 DIP + Adapter 可以把不安定要素(Presentation/Database/Service)安置在應用程式的最外緣。完全改變實現是非常容易的,因為它對業務邏輯沒有影響。

Ref

Overview

概述,這些文章在對於從未知入門的人來說,圖表與條列項,可以為他們先指出一些方向。

Guide

以下參考著重在觀念引導,再加上實例輔助,必須說這些參考文都要包含"自己的理解"閱讀價值才得以顯現。

Discussion

以下參考著重觀念釐清,初期探索採坑的地方與一些討論串。

Notes

一些 CA 的閱讀心得。

Repo