[IT] EBI 架構

EBI 架構全名是 Entity-Boundary-Interactor Architecture,第一次由 Robert C. Martin 在他乾淨架構(Clean Architecture)中的演講中提到。 然而,EBI 架構正式的發布是來自 Ivar Jacobson 在 1992 年所出版的 《物件導向的軟體工程:用例驅動方法(Object-Oriented Software Engineering: A use case driven approach)》。當時,Jacobson 實際上稱之為 Entity-Interface-Control,爾後才進行更名,為了避免將 Interface 與程式語言中的 Interface 或 User Interface 混淆;也避免將 Control 與 MVC 中的 Controller 混淆。 Entity 實體 Entity objects 持有所有系統使用的數據且持有所有與數據耦合的行為。每個 Entity object 代表一個與問題領域相關的概念,同時具備身份(identity)與永久性(persistence)。Jacobson 告訴我們,Entity object 應該要包含那些會因 entity 自身變化而變化的邏輯,也就是說,如果它持有的數據結構改變,則對該數據的操作也將需要變化,因此它們應該位於 entity中。 值得注意的是,Jacobson在1992年就已經發出了一個警告: Beginners may sometime only use entity object as data carriers and place all dynamic behaviour in control objects […]. This should, however be avoided. […] Instead, quite a lot of behaviour should be placed in the entity objects. - Ivar Jacobson 1992, pp. 134 初學者有時可能只將實體物件用作數據載體,並將所有動態行為放在控制物件中[…]。然而,這樣的做法應該避免[…]。相反,應該將相當多的行為放在實體物件中。 - 伊瓦爾‧雅各布森 1992, pp. 134 ...

<span title='2023-10-22 23:54:36 +0800 +0800'>October 22, 2023</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;hgraca

[IT] Model1 & Model2

Java Server Pages (JSP) 是一種技術,這種腳本語言與 PHP、ASP,甚至 Python 相當,用於創建由 JVM 解釋的伺服器端頁面,並可以使用 Java 物件。 首次由 Sun Microsystems 於 1998 年發布的 JSP 規範,定義了兩種結構化應用程式的方式,使得呈現邏輯能與業務邏輯,甚至是在 HTTP 請求/回應範疇中的使用案例,進行解耦。 有些人認為這些"Model1"和"Model2"是首次嘗試將原本為桌面軟體開發環境而設計的 MVC 模式,適配到網路 HTTP 請求/回應範疇的嘗試。 Model1 JSP 規範 v0.92 的首次提議,是將 JSP 作為唯一的呈現工具,其中包含所有的呈現和用例邏輯。 這種方法對當時的大多數使用情況可能已經足夠好,因為當時的網路大部分是由簡單的動態頁面組成,而不是我們今天所熟知的複雜的網路企業應用程式。 Model2 關於如何使用 JSP 的第二個建議,當時是針對被視為複雜的網路應用程式而設計的。然而,請記住,如今的網路應用程式的規模和複雜度已經更高了。 在“Model2”中,一個 HTTP 請求會到達一個 servlet,該 servlet 會解釋 HTTP 請求,使用 Java 物件和 EJBs(repositories)執行一些用例邏輯,收集結果數據,並將該數據傳遞給一個 JSP,該 JSP 再渲染頁面,並發送回客戶端。在“Model2”中,JSPs 僅作為模板引擎使用。 在1999年,Govind Seshadri 發表了一篇文章,其中他將"Model2"對應到 MVC: servelet 是 Controller,它控制應如何處理用戶的請求。 JSP 是 View,它決定了顯示給使用者的內容。 在 MVC 和"Model2"中,Model 指的都是一整個領域模型(domain model)。 我的看法 這兩種方法至今只能說是堪用,但對今日的網路企業應用程式而言,我們需要更好的東西,因為這兩種模式都不遵守單一職責原則(Single Respoinsibility Pinciple, RSP)。 ...

<span title='2023-10-19 22:47:32 +0800 +0800'>October 19, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[IT] Action-Domain-Responder(ADR) 架構

這篇文章將討論 MVC 的另一種變體:由 Paul M. Jones 創建的 Action-Domain-Responder。 2014 – Action-Domain-Responder(ADR) ADR 模式是由 Paul M. Jones 於 2014 年創建的,其想法就像 RMR 一樣,是為了將 MVC 調整到網路 REST APIs的 情境中。ADR 的原始解釋非常簡單明瞭,我實在無法更好地改述它,所以我將在這裡複製/貼上部分內容,並只添加一些更多的評論。 Action 行動 Is the logic to connect the Domain and Responder. It invokes the Domain with inputs collected from the HTTP request, then invokes the Responder with the data it needs to build an HTTP response. 這是連接 domain 和 responder 的邏輯,它會用從 HTTP 收集來的請求來觸發 domain,接著使用需要構建 HTTP 響應的數據來調用 responder。 ...

<span title='2023-10-19 22:43:08 +0800 +0800'>October 19, 2023</span>&nbsp;·&nbsp;4 min&nbsp;·&nbsp;hgraca

[IT] Resource-Method-Representation(RMR) 架構

MVC 於 1979 年在桌面應用程式與 CLI 使用者介面的背景下出現,這意味著如果資料庫因使用者以外的某些因素發生變化,則使用者介面將自動更改。同樣的模式後來在具有 GUI 的桌面應用程式上也完全可用。 然而,其在網路應用程式中的使用一直都是一種調適,因為大多數的網路應用程式並不會因為伺服器端的變更而改變使用者介面,使用者介面總是會呼叫伺服器端要求更新畫面。 我之前已經談過 MVC 模式的變體,這篇文章將討論另一種變體:Resource-Method-Representation。 我覺得有必要談論這個議題,是因為我曾對它產生誤解,認為它與 ADR 模式一樣,而我很快就會寫到這一點。 2008 – Resource-Method-Representation(RMR) RMR 模式是由 Paul James 在 2008 年創建的,它將 MVC 模式適配到 REST APIs 的情境中。 Resource 資源 The idea is that the Entities are modelled as REST resources (the first R in the pattern name), with its only public methods mapping to an HTTP method: 這個概念是將實體模型化為 REST resources 資源(RMR中的第一個R),與其唯一的公開方法映射到一個 HTTP 方法: <?php // taken from http://www.peej.co.uk/articles/rmr-architecture.html class Resource { private resourceData = []; method constructor(request, dataSource) { // load data from data source } method get(request) { return new Response(200, getRepresentation(request.url, resourceData)); } method put(request) { return new Response(405); } method post(request) { return new Response(405); } method delete(request) { return new Response(405); } } Method 方法 當向 API 發出請求時,該請求會被路由到這些業務物件之一,即資源,並且在此資源上被調用的方法對應於請求的 HTTP 方法。然後,這個業務物件上的方法負責返回一個完整的 http 響應,包括其狀態碼和 headers 信息。 ...

<span title='2023-10-19 22:17:59 +0800 +0800'>October 19, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[IT] MVC 及其變形

創立一個可維護的應用程式一直是程式設計的一項長期挑戰。 不久前,我在一家公司工作,其核心業務應用是一個 SaaS 平台,被幾千個客戶公司使用,這項應用程式已經運營了三年,其中的程式碼混雜了 HTML, CSS, 業務邏輯及 SQL,當然,在應用程式推出後的兩年,公司決定開始重構。儘管我們知道這樣的做法是不好的,且我們也知道如何避免,但是這樣的情況還是時常發生。 然後,回溯到 1970 年代,混合職責是很常見的做法,且人們仍在努力尋找如何改進。隨著應用程式的複雜性提升,對 UI 的更動必然會導致業務邏輯的更改,從而增加了修改的複雜度、執行的時間與 bug 出現的可能性。(因為會有更多的程式碼被更改)。 1979 - Model-View-Controller 為了解決上述問題,Trygve Reenskaug 於 1979 年提出了 MVC 架構,以此來將關注點分離,將 UI 與業務邏輯分離。該模式被應用於 1973年出現的桌面 GUI。 MVC 架構將程式分為三個部分: Model: 模型,代表了商業邏輯。 View: 視圖,代表了 UI 中的組件,如 button, text box 等。 Controller: 控制器,負責協調視圖和模型之間的配合,這意味著它: 決定要顯示哪些視圖,以及使用什麼數據。 將使用者行為轉化為業務邏輯。 A model could be a single object (rather uninteresting), or it could be some structure of objects. - Trygve Reenskaug 1979, MVC 一個模型可以是單一物件(相對無趣),或可以是一些物件的結構。 - 特里格維‧倫斯考 1979, MVC ...

<span title='2023-10-19 10:54:19 +0800 +0800'>October 19, 2023</span>&nbsp;·&nbsp;4 min&nbsp;·&nbsp;hgraca

[IT] 分層架構 Layered Architecture

分層是一種常見於系統中做法,用於分隔或組織程式碼,根據程式碼在系統中的角色或職責。 In an object-oriented program, UI, database, and other support code often gets written directly into the business objects. Additional business logic is embedded in the behaviour of UI widgets and database scripts. This happens because it is the easiest way to make things work, in the short run. When the domain-related code is diffused through such a large amount of other code, it becomes extremely difficult to see and to reason about. Superficial changes to the UI can actually change business logic. To change a business rule may require meticulous tracing of UI code, database code, or other program elements. Implementing coherent, model-driven objects becomes impractical. Automated testing is awkward. With all the technologies and logic involved in each activity, a program must be kept very simple or it becomes impossible to understand. - Eric Evans 2014, Domain-Driven-Design 在物件導向的程式中,使用者介面、資料庫和其他輔助程式碼常常直接被寫入商業物件中,額外的商業邏輯被嵌入在使用者介面和資料庫腳本,因為這是最簡單且最快速使事情可以運作的方式,導致這種情況時常發生。 當與領域相關的程式碼在更大量的程式碼中擴散,程式碼便開始變得困難且難以理解,UI 的變更可能導致業務邏輯的改變,同樣地,業務邏輯的更動需要密切地追縱 UI、資料庫與組其它組件的程式碼。實作內聚且以模型驅動的物件變得不可行、自動化測試變得笨拙,由於每個動作都涉及到所有的技術和邏輯,程式必須保持簡單,否則就會難以理解。 - 艾瑞克‧埃文斯 2014, 領域驅動設計 ...

<span title='2023-10-17 21:44:07 +0800 +0800'>October 17, 2023</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;hgraca

[IT] 單體架構 Monolithic Architecture

建立一個單體系統一直以來都是預設的架構風格,最初軟體開發剛萌芽時,每個應用程式只有一個檔案,然後才出現了包含多個檔案的應用程式,並且直到1990年代我們才開始看到由其他應用程式組成的應用程式(儘管第一次的實驗是在1980年代進行的)。 單體結構自身也在演進,當應用程式開始使用多個檔案進行建構時,因為這些應用程式相對簡單,所以對每個檔案的職責與檔案之間的關係並沒有太多的思考。但隨著應用程式變得越來越大且越來越複雜,我們便開始需要思考要創建哪些檔案以及如何關聯它。 模組化軟體開發 Modular Software Development 模組化程式設計是在 1960 年代晚期和 1970年代 所提出的解決方案。它是從類別演變到對粒度更大程式碼單元進行明確定義(explicit definition),程式語言以不同程度的明確性(explicitness)實現了模組化。 例如,JAVA 具有 default 和 public 的類別級別可見性,其中 default 級別意味著一個類別只在其套件(模組)中可見,而 public 則意味著該類別在其套件(模組)內外都可見,這讓我們可以定義哪些類別可以當作套件被客戶端使用。 組件化軟體開發 Componentized Software Development 另一種模組化的風格是組件。如我在之前的文章中所解釋的,組件是以領域概念為基礎創建的模塊。理想情況下,它們是可以用來創建複合應用的獨立「應用程式」。這種風格的一個常見例子是 pipes 和 filters 架構,這在 Unix 系統中被廣泛使用,並允許我們做像 ps -ef | grep php 這樣的操作。另一個例子是使用微服務作為複合應用的組件,如 Netflix。 這種程式碼組織方式也已經存在很長時間了,可以追溯到1960年代末,就像模組化軟體開發一樣。 現代的單體架構 現今,擁有單體架構風格簡單來說就是所有的應用程式碼都被部署(deployed) 並在單一節點(node) 上作為單一進程運行。我們假設它正在使用模組和組件,儘管事實上往往並非如此。 理解這裡的關鍵詞「部署」和「節點」至關重要。關於第一個詞,部署,這意味著無論程式碼在物理上儲存在一個或多個儲存庫的任何地方,重要的是它在運行時是如何組織的。關於第二個關鍵詞,節點,這意味著即使我們將應用程式部署到多個服務器,就像在水平擴展的情況下,它仍然是一個單體。 在單一節點伺服器中,單體中的所有模組都被組裝到同一記憶體 image 中,並在單一節點上作為單一進程運行。通訊是通過同一 heap 和 stack 進行標準程式調用。正是這種單一記憶體 image 使得應用程序變得單體化。如果你在不同的進程中運行模組,那麼你正在進行 IPC。因為模組落入不同的進程邊界,你將開始面臨分散式計算的挑戰,這就進入了微服務領域。 這種風格,儘管聲名狼藉,但即使對於大型應用程式也能運作得相當好。只有當我們需要以下情況時,它才不再足夠好: 不同領域組件的獨立可擴展性(Independent scalability)。 需要用不同的程式語言撰寫不同的組件或模組; 獨立部署能力(Independent deployability),或許是因為我們的釋出速率超過了單一程式碼庫的部署管道所能處理的範圍,導致一個版本的部署變慢,因為它需要等待其他版本的部署,甚至導致部署佇列的增長速度超過了它的消耗速度。 在那個時候,我們需要將我們的單體系統分離成不同的應用程式,並採用SOA架構風格(關於這點將在後續的文章中詳述)。 反模式:大泥球/義大利麵條式架構 Anti-pattern: Big Ball of Mud / Spaghetti Architecture 所謂的「泥球」,又稱為「義大利麵條式架構」,就是這種風格的反模式,其中套件的結構和關係並不明確,結構的內聚力和封裝性幾乎不存在,依賴性沒有遵循任何規則,並且很難理解子系統,以進行變更和重構。該系統是不透明的,黏稠的,脆弱的,且僵硬的:一個大泥球! ...

<span title='2023-10-15 12:06:51 +0800 +0800'>October 15, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[IT] 架構風格 vs. 架構模式 vs. 設計模式 Architectural Styles vs. Architectural Patterns vs. Design Patterns

在上一篇文章中,我介紹了程式語言的演進,以及它告訴我們的事情:軟體發展的驅勢是走向更多的模組化(modularity)與封裝(encapsulation)。 在接下來的文章,我會開始介紹架構風格與架構模式的演進。首先,我們要先知道什麼是架構風格,什麼是架構模式。 在軟體開發中的眾多術語中,定義往往不夠明確,卻不同人都有自己不同的解釋。MSDN 認為架構風格(architectural styles)與架構模式(architectural patterns)是同一碼子事,但我個人更傾向於 George Fairbanks 與 Michael Keeling 在 stack overflow 中與維基百科中的解釋。其關鍵的差異在於範疇(scope)。 同時,我們也需要強調這個觀念:架構風格、架構模式和設計模式並非互斥,它們相輔相成,每一種都能帶給我們一些啟示。然而,如同常情,我們只應在需要時才使用它們。 Architectural Styles 架構風格 架構風格非常大方向的告訴我們該如何組織我們的程式碼,它有著高層次的粒度。它定義了層的概念,尤其應用程式的高階模組。它告訴我們模組與層之間是怎麼交互作用的、它們的關係等等。架構風格的例子: Component-based Monolithic application Layered Pipes and filters Event-driven Publish-subscribe Plug-ins Client-server Service-oriented 一種建架構格可以透過各種方式實現,包括特定的技術環境、特定的政策、框架或實踐方法。 Architectural Patterns 架構模式 模式是一種對反覆出現的問題的反覆解決方案。在架構模式的情況下,他們解決與架構風格相關的問題。例如,「我們該有哪些類別,它們該如何互動,以實現具有特定層次的系統」,又或者「我們的服務導向架構將擁有哪些高級模組,以及他們將如何溝通」,或者「我們的客戶端-伺服器架構將擁有多少層」。 架構模式對程式碼庫有著廣泛的影響,通常會橫向(即如何在一層內結構化程式碼)或縱向(即如何從外層處理請求到內層並返回)影響整個應用程式。架構模式的例子: Three-tier 三層架構 Microkernel 微核心 Model-View-Controller 模型-視圖-控制器 Model-View-ViewModel 模型-視圖-視圖模型 Design Patterns 設計模式 設計模式與架構模式在範疇上有所不同,它們更為局部化,對程式碼庫的影響較小,它們只影響程式碼的特定部分,例如: 如何在我們只知道在運行時需要實例化什麼類型的情況下實例化一個對象(可能是一個工廠類別?) 如何讓一個物件根據其狀態(可能是一個狀態機,或者是策略模式?)表現出不同的行為? Conclusion 結論 如我在這篇文章的開頭所提到的,一切都關於範疇(scope): 架構風格是在最高抽象層次上的應用設計。 架構模式是實現架構風格的一種方式。 設計模式是解決局部問題的一種方式。 此外,一種模式可能既可以作為架構模式,也可以作為設計模式使用,這再次取決於我們在特定項目中使用它的範疇。

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

[IT] 程式語言的演進 Programming Language Evolution

程式設計語言本身並非架構的產物,但如果我不寫關於它,我會覺得《軟體架構編年史》似乎缺少了些什麼。 那麼,讓我們快速回顧一下程式語言的歷史,它的演變,並檢查我們可以從中學到什麼。我在文章中加入了一些日期,僅作為參考,它們應被視為大致的估計,重要的是演變的順序和他們試圖解決的問題。 1950s - Non-structured Programming Assembly ~1951 軟體開發曾是一項非常晦澀的活動,只在世界上少數地方進行。當時流行的語言是組合語言,它使用了非常低階的操作,如 add, sub, goto,並直接操作記憶體地址。建立一個簡單的應用程式既緩慢又困難。要創建一個簡單的 if 語句,我們需要幾行程式碼,對於一個迴圈,則需要超過幾行的程式碼…稍後才出現了將程式碼分組和重用的可能性,所以當時的編碼風格非常線性,程式碼的重用僅限於在檔案內或檔案間複製和貼上程式碼。 1960s – Structured Programming Algol ~1958, Fortran 結構化程式設計出現了,引入了 code blocks 概念,控制結構 if, then, else, case, for, while, do, …,和子程序的構造。然後,我們可以創建更有趣的程式流程,更重要的是,我們可以將程式碼指令分組並重用它,儘管有一些限制,例如子程序總是對同一全域變數進行操作。但就在這個時候,重用性 (reusability) 的概念開始被使用。 1970s – Procedural & Functional Programming Pascal ~1970, C ~1972 程序和函數式程式設計在1970年代開始活躍起來。到了這個時候,我們終於有了: 程序(Procedures):一組不返回數據的指令 函式(Functions):一組會回傳資料的指令集 資料結構(Data structures):記錄,類似於關聯式陣列 模組(Modules):可以被導入到其他程式碼檔案的程式碼檔案。 在1970年代,「Spaghetti code」這個詞也被創造出來,這是在Edsger W. Dijkstra於1968年寫給「計算機機構通訊」(CACM)的信中提出的,該信的標題為「Go To Statement Considered Harmful」。 在1970年代晚期,事件導向程式設計(Event Oriented Programming)的初步概念首次浮現,而Trygve Reenskaug則撰寫了他關於MVC(使用事件)的論文。 有了這些改進,因此我們有更好的重用性(reusability),因為子程序(程序和函式),我們可以使用不同的數據執行相同的邏輯。我們也可以通過將相關數據分組到複雜的數據結構中來模擬領域概念(domain concepts)。最後,我們在**解耦(decoupling)和模組化(modularity)**方面邁出了第一步,我們可以創建在其他程式碼文件和事件中可重用的程式碼,以將客戶端程式碼與正在執行的邏輯解耦。 1980s – Object Oriented Programming Simula ~1965, Smalltalk-71 ~1971, C++ ~1980, Erlang ~1986, Perl ~1987, Python ~1991, Ruby ~1993, Delphi, Java, Javascript, PHP ~1995 ...

<span title='2023-10-14 02:09:18 +0800 +0800'>October 14, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[IT] 軟體架構前提 The Software Architecture Premises

在這篇文章中,我將建立有關軟體架構的最初概念,這將有助於更好地理解接下來的文章。 沒有萬靈丹 無論你如何理解我在《軟體架構編年史》中所談論的內容,最重要的是要明白,並不存在萬能的解決方案。儘可能多學習不同的方法,理解每種方法的優點和缺點,以及它們解決的具體技術問題。 然後,在接受新的挑戰時,首先要理解業務和終端用戶的需求。只有在清楚理解這些需求之後,您才能理智地思考應該使用哪種架構風格和模式來更好地解決手頭的問題。 最後,做出你自己的選擇,也許實施其中一種已知的解決方案,或者創建你自己的設計來適應你特定的問題。 Some architectural styles are often portrayed as ‘silver bullet’ solutions for all forms of software. However, a good designer should select a style that matches the needs of the particular problem being solved. - Roy Fielding, 2000 有些架構風格常被描繪為所有軟體問題的「萬靈丹」解決方案。然而,一個優秀的設計師應該選擇一種與特定問題需求相匹配的風格。 - 羅伊·菲爾丁,2000 Terminology 術語 在軟體開發的世界裡,使用的術語充滿了模糊性,因此,在繼續之前,澄清我所使用的一些術語的意義是非常重要的。 Functional 功能性的 這是任何一段程式碼、方法、類別、類別群組,它在應用程式中擔任純粹的技術角色。它與領域無關,僅代表應用程式中的技術能力。例如: Layers Factories Repositories Value Objects Views ViewModels Conceptual 概念性的 這是任何一段代碼、方法、類別、一組類別,都反映了應用程式中的領域概念。它與領域直接相關,代表了應用程式中的業務能力。例如: User Product Stock Management Product Variants Checkout Upsells 這種分離並不意味著一個程式碼單元不能以兩種方式(功能性和概念性)被引用。例如,一個 Money 類別可以代表一個 domain concept,也可以是一個 value object。 ...

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