[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

[IT] 套件與命名空間 packaging & namespacing

系統的架構是該系統的高層視圖、大局觀,以粗略的筆觸描繪的系統設計。架構決策是系統中的結構性決策,影響整個程式庫的決策,也是定義其他所有元素將在其上建立的決策。 架構決定系統的許多元素,包含: 組件 Components 組件之間的關係 Relationships between components 指導組件與組件間關係如何設計與如何演化 Principles guiding the design and evolution of components and relationships 換句話說,這些是隨著系統演進更難改變的設計決策,它們是支撐功能開發的基礎。 義大利麵架構 Spaghetti Architecture 有些專案,結構隨機,既不反映架構,也不反映領域。如果我問「我應該把這個 value object 放在哪裡?」結果得到「把它放在 src 資料夾的某個地方」這樣的回答;如果我問「執行這個邏輯的 service 在哪裡?」卻得到「用你的 IDE 進行搜索」這樣的回答。這意味著專案沒有經過組織,這樣鬆散的結構就稱為義大利麵架構(Spaghetti Architecture)。 這是一個大問題,因為這意味著沒有套件模組化,高階的程式碼關係和流程並沒有可以遵循的邏輯結構,導致模組之間高度耦合且低內聚,實際上可能代表根本沒有模組,應該屬於模組的程式碼散佈在整個程式碼庫中。 可維護的程式碼庫 擁有一個可維護的程式庫意味著我們可以變更最少的程式碼來實現最大的概念變更。換句話說,當我們需要對一個程式碼單元進行變更時,我們應該盡可能少地對其他程式碼單元進行變更。 這樣帶來的優點有: 程式碼的修改變得簡單,因為它們對較少的程式碼產生影響。 程式碼的修改會更快,因為需要修改的程式碼較少。 因為修改的程式碼變少,出現錯誤的可能性也更低。 封裝(encapsulation)、**低耦合(low coupling)和高內聚(high cohesion)**是使程式碼隔離的核心原則,使得我們能夠擁有可維護的程式碼基礎。 封裝 Encapsulation 這是隱藏類別的內部訊息與實作的過程。 也就是說,它對外隱藏了實作的方式,使得一個類別的內部結構可以自由變更,而不會影響使用這個特定類別的其他類別。 低耦合 Low coupling 耦合是指一個程式碼單元與另一個程式碼單元的關係。如果對一個模組的更改將導致對另一個模組的更改,則該模組被認為與另一個模組高度耦合。而如果一個模組獨立於任何其他模組,則該模組被認為是低耦合的。這可以通過擁有一個穩定的介面來實現,有效地隱藏了對其他模組的實現。 低耦合的好處 可維護性(maintainability) - 變更僅限於單一模組 可測試性(testability) - 可以將單元測試涉及的模塊限制到最小 可讀性(readability) - 需要分析的類別被保持在最小範圍內 高內聚 High Cohesion 內聚性是指一個模塊的功能之間的緊密相關程度的衡量。低內聚是指模組間具有許多不相關的職責;高內聚是指模組間有類似的概念。 高內聚的好處 可讀性(readability) - 相關的功能都包含在單一模塊中 可維護性(maintainability) - bug 通常會被限制在單一模組中 重用性(reusability) - 專注於類別的功能,不被無用的功能污染 結構上的影響 前述的原則通常與類別相關,然而,它們對於類別群組也同樣適用。類別群組在一般情況下被稱為套件(package),但如果它們具有純粹的功能性目標(例如 ORM),我們可以更具體地稱呼它們為模組(module);如果它們具有領域目標(例如 AccountManagement),我們可以稱呼它們為組件(components)。這與 Bass, Clements 和 Kazman 在他們的書《實踐軟體架構(Software Architecture in Practice)》中解釋的定義是一致的。...

<span title='2023-10-23 23:14:14 +0800 +0800'>October 23, 2023</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;hgraca