自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

從業(yè)務(wù)開發(fā)中學(xué)習和理解架構(gòu)設(shè)計

原創(chuàng) 精選
開發(fā) 架構(gòu)
對于架構(gòu)設(shè)計的學(xué)習和理解,我認為很難的一點是:即使懂得很多道理還是很難把事情做好。眾多的設(shè)計原則都是在不同業(yè)務(wù)場景下提出的,有些原則之間本身就是矛盾的。無論是架構(gòu)設(shè)計方法還是設(shè)計原則,它們不是金科玉律,更不可能放之四海而皆準。

作者 |  張東愛(當愛)

前言

在軟件開發(fā)領(lǐng)域經(jīng)常會接觸到架構(gòu)這個詞匯,在我最初的印象中,架構(gòu)是一個很高級的詞匯。它似乎代表了復(fù)雜的工程結(jié)構(gòu)、高層次的抽象設(shè)計、最新的開發(fā)語言特性等等。對于當時只專注于寫業(yè)務(wù)邏輯的我來說,不免心生對架構(gòu)的敬畏。工作中對架構(gòu)的討論很少,出現(xiàn)則是一些高級晦澀的描述,但是從來沒有人清楚地解釋過架構(gòu)做了哪些事。所以,架構(gòu)到底是什么?架構(gòu)和業(yè)務(wù)之間是什么關(guān)系?

當我們看一些關(guān)于架構(gòu)的書籍或者資料,不免會接觸到一些對架構(gòu)的定義或者描述。比如:約束、規(guī)則、邊界、實體關(guān)系、模型定義等等。但是懂得這些概念并不能幫助我們設(shè)計出來更好的架構(gòu),當我們套用設(shè)計原則進行架構(gòu)設(shè)計時,不免會覺得空洞乏味,總覺得少了點什么。雖然我們?yōu)榧軜?gòu)設(shè)計做了很多事,但是似乎什么也沒做。因為只針對架構(gòu)設(shè)計本身來說,很難說清楚它所產(chǎn)生的價值。所以,好的架構(gòu)設(shè)計的出發(fā)點是什么?好的架構(gòu)應(yīng)該是什么樣的呢??

去年我有一個任務(wù):將我們當前工程的代碼進行重新的拆分和組合,以厘清模塊間的關(guān)系,控制工程中模塊依賴的復(fù)雜度。這看起來是一個很簡單的工作,找到一個不同于當前的且更合理的目錄劃分方案,就可以嘗試落地實施。但是這又是一個很困難的工作,因為我們首先要回答有哪些模塊、模塊間是什么依賴關(guān)系的問題。其實,回到任務(wù)的本身,我們并不是只想對代碼文件進行重新的組織和劃分,我們的目標是業(yè)務(wù)模塊解耦合,定義并明確業(yè)務(wù)模塊間的依賴規(guī)則。面對這樣的目標,我們需要首先從業(yè)務(wù)視角更清晰地定義和劃分模塊,然后從工程結(jié)構(gòu)視角確定模塊間的關(guān)系。所以,代碼目錄調(diào)整實際上是一個對業(yè)務(wù)場景、工程結(jié)構(gòu)理解和設(shè)計的問題。代碼目錄的結(jié)構(gòu)代表了我們的工程結(jié)構(gòu),也是業(yè)務(wù)場景劃分的抽象描述,更是模塊定義以及模塊依賴關(guān)系的展現(xiàn)。

在設(shè)計代碼目錄劃分方案的過程中,看了一些工程結(jié)構(gòu)設(shè)計的資料,讀了一些關(guān)于架構(gòu)設(shè)計的書。對于架構(gòu)有了一些理解。本文是對這段學(xué)習和任務(wù)完成過程的思考和沉淀。我希望能夠回答上面提到的幾個問題:

  • 架構(gòu)到底是什么?架構(gòu)和業(yè)務(wù)之間的關(guān)系
  • 好的架構(gòu)的設(shè)計出發(fā)點是什么?好的架構(gòu)應(yīng)該是什么樣的

什么是架構(gòu)

架構(gòu)的定義

首先架構(gòu)是一個漢語詞匯。它的定義是:人們對一個結(jié)構(gòu)內(nèi)的元素及元素間關(guān)系的一種主觀映射的產(chǎn)物。從這個定義可以看出,傳統(tǒng)的架構(gòu)在描述一個系統(tǒng)中有什么元素,以及元素之間關(guān)系。在建筑領(lǐng)域,架構(gòu)也用于描述建筑物的結(jié)構(gòu)。?  

作為一個計算機領(lǐng)域的詞匯,架構(gòu)的定義是:有關(guān)軟件整體結(jié)構(gòu)與組件的抽象描述,用于指導(dǎo)大型軟件系統(tǒng)各個方面的設(shè)計。實際上也在定義有什么以及關(guān)系的問題。

圖片

從工程化解讀架構(gòu)設(shè)計的作用

無論是在建筑領(lǐng)域還是計算機領(lǐng)域,我們通常會用工程描述這類工作的項目。比如我所在的部門是工程技術(shù)中心,我是一個工程類的程序員等。我們可以稱之為工程的工作項目包括:建筑工程、軍事工程、水利工程、生物工程、軟件工程等。而我們在完成項目的過程中,進行架構(gòu)設(shè)計實際上就是推進實施工程化的一部分。那么進行工程架構(gòu)設(shè)計會考慮哪些因素,它對實施工程化的作用是什么呢?假設(shè),讀者你現(xiàn)在是一位建筑工程師,負責建造一棟房屋。雖然我們沒有真正的蓋過房子,但是在進行房屋的整體結(jié)構(gòu)設(shè)計時,你一定會關(guān)心這些:

  • 房屋用途。首先要明確這棟房子是干什么用的
  • 房屋層數(shù)。和用途緊密相關(guān),不同用途的房子層數(shù)也是不一樣的
  • 房屋外觀。定義這棟房屋應(yīng)該長什么樣
  • 房屋的布局。定義這棟房屋應(yīng)該怎么更好地被使用

等等。我們稱上面這幾個屬性是房屋的基礎(chǔ)能力。作為一個靠譜的建筑工程師,你一定還會著重地設(shè)計這些:

  • 水電走向。這很重要。保證房屋的安全性和使用的便捷
  • 承重和抗壓。房屋的使用壽命很大程度上依賴于此

等等。我們稱上面的這幾個屬性是安全性和性能。另外一方面,你大概不會關(guān)心房屋的裝修風格、地板顏色、衣柜品牌等等因素。我們稱這些為應(yīng)用細節(jié)??偨Y(jié)來說,進行房屋的工程架構(gòu)設(shè)計時更多地關(guān)系底層設(shè)計,而不在乎過多的技術(shù)細節(jié)。所以,我們可以給架構(gòu)的作用下一個定義:在明確用途的基礎(chǔ)上定義使用的規(guī)則和約束,提供了基礎(chǔ)的支撐能力,并保障安全性、性能和使用周期。

軟件架構(gòu)設(shè)計的原則和要求

到目前為止,我們已經(jīng)明確了在做架構(gòu)設(shè)計時必須遵循的前提和原則:明確用途。此外也對架構(gòu)設(shè)計提出要求:提供基礎(chǔ)能力、保障安全性、性能等。

同樣的,引申到計算機領(lǐng)域。當我們進行軟件架構(gòu)設(shè)計時也必須遵循的原則有:

  • 架構(gòu)設(shè)計一定要從業(yè)務(wù)場景出發(fā)

這實際上就是明確用途的大前提。架構(gòu)設(shè)計一定是要從業(yè)務(wù)出發(fā)、面向業(yè)務(wù)變化的。只有在我們明確了我們的業(yè)務(wù)場景和業(yè)務(wù)目標后,在此基礎(chǔ)上進行的架構(gòu)設(shè)計才是能真正產(chǎn)生業(yè)務(wù)價值的。一個脫離了業(yè)務(wù)場景而設(shè)計的架構(gòu),無論多么新穎和高級,也絕不是一個好的架構(gòu)。

  • 架構(gòu)設(shè)計一定要落到業(yè)務(wù)場景中去驗證

我們不能只從基礎(chǔ)能力、安全性或者性能方面去評判一個架構(gòu)的好壞。架構(gòu)對業(yè)務(wù)開發(fā)的支持能力,面向業(yè)務(wù)變化時的靈活度以及持續(xù)演進能力等都是評判的因素。

此外,我們要求軟件架構(gòu)必須是靈活的,能夠滿足未來業(yè)務(wù)持續(xù)發(fā)展的要求。

業(yè)務(wù)場景是不斷變化的,架構(gòu)也要具有跟隨業(yè)務(wù)形態(tài)不斷演進的能力。架構(gòu)設(shè)計的核心是保證面向業(yè)務(wù)變化時有足夠靈活的響應(yīng)力,這要求架構(gòu)設(shè)計能夠識別到業(yè)務(wù)的核心領(lǐng)域。所以,無論是面向當前還是面向未來,架構(gòu)設(shè)計都需要真正地識別和理解業(yè)務(wù)問題。

架構(gòu)設(shè)計的原則

本章節(jié)介紹幾個軟件架構(gòu)設(shè)計時可以遵循的原則,實際上在進行功能模塊設(shè)計也可以參考這些設(shè)計原則。

SRP 單一職責原則

1.一個函數(shù)只負責完成一個功能2.任何一個模塊只對某一類行為者負責3.一個類或者函數(shù)應(yīng)該有且僅有一個被改變的理由在實際的編碼中,我們還是可以看到很多違反單一職責的例子的,比如超長的函數(shù)體。一個函數(shù)內(nèi)做了很多事,實際上就是負責了太多的功能,很多的變更都要修改這個函數(shù),這導(dǎo)致很難控制變更影響的范圍。我們可以將大函數(shù)拆分成小函數(shù),小函數(shù)體負責的功能更加單一,相應(yīng)的也會更加靈活。所以我們建議大家多寫一些小的函數(shù)體。但是不要在函數(shù)拆分的過程中進行過度的封裝和抽象。

OCP 開閉原則

1.易于擴展,抗拒修改模塊要易于擴展,控制修改。這是我們在初學(xué)編程語言時就會被教育到的設(shè)計原則。開閉原則幫助我們設(shè)計更加靈活的模塊,同時還能控制模塊變更的影響范圍。

LSP 里氏替換原則

1.所有引用父類的地方都可以替換成子類,而行為不發(fā)生改變

使用里氏替換原則可以保證父類的復(fù)用性。它主要是用來判斷抽象和繼承關(guān)系設(shè)計是否合理,即某個類是否應(yīng)該具有某個屬性,以及一個類到底是不是另外一個類的子類。舉一個典型的例子,乘馬是乘馬,乘白馬也是乘馬,乘黑馬也是乘馬。那么白馬和黑馬就是馬的子類,是符合LSP的。下面是兩個典型的違反LSP原則的例子。也是網(wǎng)上也特別常見的例子。第一個是正方形不是矩形。

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,并且重寫了函數(shù)setWidth和setHeight。在函數(shù)reSize中,將父類Rectangle對象替換成子類Square后,將會出現(xiàn)死循環(huán),程序出現(xiàn)異常。不符合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。在函數(shù)crossRiver中,將基類Brid對象替換成子類Ostrich對象后,獲取的飛行速度為0,出現(xiàn)了除0異常。不符合LSP原則。

所以鴕鳥不是鳥。

在這兩個例子中,結(jié)合里氏替換原則, 我們得出了兩個奇怪的結(jié)論,違背了幾何學(xué)和生物學(xué)的常識。其實問題在于我們對抽象和接口的設(shè)計上。比如前一個例子中reSize函數(shù),它的條件判斷是有問題的。對于一個矩形對象,寬高不一定非得相等,所以將寬高相等作為循環(huán)的條件是不合理的。

對于后一個例子,飛行并不是鳥類的統(tǒng)一特征,所以抽象的鳥類不應(yīng)該擁有飛行速度這個屬性,也不應(yīng)該具有飛行的接口。那么我們應(yīng)該怎么處理這個問題呢。準確來說,鳥類可以具有是否可以飛行的接口,然后有一個速度屬性??梢燥w行的鳥返回飛行速度,而鴕鳥返回行走速度。

所以,里氏替換原則用于驗證我們的接口和抽象設(shè)計是否合理,同時也可以驗證繼承關(guān)系是否合理。

ISP 接口隔離原則

  • 不依賴于自己不需要的東西
  • 使用接口類的方式細化功能模塊,每個接口類負責某一類明確的功能

指導(dǎo)我們進行接口設(shè)計的原則。類似于單一職責原則,多個單一的接口負責的功能更簡單,更易于維護,這比一個龐大的接口要好。在做接口設(shè)計時要盡量保證接口的小巧、簡潔和正交,這樣給業(yè)務(wù)層提供了更多的靈活性。一個大的接口可能會做業(yè)務(wù)層并不希望做的事,同時當業(yè)務(wù)層需要擴展功能時也會使變更影響的范圍過大。

DIP 依賴反轉(zhuǎn)原則(依賴倒置)

  • 為了保證系統(tǒng)的靈活性(易于修改)和穩(wěn)定性(修改影響范圍?。谝蕾囮P(guān)系中應(yīng)該避免引用具體的類
  • 接口比實現(xiàn)更穩(wěn)定,所以盡量避免修改函數(shù)實現(xiàn)時對依賴該接口的模塊的影響
  • 繼承關(guān)系是依賴關(guān)系中最強的,盡量避免繼承自有具體實現(xiàn)的類

這個原則目的在于降低使模塊間的耦合度,并且使底層模塊更易于被修改和替換。當下層功能發(fā)生變化時可以控制對上層業(yè)務(wù)的影響范圍,使得整體系統(tǒng)更加穩(wěn)定和靈活。

DIP原則在后面章節(jié)介紹架構(gòu)設(shè)計方法時也會多次提到。

以上這五個設(shè)計原則統(tǒng)稱為SOLID原則。在《整潔架構(gòu)之道》中有比較詳細的介紹。

奧卡姆剃刀原則

奧卡姆剃刀原則不是在軟件開發(fā)領(lǐng)域提出的,而是在哲學(xué)領(lǐng)域提出的。奧卡姆剃刀原則對科學(xué)和哲學(xué)的發(fā)展都極為重要,因為它告訴人們理論應(yīng)該盡量簡潔,理論中一切不影響結(jié)論的多余部分都應(yīng)該被剔除掉。

正如奧卡姆剃刀原則的精髓一樣,它的描述非常簡潔有力:如非必要,勿增實體。

我們也可以稱它為簡單即為美原則。通俗的描述是:用盡量少的步驟完成一件事。或者,如果對于一個事物有兩種解釋,采用最簡單或能被證偽的那種。正是因為奧卡姆剃刀原則,我們才更加相信哥白尼的日心說,更相信牛頓和愛因斯坦。否則,地球是宇宙中心的理論也沒錯,只是其他行星和恒星環(huán)繞地球的軌道公式也太復(fù)雜了,而且也容易被自然現(xiàn)象證偽。

在眾多的介紹軟件設(shè)計方法的書籍和資料中也多次提到過奧卡姆剃刀原則。應(yīng)用到軟件開發(fā)領(lǐng)域,它確實給了我們很大的啟示。設(shè)想一下我們是不是遇到過這樣的場景:

  • 費力地向別人解釋某個模塊為什么那么設(shè)計
  • 為某段代碼加的注釋比代碼都多
  • 為了解決一個問題而引入一個新的模塊

當我們費力說明和解釋某個代碼設(shè)計時,真正的問題并不在于我們解釋的不夠充分,或者聽眾不夠聰明理解不了,而在于代碼設(shè)計本身沒有很好地體現(xiàn)其業(yè)務(wù)語義。實際上過多的解釋和注釋都是多余的,是可以被奧卡姆剃刀砍掉的。

對于為了解決一個問題而引入一個模塊也是在工作中經(jīng)常遇到的問題。有很多原因?qū)е履承┠K變得腐化難以維護,比如最初的設(shè)計沒有很好地貼合業(yè)務(wù)場景;編碼規(guī)范不夠好,后面的修改也沒有遵守規(guī)則;接手者沒有完全理解作者的意圖就著手修改等等。而程序員也經(jīng)常會有的一個想法是:當一個模塊難以維護了,最好的方法是用一個新模塊替換掉它。實際上這種方法并沒有觸及問題的本質(zhì),在沒有找到導(dǎo)致模塊腐化的原因之前,在沒有制定規(guī)范的模塊設(shè)計方案之前,我們都不能保證新模塊不會有舊模塊一樣的問題。所以,想開發(fā)新模塊替換掉舊模塊很大程度上是在逃避對舊模塊問題的思考,新模塊也很有可能淪落到舊模塊一樣的地步。如果回答不了這個矛盾的問題,還是用奧卡姆剃刀把新模塊剔除掉吧,新模塊是多余的,并沒有解決真正的問題。

奧卡姆剃刀原則保證解決問題的方法是簡單有效的,同時也約束我們應(yīng)當思考更根本的問題,不能浮于問題表象采用最省力的方法。

其他的設(shè)計原則概覽

DRY(Dont Repeat Yourself):保證代碼的可復(fù)用性,避免代碼邏輯的重復(fù)

YAGNI(You Aint Gonna Need It):代碼應(yīng)易于擴展,但要避免過度設(shè)計,不要編寫當前用不到的代碼。

KISS(Keep It Simple, Stupid):把事情想復(fù)雜,做簡單

POLA(Principle of Least Astonishment):最小驚奇原則。代碼應(yīng)合乎邏輯和規(guī)范,給閱讀者最少的驚嚇。接口設(shè)計避免標新立異。

常用的幾種架構(gòu)設(shè)計

分層架構(gòu)

分層架構(gòu)是指基于具體的業(yè)務(wù)模型按照功能模塊將代碼進行分層組織。每一層代表了一組相關(guān)功能的集合。具體分為幾層沒有明確的規(guī)則,通??梢苑譃?-4層或者更多。在分層架構(gòu)中,依賴關(guān)系是由上往下,上層依賴于下層,不能反向依賴。越往下的層次越通用,偏向于基礎(chǔ)能力。越往上層次越動態(tài),偏向于業(yè)務(wù)。

圖片

分層架構(gòu)設(shè)計按照依賴規(guī)則的嚴格程度分為嚴格型分層架構(gòu)和松散型分層架構(gòu)。嚴格型分層架構(gòu)要求每一層只能訪問其直接依賴的層,不能訪問其間接依賴的層。松散型分層架構(gòu)允許每一層訪問位于其下方的任意一層。嚴格型分層架構(gòu)使得各個層之間的耦合度降到最低,但是靈活性不足,當上層需要訪問下面間接層的能力時必須從上往下層層穿透。松散型分層架構(gòu)在保證依賴規(guī)則的前提下提供了足夠的靈活性,所以大部分分層架構(gòu)都是松散型的。

分層架構(gòu)設(shè)計簡潔易懂。對抽象事物按照基礎(chǔ)特征進行分類,符合我們的思維習慣,易于理解。分層架構(gòu)設(shè)計保證每一層內(nèi)部有較好的內(nèi)聚性,減少了層與層之間的耦合度,易于基礎(chǔ)能力的沉淀和復(fù)用,也易于控制變更帶來的風險。?

另外一方面,分層架構(gòu)設(shè)計雖然定義了多個層,但是層與層之間的邊界并不是特別清晰。對于新增的模塊有可能難以確定應(yīng)該放在哪一層。或者隨著業(yè)務(wù)邏輯的變化,未來可能需要調(diào)整模塊所屬的層次。分層架構(gòu)中,上層模塊對下層模塊有直接的依賴,下層模塊的實現(xiàn)直接向上層模塊暴露。在修改或者替換下層模塊時需要修改上層模塊,對上層業(yè)務(wù)的影響較大。業(yè)務(wù)實現(xiàn)與基礎(chǔ)能力沒有完全解耦合。

六邊形架構(gòu)

又稱為端口-適配器架構(gòu)。為了解決具體實現(xiàn)依賴于基礎(chǔ)能力的問題,采用依賴倒置設(shè)計方法將工程分為內(nèi)部和外部。內(nèi)部是具體的業(yè)務(wù)邏輯,外部是依賴的基礎(chǔ)能力。內(nèi)部業(yè)務(wù)邏輯不再直接依賴于外部基礎(chǔ)能力,而是都依賴于其抽象定義。使用依賴注入的方式將外部實現(xiàn)傳入內(nèi)部業(yè)務(wù)邏輯中。內(nèi)部和外部使用接口進行交互,內(nèi)部業(yè)務(wù)邏輯訪問基礎(chǔ)能力時直接調(diào)用其抽象接口即可。

圖片

六邊形架構(gòu)解決了業(yè)務(wù)邏輯直接依賴外部模塊的問題,它們都依賴于抽象,不依賴于直接的實現(xiàn)和細節(jié)。它們直接通過定義好的接口進行交互。因為業(yè)務(wù)邏輯和外部模塊沒有直接的依賴關(guān)系,在修改和替換外部模塊時只需要按照接口定義實現(xiàn)功能,不需要改動業(yè)務(wù)邏輯。

洋蔥圈架構(gòu)(整潔架構(gòu))

洋蔥圈架構(gòu)又稱為整潔架構(gòu),結(jié)合了分層架構(gòu)、六邊形架構(gòu)和領(lǐng)域驅(qū)動設(shè)計特點的架構(gòu)設(shè)計方法。洋蔥圈架構(gòu)是對六邊形架構(gòu)的進一步擴展,依賴關(guān)系依然是外部依賴內(nèi)部。參考領(lǐng)域驅(qū)動設(shè)計,將依賴層次劃分為3-4層甚至更多。從內(nèi)向外依次為:領(lǐng)域模型、業(yè)務(wù)邏輯、領(lǐng)域服務(wù)、基礎(chǔ)能力、外部模塊等。

洋蔥圈架構(gòu)具有六邊形架構(gòu)的優(yōu)點,采用依賴倒置的原則使內(nèi)部業(yè)務(wù)模型不再直接依賴于外部基礎(chǔ)能力。外部模塊的變動和替換不影響內(nèi)部業(yè)務(wù)邏輯。采用領(lǐng)域驅(qū)動設(shè)計的方法劃分實體和模型,利于業(yè)務(wù)規(guī)則的抽象和業(yè)務(wù)模型的建立,對未來業(yè)務(wù)迭代的支持較好。洋蔥圈架構(gòu)使業(yè)務(wù)實體、業(yè)務(wù)模型和業(yè)務(wù)實現(xiàn)處在里層,保證了業(yè)務(wù)模型和實現(xiàn)的穩(wěn)定,避免受到外部模塊變動的影響。

例如,使三方SDK或者數(shù)據(jù)庫系統(tǒng)屬于最外層,使用依賴注入的方法將它們的實現(xiàn)傳入內(nèi)部邏輯。當替換三方SDK或者數(shù)據(jù)庫系統(tǒng)時,按照接口定義實現(xiàn)具體細節(jié)即可。不需要對內(nèi)部邏輯進行改動。

領(lǐng)域驅(qū)動設(shè)計方法

領(lǐng)域驅(qū)動設(shè)計簡稱為DDD(Domain-Driven Design)。準確來說它不是一個架構(gòu)設(shè)計方法,而是一種以業(yè)務(wù)分析和劃分來驅(qū)動系統(tǒng)架構(gòu)設(shè)計的軟件開發(fā)方法。它強調(diào)識別業(yè)務(wù)的核心問題域來確定問題邊界,同時將問題域進行分解降低分析的復(fù)雜度。DDD強調(diào)通過關(guān)注業(yè)務(wù)核心提升業(yè)務(wù)價值。

下面是DDD的一些核心概念,我們做一些簡單的介紹。

  • 領(lǐng)域:有確定的范圍和邊界的業(yè)務(wù)問題域。實際上是我們要解決什么業(yè)務(wù)問題的抽象描述。比如提供給用戶當前位置、目的地位置且提供到達信息是高德地圖的問題域。
  • 子域:將大的問題域根據(jù)業(yè)務(wù)規(guī)則的不同拆分成的小問題域。比如高德地圖的問題域太大了,難以解決。我們可以將問題域拆分成定位、POI搜索、路線規(guī)劃等子問題域。
  • 界限上下文:領(lǐng)域之間的抽象邊界。封裝了領(lǐng)域內(nèi)的概念、規(guī)則和模型。
  • 實體:具有唯一標識的、存在生命周期的對象。比如展示給用戶可見的POI氣泡是一個實體,它有狀態(tài)和確定的生命周期。
  • 值對象:沒有唯一標識和生命周期的對象,依附于實體而存在。比如POI信息是值對象,本身沒有狀態(tài),只能依附于POI氣泡這個實體而存在。
  • 聚合:領(lǐng)域內(nèi)一組實體、值對象的集合。封裝了集合與外界的交互

圖片

使用DDD對業(yè)務(wù)問題進行分析和拆解后,可以采用任何一種架構(gòu)設(shè)計方法,無論是分層架構(gòu)、六邊形架構(gòu)或者整潔架構(gòu)等。但是DDD要求架構(gòu)設(shè)計從實際的業(yè)務(wù)場景出發(fā),理解業(yè)務(wù)的核心問題。架構(gòu)需要明確概念、規(guī)則的設(shè)計,并且保證業(yè)務(wù)模型的穩(wěn)定性。使用分層架構(gòu)展現(xiàn)DDD的領(lǐng)域設(shè)計方法,將工程分為4層:基礎(chǔ)設(shè)施層、領(lǐng)域?qū)?、?yīng)用層和用戶接口層。

圖片


我們所用的架構(gòu)方案

鷹巢

我所在的團隊——鷹巢業(yè)務(wù)組負責高德地圖規(guī)劃和導(dǎo)航的業(yè)務(wù)能力實現(xiàn)。它向下對接引擎層,包括定位引擎、導(dǎo)航引擎、渲染引擎等,向上對接前端JS層。除了承接功能龐大、邏輯復(fù)雜的導(dǎo)航業(yè)務(wù)外,鷹巢還負責引擎能力的封裝以及將這些封裝能力向上層JS透出。

在進行代碼目錄劃分之前,鷹巢的功能實現(xiàn)也是按照模塊化進行設(shè)計的,但是模塊之間并沒有明確的依賴關(guān)系。任何代碼都可以互相的引用,這也就導(dǎo)致了工程中各模塊之間有錯綜復(fù)雜的調(diào)用關(guān)系,很難以說清楚某一個模塊應(yīng)該處于哪個位置,應(yīng)該如何被引用。雖然我們一直將工程代碼分為框架層和業(yè)務(wù)層,但是框架層和業(yè)務(wù)層之間的依賴關(guān)系并不明確。業(yè)務(wù)層依賴框架層,框架層也依賴了業(yè)務(wù)層,并不符合分層架構(gòu)的設(shè)計原則,所以鷹巢的工程架構(gòu)不屬于分層架構(gòu)。

在我們?nèi)ツ甑拇a目錄劃分的工作中,我們最終參考領(lǐng)域驅(qū)動設(shè)計的方法對代碼目錄進行了重新的組織和劃分。將工程代碼整體上分為4層:基礎(chǔ)能力、業(yè)務(wù)層、工具層和接入層。以下是整體結(jié)構(gòu)圖:

圖片

適配層與以下的4層不在同一個倉庫,它包含了與前端JS交互的必要能力封裝。按照模塊的劃分規(guī)則,我們可以說,鷹巢的工程架構(gòu)屬于結(jié)合領(lǐng)域驅(qū)動設(shè)計的松散型分層架構(gòu)。它的特點是:

  • 按照領(lǐng)域驅(qū)動設(shè)計對工程代碼進行組織和劃分,在業(yè)務(wù)層按照不同業(yè)務(wù)領(lǐng)域劃分代碼模塊
  • 采用分層架構(gòu)設(shè)計將工程分為多層,上層依賴于下層,下層不能依賴于上層
  • 上層任意模塊都可以調(diào)用下層任意模塊,屬于松散型架構(gòu)。更加靈活

工程技術(shù)中心C++能力層(包括地圖引擎層)

在工程技術(shù)中心的語言能力框架中,從鷹巢、地圖引擎到基礎(chǔ)庫都是C++語言實現(xiàn)。使用統(tǒng)一的流程管控它們的開發(fā)、構(gòu)建、集成。在引擎架構(gòu)升級之前的相當長的一段時間內(nèi),它們都屬于松散型分層架構(gòu),以下是簡化版的結(jié)構(gòu)圖:

圖片

實際上,包括引擎庫在內(nèi)的C++層有幾十上百個代碼倉庫,層次眾多,且從上層到下層的依賴關(guān)系復(fù)雜。如果將所有的依賴關(guān)系繪制出來,將是一個復(fù)雜的網(wǎng)狀。雖然整體架構(gòu)依然遵守了分層架構(gòu)的設(shè)計原則:只能上層依賴下層。但是因為依賴層次和關(guān)系的復(fù)雜,導(dǎo)致下層代碼的改動對上層的影響很大,在構(gòu)建時也經(jīng)常出現(xiàn)庫版本不匹配的沖突。這使得上層業(yè)務(wù)層經(jīng)常處于不穩(wěn)定狀態(tài),不利于上層業(yè)務(wù)的快速迭代。并且下層能力升級也必然需要上層業(yè)務(wù)層做大工作量的適配。

在去年的引擎架構(gòu)升級中,抽離出抽象層,使得各個倉庫都依賴于抽象接口,不再依賴于具體的實現(xiàn)。抽象出來的抽象層包括:InterfaceApp、InterfaceAR、InterfaceARWalk、InterfaceHorus、InterfaceMap、InterfaceVMap、InterfaceTBT、InterfacePosEngine等。比如鷹巢和TBT都依賴于InterfaceTBT抽象層,使用依賴倒置的原則在App初始化時將TBT的實例化對象設(shè)置給鷹巢。鷹巢通過調(diào)用實例化對象的抽象接口訪問TBT的能力。同理,鷹巢和渲染都依賴于InterfaceMap抽象層。這種方式使得上層的業(yè)務(wù)層比較穩(wěn)定,只要保證抽象層接口的穩(wěn)定性,業(yè)務(wù)層基本上就不會受到下層改動的影響。而且,當下層進行能力升級時,只要按照抽象接口定義實現(xiàn)對應(yīng)能力即可,不需要業(yè)務(wù)層做適配。

從這方面來講,在引擎架構(gòu)升級后,引擎具有整潔架構(gòu)的特征。但是并不能完全稱為整潔架構(gòu),因為從更大的視角來看(將基礎(chǔ)庫和Native層包括進去),依然是松散型的分層架構(gòu)。所以,我們可以稱之為具有整潔架構(gòu)特征的松散型分層架構(gòu)。

總結(jié)

對于架構(gòu)設(shè)計的學(xué)習和理解,我認為很難的一點是:即使懂得很多道理還是很難把事情做好。眾多的設(shè)計原則都是在不同業(yè)務(wù)場景下提出的,有些原則之間本身就是矛盾的。無論是架構(gòu)設(shè)計方法還是設(shè)計原則,它們不是金科玉律,更不可能放之四海而皆準。它們的價值在于告訴我們應(yīng)該摒棄什么,應(yīng)該遵守什么。我們不用那些技術(shù)官僚的詞匯,用更接地氣的描述來說,設(shè)計原則也只是要求我們做到簡潔、規(guī)范和易于理解而已。架構(gòu)設(shè)計并不高端,它本身所產(chǎn)生的價值并不明顯,真正能夠產(chǎn)生價值的在于我們當前正在走的路:如何理解我們的業(yè)務(wù)問題。?

參考資料和書籍:

應(yīng)用架構(gòu)之道:分離業(yè)務(wù)邏輯和技術(shù)細節(jié)?:https://www.cnblogs.com/alisystemsoftware/p/13846127.html

The Onion Architecture?:https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/

《架構(gòu)整潔之道》、《領(lǐng)域驅(qū)動設(shè)計-ThoughtWorks洞見》、《代碼精進之路-從碼農(nóng)到工匠》、《UNIX編程藝術(shù)》

附錄

工程:是指以某組設(shè)想的目標為依據(jù),應(yīng)用有關(guān)的科學(xué)知識和技術(shù)手段,通過有組織的一群人將某個(或某些)現(xiàn)有實體(自然的或人造的)轉(zhuǎn)化為具有預(yù)期使用價值的人造產(chǎn)品過程

工程化:是指以提高效率、降低成本、保證質(zhì)量保證為目的從而促進多人合作,實現(xiàn)功能強大,健壯性好的項目的手段和措施

責任編輯:武曉燕 來源: 阿里開發(fā)者
相關(guān)推薦

2022-07-22 10:09:28

架構(gòu)設(shè)計

2012-06-07 10:45:12

軟件架構(gòu)設(shè)計原則

2019-03-20 09:30:31

2012-05-30 09:43:45

業(yè)務(wù)邏輯層

2012-06-07 10:35:40

架構(gòu)設(shè)計業(yè)務(wù)邏輯Java

2023-06-08 09:05:55

2023-12-13 08:31:23

2011-04-08 17:03:19

Java架構(gòu)

2012-06-07 10:25:35

架構(gòu)設(shè)計服務(wù)層軟件設(shè)計

2012-04-16 10:45:17

三層架構(gòu)

2009-07-06 10:36:41

敏捷開發(fā)

2023-07-17 18:39:27

業(yè)務(wù)系統(tǒng)架構(gòu)

2017-02-20 09:02:31

Impala架構(gòu)設(shè)計

2017-07-06 00:27:17

虛擬訂單中心京東數(shù)據(jù)

2024-02-26 00:00:00

Nginx服務(wù)器HTTP

2017-03-13 08:58:46

Spring Clou概覽架構(gòu)

2013-05-27 10:58:28

Tumblr架構(gòu)設(shè)計雅虎收購

2022-11-11 10:48:55

AQS源碼架構(gòu)

2021-07-07 10:00:03

深度學(xué)習系統(tǒng)機構(gòu)

2009-03-18 18:26:32

英特爾Nehalem服務(wù)器
點贊
收藏

51CTO技術(shù)棧公眾號