[IT] 乾淨架構 Clean Architecture

Robert C. Martin (aka Uncle Bob) 在 2012 年在他的部落格上發表了他對於乾淨架構的想法,並在幾個會議上進行了關於乾淨架構的演講。 乾淨架構套用了許多為人熟知的概念、規則和模式,並解釋如何將它們組合在一起,以提出一種標準化的應用程式建構方式。 站在 EBI, Ports & Adapters 與洋蔥架構的肩膀上 乾淨架構背後的核心目標與 Ports & Adapters(六邊形)和洋蔥架構的目標是相同的: 工具的獨立性。 交付機制的獨立性。 獨立測試的可行性。 在發布有關乾淨架構的文章中,這是用來解釋整體概念的一張圖: 正如 Uncle Bob 在他的文章中所說,上面這張圖嘗試將最新的架構思想整合成一個可行的概念。 讓我們將乾淨架構的圖表與用來解釋六角架構和洋蔥架構的圖表進行比較,看看它們在哪些地方相符: 工具和交付機制的外部化 六角形架構專注於將工具和交付機制從應用程式中外部化,使用介面(ports))和適配器(adapters)。這也是洋蔥架構的核心價值之一,如圖所見,UI、基礎設施和測試都在圖表的最外層。乾淨的架構具有完全相同的特性,將 UI、Web、DB 等都放在最外層。最後,所有應用程式核心程式碼都是與框架、庫獨立的。 依賴方向 在六角架構中,我們並沒有任何明確的指示告訴我們依賴性的方向。然而,我們可以輕易地推斷出來:應用程式有一個埠(或介面),必須由一個適配器來實現或使用。因此,適配器依賴於介面,它依賴於位於中心的應用程式。外部的東西依賴於內部的東西,依賴性的方向是朝向中心。在洋蔥架構圖中,我們也沒有任何明確的指示告訴我們依賴性的方向,然而,在他的第二篇文章中,Jeffrey Palermo 非常清楚地說明所有的依賴性都是朝向中心。乾淨架構則是非常明確地指出依賴性方向是朝向中心。他們都在架構層面引入了依賴反轉原則。內圈中的任何東西都不能知道外圈中的任何東西。此外,當我們跨越邊界傳遞數據時,它總是以對內圈來說最方便的形式存在。 分層 六角形架構圖只顯示了兩層:應用程式的內部和外部。然而,洋蔥架構則將 DDD 中 application layer 融入其中:application service 持有用例邏輯(use case logic);domain service 封裝不屬於實體或價值對象的領域邏輯。與洋蔥架構相比,乾淨架構保留了 application layer(use case)和 entities layer,但似乎忽略了 domain service layer。然而,閱讀 Uncle Bob 的文章後,我們意識到他認為一個 entity 不僅是 DDD 意義上的 entity,而且是任何 domain object:「一個實體可以是一個帶有方法的物件,或者可以是一組數據結構和函數。」實際上,他合併了這兩個最內層的層級以簡化圖表。 獨立測試性 在所有三種架構風格中,他們遵循的規則為應用程式和領域邏輯提供了隔離。這意味著在所有情況下,我們都可以簡單地模擬外部工具和傳遞機制,並在隔離中測試應用程式程式碼,而無需使用任何數據庫或 HTTP request。...

<span title='2023-10-29 21:11:58 +0800 +0800'>October 29, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[IT] 洋蔥架構 Onion Architecture

洋蔥架構 洋蔥架構是由 Jeffrey Palermo 在 2008 年提出的。在我看來,它是基於 Ports & Adapters 架構的構念,將領域(domain)放在應用程式的中心,將交付機制(UI)和系統使用的基礎設施(infrastructure, ex.ORM,搜索引擎,第三方API等)。差別是,它對內部進行了分層。 我們從分層架構學習到最基本的分層通常有: Presentation 呈現層 Application 應用層 Domain 領域層 Persistence 持久層 而 Ports & Adapters 架構隱含了兩個同心層: 外部:傳遞機制(delivery mechanisms)與基礎設施(infrastructure) 內部:業務邏輯 Ports & Adapters 和 Onion Architecture 同時擁有一個概念,那就是通過編寫適配器(adapter),將應用程式的核心與基礎設施隔離,以防止基礎設施滲透到應用程式核心中(意思是應用程式核心直接對基礎設施產生依賴)。這使得抽換應用程式使用的工具和交付機制變得更容易,提供了一些對技術、工具和供應商鎖定的保護。 這也賦予應用程式一種愉快的能力,即無需真實的基礎設施或交付機制就能運行,因為它們可以被模擬物件所替換,這易於進行程式碼的測試。 然而,洋蔥架構也告訴我們,在企業應用中,我們將不只有內部與外部這樣簡單的分層,在內部,也就是業務邏輯中,我們會增加一些我們從領域驅動設計(DDD)認識的一些層: 此外,它明確地闡述了 Ports & Adapters 架構中關於依賴方向的隱含概念: 外層依賴於內層, 內層對外層一無所知。 這意味著耦合的方向是朝向中心,為我們提供了一個獨立的物件模型(domain model),其核心不依賴任何東西。我們有足夠的靈活性可以改變外層,而不影響內層,更重要的層面。它在架構層面上運用了依賴反轉原則。 洋蔥架構的主要原則: 應用程式是建立在一個獨立的物件模型周圍。 內層定義介面,外層實現介面。 耦合的方向是朝向中心。 所有應用程式的核心代碼都可以獨立於基礎設施進行編譯和運行 此外,任何外層都可以直接呼叫任何內層,這不會破壞耦合方向,並避免創建僅包含無業務邏輯的 proxy methods,甚至是 proxy classes,僅為了符合某種分層方案。這也符合 Martin Fowler 所表達的。 […] the layers above can use any layer beneath them, not just the layer immediately beneath....

<span title='2023-10-29 20:34:57 +0800 +0800'>October 29, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[ML] sample1 - 手寫數字辨識

MNIST NIST(National Insitute of Standards and Technology) 是美國國家標準與技術研究院,MNIST 是由 NIST 所提供的一組經典的機器學習測資,可以想成是深度學習中的「Hello World!」,它由 60000張 訓練圖片與 10000 張測試圖片所組成,為手寫數字的灰階圖片,大小為 28 * 28 像素,分類 0 到 9 共 10 個數字。 可透過 keras 模組直接取得資料 >>> from tensorflow.keras.datasets import mnist 輸入 mnist.load_data() 可取得 mnist 資料集,回傳值為 2*2 的 tuple of ndarray。 >>> (train_images, train_labels), (test_images, test_labels) = mnist.load_data() tuple 裡面裝載的是 NumPy 的 ndarray 物件,我們可以利用 o.shape 來取得 ndarray 的屬性 len(o) 來取得陣列的個數 >>> train_images.shape (60000, 28, 28) # 3 軸陣列,其大小為 60000 * 28 * 28 >>> test_images....

<span title='2023-10-28 14:11:35 +0800 +0800'>October 28, 2023</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;Rain Hu

[IT] 埠與適配器架構 Ports & Adapters Architecture aka 六邊形架構 Hexagonal Architecture

埠與適配器架構(又稱為六角架構)是由 Alistair Cockburn 所構想,並在 2005 年於他的部落格中寫下。這是他用一句話定義其目標的方式: Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. - Alistair Cockburn 2005, Ports and Adapters 允許一個應用程式能夠同等地由用戶、程式、自動化測試或批次腳本驅動,並且能夠在與其最終運行時間設備和數據庫隔離的情況下進行開發和測試。 - 艾利斯特·科本 2005,端口與適配器 我看過一些討論「Ports & Adapters 架構」的文章,其中大量提到了分層(layers)。然而,在原始的 Alistair Cockburn 的文章中,我並未讀到任何關於分層的內容。 Ports & Adapters 架構的思想是將我們的應用程式視為系統的中心產物,所有的輸入和輸出都通過一個端口進入/離開應用程式,該端口將應用程式與外部工具、技術和交付機制隔離。應用程式應該對誰/什麼正在發送輸入或接收其輸出一無所知。這旨在提供一些保護,以防止技術和業務需求的演變,促使產品在開發完成後不久就因為技術/供應商的封鎖而變得過時。 在這篇文章中,我們將深入探討以下主題: 傳統方法的問題 傳統的方法在前後兩端都可能帶來問題。 在前端方面,我們最終會有業務邏輯滲透到 UI 中(例如,當我們在控制器或視圖中放置用例邏輯,使其在其他 UI 中無法重用)或甚至是 UI 滲透到業務邏輯中(例如,由於我們在模板中需要一些邏輯,因此我們在我們的實體中創建方法)。 在後端方面,我們可能會有外部庫和技術滲透到業務邏輯中,因為我們可能會透過類型提示、子類別化,甚至在我們的業務邏輯內實例化庫類別來直接引用它們。 從分層架構演變而來 到了 2005 年,多虧了 EBI 和 DDD,我們已經知道在系統中真正重要的是內部層。這些層是所有業務邏輯之所在,它們是我們與競爭對手的真正差異,是應用程式中價值的核心。...

<span title='2023-10-27 23:46:32 +0800 +0800'>October 27, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[IT] 領域驅動設計 Domain-Driven Design

「領域驅動設計」這個詞是在 Eric Evans 在他的著作《領域驅動設計:解決軟體核心的複雜性(Domain-Driven Design: Tackling complexity in the Heart of Software, 2003)》中所提出的,書中正式地提出了許多軟體開發的概念。 我無法用一篇文章就概括 DDD,與 DDD 相關的重要概念實在太多了。以下列出我認為一些重要的 DDD 概成,包含: Ubiquitous Language Layers Bounded Contexts Anti-Corruption Layer Shared Kernel Generic Subdomain 通用語言 Ubiquitous Language 在軟體開發中,一個常見的問題是怎麼理解程式碼,它是什麼,它能做什麼,它如何做,它為什麼這樣做…如果程式碼使用的術語與領域專家使用的術語不同,理解程式碼便變得更加複雜。例如,如果領域專家談論的是 elder users,而程式碼中提到的是 supervisors,那麼名詞可能就會造成在討論應用程式時造成混淆。然而,大部分的模糊性可以透過適當地命名類別和方法來解決,讓類別的命名明確地表達出物件是什麼,讓方法的命名明確地表達出方法在領域上下文中做了什麼事。 使用通用語言的主要概念是將應用程式與商業邏輯對齊,這是通過在程式碼中採用業務與技術之間的共同語言所實現的。該語言的來源是公司的業務部門,它們擁有需要實施的概念,但術語則與公司的技術部門協商(這意味著業務部門並不總是選擇最佳命名)以創建一種業務與程式開發人員共通且不會發生歧義的共同語言。包含程式碼、類別、方法、屬性和模組的命名都最重與通用語言對齊。 層 Layers 我在之前的文章中已經談過分層的概念,但我認為此刻重提由DDD所識別的各層是很重要的 User Interface 使用者介面 負責繪製用戶與應用程式互動的螢幕,並將用戶的輸入轉換為應用程式命令。值得注意的是,「用戶」可以是人類,但也可以是連接到我們API的其他應用程式,這完全對應於EBI架構中的邊界對象。 Application Layer 應用層 協調領域對象以執行用戶所需的任務:用例。它不包含業務邏輯。這與EBI架構中的互動者相關,只是互動者是與UI或實體無關的任何對象,而在這種情況下,應用層只包含與用例相關的對象。這一層是應用服務所屬的地方,因為它們是用例協調發生的容器,使用存儲庫、領域服務、實體、價值對象或任何其他領域對象。 Domain Layer 領域層 這是包含所有業務邏輯的層,包括領域服務、實體、事件以及任何其他包含業務邏輯的對象類型。顯然,它與EBI的實體對象類型有關。這是系統的核心。領域服務將包含不完全適合於實體的領域邏輯,通常在完成某些領域動作時協調多個實體。 Infrastructure 基礎建設 支援上層的技術能力,即持久性或訊息傳遞。 有界上下文 在企業應用中,模型可能會大幅增長,同時進行程式碼開發的團隊規模也可能會擴大。這帶來了兩個問題: 開發人員必須處理的程式碼庫越大,認知負荷就越大,理解程式碼的難度也就越高,因此可能會引入更多的錯誤和判斷失誤, 越多的開發人員在同一個程式碼庫上工作,就愈難協調對應用程式的共同技術與領域視野。 換句話說,手頭的問題變得過於龐大。 對於大問題的常見解決方案是將其分解成較小的部分,這正是「有界上下文」發揮作用的地方。 Two subsystems commonly serve very different user communities - Eric Evans 2014, Domain-Driven Design Reference...

<span title='2023-10-26 23:11:53 +0800 +0800'>October 26, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca