在軟體開發領域經常會接觸到架構這個辭彙,在我最初的印象中,架構是一個很高級的辭彙。它似乎代表了複雜的工程結構、高層次的抽象設計、最新的開發語言特性等等。對於當時只專註於寫業務邏輯的我來說,不免心生對架構的敬畏。工作中對架構的討論很少,出現則是一些高級晦澀的描述,但是從來沒有人清楚地解釋過架構做了哪些... ...
前言
在軟體開發領域經常會接觸到架構這個辭彙,在我最初的印象中,架構是一個很高級的辭彙。它似乎代表了複雜的工程結構、高層次的抽象設計、最新的開發語言特性等等。對於當時只專註於寫業務邏輯的我來說,不免心生對架構的敬畏。工作中對架構的討論很少,出現則是一些高級晦澀的描述,但是從來沒有人清楚地解釋過架構做了哪些事。所以,架構到底是什麼?架構和業務之間是什麼關係?
當我們看一些關於架構的書籍或者資料,不免會接觸到一些對架構的定義或者描述。比如:約束、規則、邊界、實體關係、模型定義等等。但是懂得這些概念並不能幫助我們設計出來更好的架構,當我們套用設計原則進行架構設計時,不免會覺得空洞乏味,總覺得少了點什麼。雖然我們為架構設計做了很多事,但是似乎什麼也沒做。因為只針對架構設計本身來說,很難說清楚它所產生的價值。所以,好的架構設計的出發點是什麼?好的架構應該是什麼樣的呢?
去年我有一個任務:將我們當前工程的代碼進行重新的拆分和組合,以釐清模塊間的關係,控制工程中模塊依賴的複雜度。這看起來是一個很簡單的工作,找到一個不同於當前的且更合理的目錄劃分方案,就可以嘗試落地實施。但是這又是一個很困難的工作,因為我們首先要回答有哪些模塊、模塊間是什麼依賴關係的問題。其實,回到任務的本身,我們並不是只想對代碼文件進行重新的組織和劃分,我們的目標是業務模塊解耦合,定義並明確業務模塊間的依賴規則。面對這樣的目標,我們需要首先從業務視角更清晰地定義和劃分模塊,然後從工程結構視角確定模塊間的關係。所以,代碼目錄調整實際上是一個對業務場景、工程結構理解和設計的問題。代碼目錄的結構代表了我們的工程結構,也是業務場景劃分的抽象描述,更是模塊定義以及模塊依賴關係的展現。
在設計代碼目錄劃分方案的過程中,看了一些工程結構設計的資料,讀了一些關於架構設計的書。對於架構有了一些理解。本文是對這段學習和任務完成過程的思考和沉澱。我希望能夠回答上面提到的幾個問題:
1.架構到底是什麼?架構和業務之間的關係
2.好的架構的設計出發點是什麼?好的架構應該是什麼樣的
什麼是架構
架構的定義
首先架構是一個漢語辭彙。它的定義是:人們對一個結構內的元素及元素間關係的一種主觀映射的產物。從這個定義可以看出,傳統的架構在描述一個系統中有什麼元素,以及元素之間關係。在建築領域,架構也用於描述建築物的結構。
作為一個電腦領域的辭彙,架構的定義是:有關軟體整體結構與組件的抽象描述,用於指導大型軟體系統各個方面的設計。實際上也在定義有什麼以及關係的問題。
從工程化解讀架構設計的作用
無論是在建築領域還是電腦領域,我們通常會用工程描述這類工作的項目。比如我所在的部門是工程技術中心,我是一個工程類的程式員等。我們可以稱之為工程的工作項目包括:建築工程、軍事工程、水利工程、生物工程、軟體工程等。而我們在完成項目的過程中,進行架構設計實際上就是推進實施工程化的一部分。那麼進行工程架構設計會考慮哪些因素,它對實施工程化的作用是什麼呢?
假設,讀者你現在是一位建築工程師,負責建造一棟房屋。
雖然我們沒有真正的蓋過房子,但是在進行房屋的整體結構設計時,你一定會關心這些:
1.房屋用途。首先要明確這棟房子是乾什麼用的
2.房屋層數。和用途緊密相關,不同用途的房子層數也是不一樣的
3.房屋外觀。定義這棟房屋應該長什麼樣
4.房屋的佈局。定義這棟房屋應該怎麼更好地被使用
等等。我們稱上面這幾個屬性是房屋的基礎能力。作為一個靠譜的建築工程師,你一定還會著重地設計這些:
1.水電走向。這很重要。保證房屋的安全性和使用的便捷
2.承重和抗壓。房屋的使用壽命很大程度上依賴於此
等等。我們稱上面的這幾個屬性是安全性和性能。
另外一方面,你大概不會關心房屋的裝修風格、地板顏色、衣櫃品牌等等因素。我們稱這些為應用細節。
總結來說,進行房屋的工程架構設計時更多地關係底層設計,而不在乎過多的技術細節。
所以,我們可以給架構的作用下一個定義:在明確用途的基礎上定義使用的規則和約束,提供了基礎的支撐能力,並保障安全性、性能和使用周期。
軟體架構設計的原則和要求
到目前為止,我們已經明確了在做架構設計時必須遵循的前提和原則:明確用途。此外也對架構設計提出要求:提供基礎能力、保障安全性、性能等。
同樣的,引申到電腦領域。當我們進行軟體架構設計時也必須遵循的原則有:
1.架構設計一定要從業務場景出發
這實際上就是明確用途的大前提。架構設計一定是要從業務出發、面向業務變化的。只有在我們明確了我們的業務場景和業務目標後,在此基礎上進行的架構設計才是能真正產生業務價值的。一個脫離了業務場景而設計的架構,無論多麼新穎和高級,也絕不是一個好的架構。
2.架構設計一定要落到業務場景中去驗證
我們不能只從基礎能力、安全性或者性能方面去評判一個架構的好壞。架構對業務開發的支持能力,面向業務變化時的靈活度以及持續演進能力等都是評判的因素。
此外,我們要求軟體架構必須是靈活的,能夠滿足未來業務持續發展的要求。
業務場景是不斷變化的,架構也要具有跟隨業務形態不斷演進的能力。架構設計的核心是保證面向業務變化時有足夠靈活的響應力,這要求架構設計能夠識別到業務的核心領域。所以,無論是面向當前還是面向未來,架構設計都需要真正地識別和理解業務問題。
架構設計的原則
本章節介紹幾個軟體架構設計時可以遵循的原則,實際上在進行功能模塊設計也可以參考這些設計原則。
SRP 單一職責原則
1.一個函數只負責完成一個功能
2.任何一個模塊只對某一類行為者負責
3.一個類或者函數應該有且僅有一個被改變的理由
在實際的編碼中,我們還是可以看到很多違反單一職責的例子的,比如超長的函數體。一個函數內做了很多事,實際上就是負責了太多的功能,很多的變更都要修改這個函數,這導致很難控制變更影響的範圍。
我們可以將大函數拆分成小函數,小函數體負責的功能更加單一,相應的也會更加靈活。所以我們建議大家多寫一些小的函數體。但是不要在函數拆分的過程中進行過度的封裝和抽象。
OCP 開閉原則
1.易於擴展,抗拒修改
模塊要易於擴展,控制修改。這是我們在初學編程語言時就會被教育到的設計原則。開閉原則幫助我們設計更加靈活的模塊,同時還能控制模塊變更的影響範圍。
LSP 里氏替換原則
1.所有引用父類的地方都可以替換成子類,而行為不發生改變
使用里氏替換原則可以保證父類的復用性。它主要是用來判斷抽象和繼承關係設計是否合理,即某個類是否應該具有某個屬性,以及一個類到底是不是另外一個類的子類。
舉一個典型的例子,乘馬是乘馬,乘白馬也是乘馬,乘黑馬也是乘馬。那麼白馬和黑馬就是馬的子類,是符合LSP的。
下麵是兩個典型的違反LSP原則的例子。也是網上也特別常見的例子。
第一個是正方形不是矩形。
class Rectangle { public: int32_t getWidth() const {return width;} int32_t getHeight() const {return height;} virtual void setWidth(int32_t w) { width = w; } virtual void setHeight(int32_t h) { height = h; } private: int32_t width = 0; int32_t height = 0; };
class Square : public Rectangle { public: void setWidth(int32_t w) override { Rectangle::setWidth(w); Rectangle::setHeight(w); } void setHeight(int32_t h) override { // … } }; void reSize(Rectangle rect) { while (rect.getHeight() <= rect.getWidth()){ rect.setHeight(rect.getWidth() + 1); } }
正方形類Square繼承自矩形類Rectangle,並且重寫了函數setWidth和setHeight。在函數reSize中,將父類Rectangle對象替換成子類Square後,將會出現死迴圈,程式出現異常。不符合LSP原則。
所以正方形不是矩形。第二個是鴕鳥不是鳥。
class Bird { public: int32_t getVelocity() const {return velocity;} private: int32_t velocity = 0; // 飛行速度 }; class Ostrich : public Bird { };
void crossRiver(Bird bird) { int32_t distance = 1000; int32_t elapsed = distance / bird.getVelocity(); }
鳥類Brid具有飛行速度的屬性,鴕鳥類Ostrich繼承自類Brid,飛行速度預設為0。在函數crossRiver中,將基類Brid對象替換成子類Ostrich對象後,獲取的飛行速度為0,出現了除0異常。不符合LSP原則。
所以鴕鳥不是鳥。
在這兩個例子中,結合里氏替換原則, 我們得出了兩個奇怪的結論,違背了幾何學和生物學的常識。其實問題在於我們對抽象和介面的設計上。比如前一個例子中reSize函數,它的條件判斷是有問題的。對於一個矩形對象,寬高不一定非得相等,所以將寬高相等作為迴圈的條件是不合理的。
對於後一個例子,飛行並不是鳥類的統一特征,所以抽象的鳥類不應該擁有飛行速度這個屬性,也不應該具有飛行的介面。那麼我們應該怎麼處理這個問題呢。準確來說,鳥類可以具有是否可以飛行的介面,然後有一個速度屬性。可以飛行的鳥返回飛行速度,而鴕鳥返回行走速度。
所以,里氏替換原則用於驗證我們的介面和抽象設計是否合理,同時也可以驗證繼承關係是否合理。
ISP 介面隔離原則
1.不依賴於自己不需要的東西
2.使用介面類的方式細化功能模塊,每個介面類負責某一類明確的功能
指導我們進行介面設計的原則。類似於單一職責原則,多個單一的介面負責的功能更簡單,更易於維護,這比一個龐大的介面要好。在做介面設計時要儘量保證介面的小巧、簡潔和正交,這樣給業務層提供了更多的靈活性。一個大的介面可能會做業務層並不希望做的事,同時當業務層需要擴展功能時也會使變更影響的範圍過大。
DIP 依賴反轉原則(依賴倒置)
1.為了保證系統的靈活性(易於修改)和穩定性(修改影響範圍小),在依賴關係中應該避免引用具體的類
2.介面比實現更穩定,所以儘量避免修改函數實現時對依賴該介面的模塊的影響
3.繼承關係是依賴關係中最強的,儘量避免繼承自有具體實現的類
這個原則目的在於降低使模塊間的耦合度,並且使底層模塊更易於被修改和替換。當下層功能發生變化時可以控制對上層業務的影響範圍,使得整體系統更加穩定和靈活。
DIP原則在後面章節介紹架構設計方法時也會多次提到。
以上這五個設計原則統稱為SOLID原則。在《整潔架構之道》中有比較詳細的介紹。
奧卡姆剃刀原則
奧卡姆剃刀原則不是在軟體開發領域提出的,而是在哲學領域提出的。奧卡姆剃刀原則對科學和哲學的發展都極為重要,因為它告訴人們理論應該儘量簡潔,理論中一切不影響結論的多餘部分都應該被剔除掉。
正如奧卡姆剃刀原則的精髓一樣,它的描述非常簡潔有力:如非必要,勿增實體。
我們也可以稱它為簡單即為美原則。通俗的描述是:用儘量少的步驟完成一件事。或者,如果對於一個事物有兩種解釋,採用最簡單或能被證偽的那種。正是因為奧卡姆剃刀原則,我們才更加相信哥白尼的日心說,更相信牛頓和愛因斯坦。否則,地球是宇宙中心的理論也沒錯,只是其他行星和恆星環繞地球的軌道公式也太複雜了,而且也容易被自然現象證偽。
在眾多的介紹軟體設計方法的書籍和資料中也多次提到過奧卡姆剃刀原則。應用到軟體開發領域,它確實給了我們很大的啟示。設想一下我們是不是遇到過這樣的場景:
1.費力地向別人解釋某個模塊為什麼那麼設計
2.為某段代碼加的註釋比代碼都多
3.為瞭解決一個問題而引入一個新的模塊
當我們費力說明和解釋某個代碼設計時,真正的問題並不在於我們解釋的不夠充分,或者聽眾不夠聰明理解不了,而在於代碼設計本身沒有很好地體現其業務語義。實際上過多的解釋和註釋都是多餘的,是可以被奧卡姆剃刀砍掉的。
對於為瞭解決一個問題而引入一個模塊也是在工作中經常遇到的問題。有很多原因導致某些模塊變得腐化難以維護,比如最初的設計沒有很好地貼合業務場景;編碼規範不夠好,後面的修改也沒有遵守規則;接手者沒有完全理解作者的意圖就著手修改等等。而程式員也經常會有的一個想法是:當一個模塊難以維護了,最好的方法是用一個新模塊替換掉它。實際上這種方法並沒有觸及問題的本質,在沒有找到導致模塊腐化的原因之前,在沒有制定規範的模塊設計方案之前,我們都不能保證新模塊不會有舊模塊一樣的問題。所以,想開發新模塊替換掉舊模塊很大程度上是在逃避對舊模塊問題的思考,新模塊也很有可能淪落到舊模塊一樣的地步。如果回答不了這個矛盾的問題,還是用奧卡姆剃刀把新模塊剔除掉吧,新模塊是多餘的,並沒有解決真正的問題。
奧卡姆剃刀原則保證解決問題的方法是簡單有效的,同時也約束我們應當思考更根本的問題,不能浮於問題表象採用最省力的方法。
其他的設計原則概覽
DRY(Dont Repeat Yourself):保證代碼的可復用性,避免代碼邏輯的重覆
YAGNI(You Aint Gonna Need It):代碼應易於擴展,但要避免過度設計,不要編寫當前用不到的代碼
KISS(Keep It Simple, Stupid):把事情想複雜,做簡單
POLA(Principle of Least Astonishment):最小驚奇原則。代碼應合乎邏輯和規範,給閱讀者最少的驚嚇。介面設計避免標新立異
常用的幾種架構設計
分層架構
分層架構是指基於具體的業務模型按照功能模塊將代碼進行分層組織。每一層代表了一組相關功能的集合。具體分為幾層沒有明確的規則,通常可以分為3-4層或者更多。在分層架構中,依賴關係是由上往下,上層依賴於下層,不能反向依賴。越往下的層次越通用,偏向於基礎能力。越往上層次越動態,偏向於業務。
分層架構設計按照依賴規則的嚴格程度分為嚴格型分層架構和鬆散型分層架構。嚴格型分層架構要求每一層只能訪問其直接依賴的層,不能訪問其間接依賴的層。鬆散型分層架構允許每一層訪問位於其下方的任意一層。嚴格型分層架構使得各個層之間的耦合度降到最低,但是靈活性不足,當上層需要訪問下麵間接層的能力時必須從上往下層層穿透。鬆散型分層架構在保證依賴規則的前提下提供了足夠的靈活性,所以大部分分層架構都是鬆散型的。
分層架構設計簡潔易懂。對抽象事物按照基礎特征進行分類,符合我們的思維習慣,易於理解。分層架構設計保證每一層內部有較好的內聚性,減少了層與層之間的耦合度,易於基礎能力的沉澱和復用,也易於控制變更帶來的風險。
另外一方面,分層架構設計雖然定義了多個層,但是層與層之間的邊界並不是特別清晰。對於新增的模塊有可能難以確定應該放在哪一層。或者隨著業務邏輯的變化,未來可能需要調整模塊所屬的層次。分層架構中,上層模塊對下層模塊有直接的依賴,下層模塊的實現直接向上層模塊暴露。在修改或者替換下層模塊時需要修改上層模塊,對上層業務的影響較大。業務實現與基礎能力沒有完全解耦合。
六邊形架構
又稱為埠-適配器架構。為瞭解決具體實現依賴於基礎能力的問題,採用依賴倒置設計方法將工程分為內部和外部。內部是具體的業務邏輯,外部是依賴的基礎能力。內部業務邏輯不再直接依賴於外部基礎能力,而是都依賴於其抽象定義。使用依賴註入的方式將外部實現傳入內部業務邏輯中。內部和外部使用介面進行交互,內部業務邏輯訪問基礎能力時直接調用其抽象介面即可。
六邊形架構解決了業務邏輯直接依賴外部模塊的問題,它們都依賴於抽象,不依賴於直接的實現和細節。它們直接通過定義好的介面進行交互。因為業務邏輯和外部模塊沒有直接的依賴關係,在修改和替換外部模塊時只需要按照介面定義實現功能,不需要改動業務邏輯。
洋蔥圈架構(整潔架構)
洋蔥圈架構又稱為整潔架構,結合了分層架構、六邊形架構和領域驅動設計特點的架構設計方法。洋蔥圈架構是對六邊形架構的進一步擴展,依賴關係依然是外部依賴內部。參考領域驅動設計,將依賴層次劃分為3-4層甚至更多。從內向外依次為:領域模型、業務邏輯、領域服務、基礎能力、外部模塊等。
洋蔥圈架構具有六邊形架構的優點,採用依賴倒置的原則使內部業務模型不再直接依賴於外部基礎能力。外部模塊的變動和替換不影響內部業務邏輯。採用領域驅動設計的方法劃分實體和模型,利於業務規則的抽象和業務模型的建立,對未來業務迭代的支持較好。洋蔥圈架構使業務實體、業務模型和業務實現處在裡層,保證了業務模型和實現的穩定,避免受到外部模塊變動的影響。
例如,使三方SDK或者資料庫系統屬於最外層,使用依賴註入的方法將它們的實現傳入內部邏輯。當替換三方SDK或者資料庫系統時,按照介面定義實現具體細節即可。不需要對內部邏輯進行改動。
領域驅動設計方法
領域驅動設計簡稱為DDD(Domain-Driven Design)。準確來說它不是一個架構設計方法,而是一種以業務分析和劃分來驅動系統架構設計的軟體開發方法。它強調識別業務的核心問題域來確定問題邊界,同時將問題域進行分解降低分析的複雜度。DDD強調通過關註業務核心提升業務價值。
下麵是DDD的一些核心概念,我們做一些簡單的介紹。
1.領域:有確定的範圍和邊界的業務問題域。實際上是我們要解決什麼業務問題的抽象描述。比如提供給用戶當前位置、目的地位置且提供到達信息是高德地圖的問題域。
2.子域:將大的問題域根據業務規則的不同拆分成的小問題域。比如高德地圖的問題域太大了,難以解決。我們可以將問題域拆分成定位、POI搜索、路線規劃等子問題域。
3.界限上下文:領域之間的抽象邊界。封裝了領域內的概念、規則和模型。
4.實體:具有唯一標識的、存在生命周期的對象。比如展示給用戶可見的POI氣泡是一個實體,它有狀態和確定的生命周期。
5.值對象:沒有唯一標識和生命周期的對象,依附於實體而存在。比如POI信息是值對象,本身沒有狀態,只能依附於POI氣泡這個實體而存在。
6.聚合:領域內一組實體、值對象的集合。封裝了集合與外界的交互
使用DDD對業務問題進行分析和拆解後,可以採用任何一種架構設計方法,無論是分層架構、六邊形架構或者整潔架構等。但是DDD要求架構設計從實際的業務場景出發,理解業務的核心問題。架構需要明確概念、規則的設計,並且保證業務模型的穩定性。使用分層架構展現DDD的領域設計方法,將工程分為4層:基礎設施層、領域層、應用層和用戶介面層。
我們所用的架構方案
鷹巢
我所在的團隊——鷹巢業務組負責高德地圖規劃和導航的業務能力實現。它向下對接引擎層,包括定位引擎、導航引擎、渲染引擎等,向上對接前端JS層。除了承接功能龐大、邏輯複雜的導航業務外,鷹巢還負責引擎能力的封裝以及將這些封裝能力向上層JS透出。
在進行代碼目錄劃分之前,鷹巢的功能實現也是按照模塊化進行設計的,但是模塊之間並沒有明確的依賴關係。任何代碼都可以互相的引用,這也就導致了工程中各模塊之間有錯綜複雜的調用關係,很難以說清楚某一個模塊應該處於哪個位置,應該如何被引用。雖然我們一直將工程代碼分為框架層和業務層,但是框架層和業務層之間的依賴關係並不明確。業務層依賴框架層,框架層也依賴了業務層,並不符合分層架構的設計原則,所以鷹巢的工程架構不屬於分層架構。
在我們去年的代碼目錄劃分的工作中,我們最終參考領域驅動設計的方法對代碼目錄進行了重新的組織和劃分。將工程代碼整體上分為4層:基礎能力、業務層、工具層和接入層。以下是整體結構圖:
適配層與以下的4層不在同一個倉庫,它包含了與前端JS交互的必要能力封裝。按照模塊的劃分規則,我們可以說,鷹巢的工程架構屬於結合領域驅動設計的鬆散型分層架構。它的特點是:
1.按照領域驅動設計對工程代碼進行組織和劃分,在業務層按照不同業務領域劃分代碼模塊
2.採用分層架構設計將工程分為多層,上層依賴於下層,下層不能依賴於上層
3.上層任意模塊都可以調用下層任意模塊,屬於鬆散型架構。更加靈活
工程技術中心C++能力層(包括地圖引擎層)
在工程技術中心的語言能力框架中,從鷹巢、地圖引擎到基礎庫都是C++語言實現。使用統一的流程管控它們的開發、構建、集成。在引擎架構升級之前的相當長的一段時間內,它們都屬於鬆散型分層架構,以下是簡化版的結構圖:
實際上,包括引擎庫在內的C++層有幾十上百個代碼倉庫,層次眾多,且從上層到下層的依賴關係複雜。如果將所有的依賴關係繪製出來,將是一個複雜的網狀。雖然整體架構依然遵守了分層架構的設計原則:只能上層依賴下層。但是因為依賴層次和關係的複雜,導致下層代碼的改動對上層的影響很大,在構建時也經常出現庫版本不匹配的衝突。這使得上層業務層經常處於不穩定狀態,不利於上層業務的快速迭代。並且下層能力升級也必然需要上層業務層做大工作量的適配。
在去年的引擎架構升級中,抽離出抽象層,使得各個倉庫都依賴於抽象介面,不再依賴於具體的實現。抽象出來的抽象層包括:InterfaceApp、InterfaceAR、InterfaceARWalk、InterfaceHorus、InterfaceMap、InterfaceVMap、InterfaceTBT、InterfacePosEngine等。比如鷹巢和TBT都依賴於InterfaceTBT抽象層,使用依賴倒置的原則在App初始化時將TBT的實例化對象設置給鷹巢。鷹巢通過調用實例化對象的抽象介面訪問TBT的能力。同理,鷹巢和渲染都依賴於InterfaceMap抽象層。這種方式使得上層的業務層比較穩定,只要保證抽象層介面的穩定性,業務層基本上就不會受到下層改動的影響。而且,當下層進行能力升級時,只要按照抽象介面定義實現對應能力即可,不需要業務層做適配。
從這方面來講,在引擎架構升級後,引擎具有整潔架構的特征。但是並不能完全稱為整潔架構,因為從更大的視角來看(將基礎庫和Native層包括進去),依然是鬆散型的分層架構。所以,我們可以稱之為具有整潔架構特征的鬆散型分層架構。
總結
對於架構設計的學習和理解,我認為很難的一點是:即使懂得很多道理還是很難把事情做好。眾多的設計原則都是在不同業務場景下提出的,有些原則之間本身就是矛盾的。無論是架構設計方法還是設計原則,它們不是金科玉律,更不可能放之四海而皆準。它們的價值在於告訴我們應該摒棄什麼,應該遵守什麼。我們不用那些技術官僚的辭彙,用更接地氣的描述來說,設計原則也只是要求我們做到簡潔、規範和易於理解而已。架構設計並不高端,它本身所產生的價值並不明顯,真正能夠產生價值的在於我們當前正在走的路:如何理解我們的業務問題。
參考資料和書籍:
應用架構之道:分離業務邏輯和技術細節:https://www.cnblogs.com/alisystemsoftware/p/13846127.html
The Onion Architecture:https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/
《架構整潔之道》、《領域驅動設計-ThoughtWorks洞見》、《代碼精進之路-從碼農到工匠》、《UNIX編程藝術》
附錄:
工程:是指以某組設想的目標為依據,應用有關的科學知識和技術手段,通過有組織的一群人將某個(或某些)現有實體(自然的或人造的)轉化為具有預期使用價值的人造產品過程
工程化:是指以提高效率、降低成本、保證質量保證為目的從而促進多人合作,實現功能強大,健壯性好的項目的手段和措施
作 者 | 張東愛(當愛)
本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/Learn-and-understand-architecture-design-from-business-development.html