領域驅動設計和實踐

軟件系統(tǒng)面向對象的設計思想可謂歷史悠久英融,20世紀70年代的Smalltalk可以說是面向對象語言的經典丙者,直到今天我們依然將這門語言視為面向對象語言的基礎翠订。隨著編程語言和技術的發(fā)展峰伙,各種語言特性層出不窮浸卦,面向對象是大部分語言的一個基本特性缓醋,像C++剔交、Java、C#這樣的靜態(tài)語言改衩,Ruby岖常、 Python這樣的動態(tài)語言都是面向對象的語言。
但是面向對象語言并不是銀彈葫督,如果開發(fā)人員認為使用面向對象語言寫出來的程度本身就是面向對象的竭鞍,那就大錯特錯了板惑,實際開發(fā)中,大量的業(yè)務邏輯堆積 在一個巨型類中的例子屢見不鮮偎快,代碼的復用性和擴展性無法得到保證冯乘。為了解決這樣的問題,領域驅動設計提出了清晰的分層架構和領域對象的概念晒夹,讓面向對象 的分析和設計進入了一個新的階段裆馒,對企業(yè)級軟件開發(fā)起到了巨大的推動作用。
本文主要介紹了領域驅動設計的基本概念丐怯、要素喷好、特點,對比了事務腳本和領域模型的特點读跷,最后介紹了我們在軟件開發(fā)過程中的領域驅動設計實踐梗搅。

什么是領域驅動設計(DDD)

2004年著名建模專家Eric Evans發(fā)表了他最具影響力的書籍:《Domain-Driven Design –Tackling Complexity in the Heart of Software》(中文譯名:領域驅動設計—軟件核心復雜性應對之道),書中提出了“領域驅動設計(簡稱 DDD)”的概念效览。

領域驅動設計事實上是針對OOAD的一個擴展和延伸无切,DDD基于面向對象分析與設計技術,對技術架構進行了分層規(guī)劃丐枉,同時對每個類進行了策略和類型的劃分哆键。

領域模型是領域驅動的核心。采用DDD的設計思想瘦锹,業(yè)務邏輯不再集中在幾個大型的類上籍嘹,而是由大量相對小的領域對象(類)組成,這些類具備自己的狀態(tài)和行為沼本,每個類是相對完整的獨立體噩峦,并與現(xiàn)實領域的業(yè)務對象映射锭沟。領域模型就是由這樣許多的細粒度的類組成抽兆。基于領域驅動的設計族淮,保證了系統(tǒng)的可維護 性辫红、擴展性和復用性,在處理復雜業(yè)務邏輯方面有著先天的優(yōu)勢祝辣。

領域驅動設計的特點

領域驅動的核心應用場景就是解決復雜業(yè)務的設計問題贴妻,其特點與這一核心主題息息相關:

  • 分層架構與職責劃分:領域驅動設計很好的遵循了關注點分離的原則,提出了成熟蝙斜、清晰的分層架構名惩。同時對領域對象進行了明確的策略和職責劃分,讓領域對象和現(xiàn)實世界中的業(yè)務形成良好的映射關系孕荠,為領域專家與開發(fā)人員搭建了溝通的橋梁娩鹉。
  • 復用:在領域驅動設計中攻谁,領域對象是核心,每個領域對象都是一個相對完整的內聚的業(yè)務對象描述弯予,所以可以形成直接的復用戚宦。同時設計過程是基于領域對象而不是基于數(shù)據庫的Schema,所以整個設計也是可以復用的锈嫩。
  • 使用場景:適合具備復雜業(yè)務邏輯的軟件系統(tǒng)受楼,對軟件的可維護性和擴展性要求比較高。不適用簡單的增刪改查業(yè)務呼寸。

如果不使用DDD艳汽?

面對復雜的業(yè)務場景和需求,如果沒有建立和實現(xiàn)領域模型等舔,會導致應用架構出現(xiàn)“胖服務層”和“貧血的領域模型”骚灸,在這樣的架構中,Service層 開始積聚越來越多的業(yè)務邏輯慌植,領域對象則成為只有getter和setter方法的數(shù)據載體甚牲。這種做法還會導致領域特定業(yè)務邏輯和規(guī)則散布于多個的 Service類中,有些情況下還會出現(xiàn)重復的邏輯蝶柿。我們曾經見過5000多行的Service類丈钙,上百個方法,代碼基本上是不可讀的交汤。

在大多數(shù)情況下雏赦,貧血的領域模型沒有成本效益。它們不會給公司帶來超越其它公司的競爭優(yōu)勢芙扎,因為在這種架構里要實現(xiàn)業(yè)務需求變更星岗,開發(fā)并部署到生產環(huán)境中去要花費太長的時間。

領域驅動設計的分層架構和構成要素

下面我們簡單介紹一下領域驅動設計的分層架構和構成要素戒洼,這部分內容在Eric Evans的書中有非常詳盡的描述俏橘,想要詳細了解的,最好去讀原版書籍圈浇。

下面這張圖是該書中著名的分層架構圖寥掐,如下:


整個架構分為四層,其核心就是領域層(Domain)磷蜀,所有的業(yè)務邏輯應該在領域層實現(xiàn)召耘,具體描述如下:

分層 具體描述
用戶界面/展現(xiàn)層 負責向用戶展現(xiàn)信息以及解釋用戶命令。
應用層 ? 很薄的一層,用來協(xié)調應用的活動褐隆。它不包含業(yè)務邏輯污它。它不保留業(yè)務對象的狀態(tài),但它保有應用任務的進度狀態(tài)。
領域層 本層包含關于領域的信息。這是業(yè)務軟件的核心所在衫贬。在這里保留業(yè)務對象的狀態(tài),對業(yè)務對象和它們狀態(tài)的持久化被委托給了基礎設施層蜜宪。
基礎設施層 本層作為其他層的支撐庫存在。它提供了層間的通信,實現(xiàn)對業(yè)務對象的持久化,包含對用戶界面層的支撐庫等作用祥山。

領域驅動設計除了對系統(tǒng)架構進行了分層描述圃验,還對對象(Object)做了明確的職責和策略劃分:

  • 實體(Entities):具備唯一ID,能夠被持久化缝呕,具備業(yè)務邏輯澳窑,對應現(xiàn)實世界業(yè)務對象。
  • 值對象(Value objects):不具有唯一ID供常,由對象的屬性描述摊聋,一般為內存中的臨時對象,可以用來傳遞參數(shù)或對實體進行補充描述栈暇。
  • 工廠(Factories):主要用來創(chuàng)建實體麻裁,目前架構實踐中一般采用IOC容器來實現(xiàn)工廠的功能。
  • 倉庫(Repositories):用來管理實體的集合源祈,封裝持久化框架煎源。
  • 服務(Services):為上層建筑提供可操作的接口,負責對領域對象進行調度和封裝香缺,同時可以對外提供各種形式的服務手销。

當然,DDD中還提出了聚合和聚合根(Aggregate Root)的概念图张,不過我們在實踐過程發(fā)現(xiàn)聚合根有問題復雜化的傾向锋拖,用傳統(tǒng)的聚合、組合等概念去描述領域對象之間的關系更容易理解祸轮,所以這里對這個概念就不做介紹了兽埃。

事務腳本和領域模型

Martin Fowler 2004年所著的企業(yè)應用架構模式(Patterns of Enterprise Application Architecture)中的第九章領域邏輯模式(Domain Logic Patterns)專門介紹了事務腳本(Transaction Script)和領域模型(Domain Model),理解這兩種模式對設計和構建企業(yè)應用軟件非常有幫助适袜,所以有必要介紹一下柄错。

事務腳本:

事務腳本的核心是過程,通過過程的調用來組織業(yè)務邏輯痪蝇,每個過程處理來自表現(xiàn)層的單個請求鄙陡。大部分業(yè)務應用都可以被看成一系列事務冕房,從某種程度上來 說躏啰,通過事務腳本處理業(yè)務,就像執(zhí)行一條條Sql語句來實現(xiàn)數(shù)據庫信息的處理耙册。事務腳本把業(yè)務邏輯組織成單個過程给僵,在過程中直接調用數(shù)據庫,業(yè)務邏輯在服 務(Service)層處理。

事務腳本模式可以簡單的通過UML圖表示成這樣:


由Action層處理UI層的動作請求帝际,將Request中的數(shù)據組裝后傳遞給BusinessService蔓同,BS層做簡單的邏輯處理后,調用數(shù) 據訪問對象進行數(shù)據持久化蹲诀,其中VO充當了數(shù)據傳輸對象的作用斑粱,一般是貧血的POJO,只具備getter和setter方法脯爪,沒有狀態(tài)和行為则北。

事務腳本模式的特點是簡單容易理解,面向過程設計痕慢。對于少量邏輯的業(yè)務應用來說尚揣,事務腳本模式簡單自然,性能良好掖举,容易理解快骗,而且一個事務的處理不 會影響其他事務。不過缺點也很明顯塔次,對于復雜的業(yè)務邏輯處理力不從心方篮,難以保持良好的設計,事務之間的冗余代碼不斷增多励负,通過復制粘貼方式進行復用恭取。可維 護性和擴展性變差熄守。

領域模型:

領域模型的特點也比較明顯蜈垮, 屬于面向對象設計,領域模型具備自己的屬性行為狀態(tài)裕照,并與現(xiàn)實世界的業(yè)務對象相映射攒发。各類具備明確的職責劃分,領域對象元素之間通過聚合和引用等關系配合 解決實際業(yè)務應用和規(guī)則晋南』菰常可復用,可維護负间,易擴展偶妖,可以采用合適的設計模型進行詳細設計。缺點是相對復雜政溃,要求設計人員有良好的抽象能力趾访。

領域模型對應的就是領域驅動設計中劃分的領域層,這里就不詳細討論了董虱。

在實際的設計中扼鞋,我們需要根據具體的需求選擇相應的設計模式申鱼。具備復雜業(yè)務邏輯的核心業(yè)務系統(tǒng)適合使用領域模型,簡單的信息管理系統(tǒng)可以考慮采用事務腳本模式云头。

領域驅動設計實踐

下面主要講一下我們在構建企業(yè)級應用開發(fā)平臺中對DDD的實踐和擴展捐友。

本人近年來一直在從事企業(yè)級應用開發(fā)平臺的相關工作,GAP平臺是我們的一個軟件產品溃槐,用來解決企業(yè)級軟件開發(fā)過程中復用匣砖、快速開發(fā)和過程規(guī)范等問 題。設計這樣一個平臺昏滴,從底層的框架上就應該能夠支撐復雜業(yè)務邏輯的系統(tǒng)構建脆粥,所以我們在大的架構設計思路上采用了領域驅動設計的思路,并根據實際采用的技術和要實現(xiàn)的功能對DDD的四層架構進行了細化和實現(xiàn):

整個平臺采用了JavaEE的技術及其相關的開源框架影涉。系統(tǒng)的核心業(yè)務邏輯由Domain層處理变隔,其中的業(yè)務服務(BusinessService)負責處理某個相對內聚的業(yè)務邏輯單元,同時對內對外提供本地或遠程的服務蟹倾。


下面是對各層的簡要描述:

  • View:展示層匣缘,由于GAP平臺主要面向B/S架構,展示層主要由web資源文件組成鲜棠,包括JSP肌厨,JS和大量的界面控件,同時還采用了AJAX和Flex等RIA技術豁陆,負責向用戶展現(xiàn)豐富的界面信息柑爸,并執(zhí)行用戶的命令。
  • Control:控制層盒音,負責展示層請求的轉發(fā)表鳍、調度和基礎驗證,同時自動攔截后臺返回的Runtime異常信息祥诽,如果控制層需要與第三方系統(tǒng)交互譬圣,可以通過Action做遠程的請求。
  • Domain:領域層雄坪,是系統(tǒng)最為豐富的一層厘熟,主要負責處理整個系統(tǒng)的業(yè)務邏輯。這一層包括業(yè)務服務和領域對象维哈,同時負責系統(tǒng)的事務管理绳姨。其中業(yè)務服務可以提供本地調用和共享遠程服務的功能。
  • Persistence:持久化層阔挠,主要負責數(shù)據持久化飘庄,支持O/R Mapping和JDBC。對數(shù)據源的訪問提供多種方式谒亦。

另外,我們引入了Spring的IOC容器竭宰,系統(tǒng)的控制層、領域層和持久化層元素都有IOC容器統(tǒng)一管理份招,實現(xiàn)完全的接口分離和解耦切揭。同時在控制、領域和持久化層都可以引用日志服務锁摔。

我們對領域驅動要素的定義上和原有的命名和含義上稍有區(qū)別廓旬。
原來的服務(Service),我們定義為業(yè)務服務(BusinessService)谐腰,面向業(yè)務服務的架構是GAP平臺的核心設計思想孕豹,一個業(yè)務服務可以由一個或多個領域模型和數(shù)據訪問對象(DAO)組成,去實現(xiàn)一個完整的業(yè)務邏輯單元十气。業(yè)務服務主要負責事務處理和維護各個領域對象之間的關系励背,同 時為上層訪問提供本地和遠程服務,服務類型包括Web Service砸西,RMI等叶眉。
領域對象由實體(Entity)和值對象(VO)構成,實體類具備自己的屬性和行為芹枷、狀態(tài)衅疙,可以聚合VO,實體類之間可以有聚合關聯(lián)等關系鸳慈,可以由數(shù)據訪問對象(DAO)進行持久化饱溢。
持久化由數(shù)據訪問對象(DAO)實現(xiàn),不處理業(yè)務邏輯走芋,主要負責實體類的持久化绩郎。提供多種持久化方式(O/R Mapping和JDBC)。

那么如何在去實現(xiàn)領域驅動設計呢翁逞?我們總結了以下四個步驟:

  • 確定業(yè)務服務(Business Service):根據業(yè)務需求和功能模塊劃分嗽上,確定業(yè)務單元,每個Business Service是一個內聚的業(yè)務單元熄攘,覆蓋相關的領域對象兽愤。
  • 定義領域對象(Entity, VO):根據業(yè)務單元的業(yè)務邏輯定義領域對象挪圾,通過UML方法和設計模式描述領域對象浅萧。
  • 定義領域對象的屬性和關聯(lián)關系:確定領域對象的各種屬性和各個領域對象之間的關聯(lián)關系。
  • 為領域對象增加行為:根據業(yè)務需求(系統(tǒng)用例和界面原型等)為領域對象增加行為哲思,并定義哪些方法要被業(yè)務服務引用洼畅。

案例——網上書店

為了更好的理解領域驅動設計,我們基于以上設計方法棚赔,實現(xiàn)了一套簡單的網上書店系統(tǒng)帝簇。
網上書店系統(tǒng)是采用DDD設計思想構建的一個應用系統(tǒng)示例徘郭。通過網上書店系統(tǒng),可以快速理解領域驅動設計丧肴。該系統(tǒng)實現(xiàn)網上書店的常用功能:包括瀏覽書籍残揉、挑選書籍、提交訂單芋浮、查看訂單抱环、自動折扣、處理訂單纸巷、取消訂單等镇草。未登錄用戶可以瀏覽和挑選書籍;已登錄用戶可以提交和查看自己相關的訂單瘤旨;管理員 可以處理訂單梯啤。

經過業(yè)務抽象,即使是這樣一個簡單的業(yè)務場景也包含了很多領域對象存哲,例如訂單条辟、賬戶、書籍宏胯、購物車羽嫡、購物項、折扣等肩袍,通過分析和設計杭棵,我們可以得到這樣的設計圖(為了查看方便,圖中的類隱藏了屬性信息):

BookStoreAction負責處理展現(xiàn)層的請求氛赐,并把請求轉發(fā)給業(yè)務服務IBookStoreBS魂爪,業(yè)務服務負責調度上圖中顯示的領域對象,處理該場景的所有業(yè)務艰管。
其中領域對象和現(xiàn)實業(yè)務的對應關系為:

  • Account——賬戶
  • Order——訂單
  • Book——書籍
  • Cart——購物車
  • Item——訂單項
  • Discount——折扣

與事務腳本的編程模式不同滓侍,領域驅動設計不是把業(yè)務邏輯放在BS(BusinessService)中,而是由具備屬性牲芋、行為和狀態(tài)的領域對象處理撩笆。例如Order類,如果是貧血的POJO缸浦,那它內部只有與數(shù)據表字段對應的屬性以及getter和setter方法夕冲,而在領域驅動設計中,則是一個相對獨立的裂逐、能夠處理自身關聯(lián)業(yè)務的領域對象歹鱼。在本系統(tǒng)中,我們對Order的描述如下:

訂單的實現(xiàn)類是gap.template.bookstore.model.Order卜高,類中除了聯(lián)系方式弥姻、郵寄地址等基本屬性外南片,還有以下領域相關的行為:

  • init(...),結算時調用方法庭敦,根據當前用戶與購物車中的Items初始化訂單疼进,供用戶修改。
  • submit(...)螺捐,提交訂單時調用的方法颠悬,保存訂單矮燎。
  • cancel(...)定血,取消訂單,把訂單和相關item的狀態(tài)設置為“已取消”诞外,然后委托Dao進行持久化澜沟。
  • dispose(...),處理訂單峡谊,首先更新訂單項的狀態(tài)茫虽,然后委托Dao持久化訂單數(shù)據。
  • reSubmit既们、setItemsStatus......

通過以上的描述濒析,我們可以看到,Order類基本上覆蓋了現(xiàn)實世界中訂單這個業(yè)務的所有行為和狀態(tài)啥纸,是相對內聚的号杏,這樣的特性使其復用性大大增加, 即使未來開發(fā)新的模塊斯棒,涉及到訂單業(yè)務的盾致,可以直接復用Order類。同時在后期維護中荣暮,如果我想了解訂單的業(yè)務庭惜,直接讀Order的代碼就可以了。

從上圖中我們還可以清晰的看到各個領域對象之間的關系穗酥。Order和Cart都聚合了Item护赊,對應都是1...n,Item聚合了Book砾跃,對應關系1...1百揭。Order分別與折扣、賬戶發(fā)生關聯(lián)和調用等等蜓席,整個網上書店的場景就這樣描述出來了器一。

另外,不要忘了BS厨内,除了起到基礎設施的作用外(事務管理和服務共享)祈秕,它還要負責調度和維護領域對象之間的關系渺贤。因為總會有些業(yè)務邏輯,既不屬于這個領域對象请毛,也不屬于那個志鞍,那這部分業(yè)務由誰來處理呢?由BS來處理方仿。例如在管理員處理訂單這個場景中固棚,首先需要根據訂單信息獲取賬戶,根據賬戶信息確 定折扣率仙蚜,同時進行余額校驗此洲,如果校驗通過,就會調用訂單對象的dispose方法處理訂單委粉,這個場景會涉及到Order呜师、Account、 Discount等對象贾节,這樣的業(yè)務邏輯汁汗,應該由BS實現(xiàn)。

IBookStoreDao是數(shù)據訪問對象栗涂,可以被BS調用知牌,用來持久化對象,也可以被領域對象引用斤程,用來持久化自身角寸。

通過以上的描述,我們可以看到暖释,整個設計和實現(xiàn)是優(yōu)雅袭厂、清晰的。業(yè)務邏輯沒有堆積在BS中球匕,而是分散在BS和各個領域對象中纹磺,服務和對象都與現(xiàn)實世界的業(yè)務息息相關,無論是對領域專家亮曹、開發(fā)人員和后期維護人員橄杨,都能這種方式中獲得自己需要的內容。

總結

我們采用領域驅動設計相對比較早照卦,就我個人的檢驗和實踐而言式矫,DDD對構建企業(yè)級應用開發(fā)平臺和大型核心業(yè)務系統(tǒng)的作用是非常明顯的,無論是在產品的穩(wěn)定性役耕、擴展性采转、可維護性、生命周期等方面都有顯著的提升。

但是故慈,由于這樣那樣的原因(復雜度板熊、工期、開發(fā)人員能力限制等等)察绷,很多人會不自覺的抵制采用DDD干签,有時候一個軟件項目重寫了兩次,第二次依然不 去做良好的設計拆撼。事實上采用了DDD的設計方法容劳,我們的設計階段已經變得非常輕量級和敏捷了,開發(fā)人員只要能夠把領域模型之間的關系畫出來并描述說明闸度,并與需求人員達成一致竭贩,那么做出來的東西基本上是靠譜的。

在技術領域筋岛,只有主動的嘗試和提升娶视,效果才是最明顯的晒哄。很多人問過我睁宰,如何開始學習和實踐XXX,其實很簡單寝凌,現(xiàn)在就開始吧柒傻!

參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末伐债,一起剝皮案震驚了整個濱河市预侯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌峰锁,老刑警劉巖萎馅,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異虹蒋,居然都是意外死亡糜芳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門魄衅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峭竣,“玉大人,你說我怎么就攤上這事晃虫〗粤茫” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵哲银,是天一觀的道長扛吞。 經常有香客問我沮榜,道長,這世上最難降的妖魔是什么喻粹? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任蟆融,我火速辦了婚禮,結果婚禮上守呜,老公的妹妹穿的比我還像新娘型酥。我一直安慰自己,他們只是感情好查乒,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布弥喉。 她就那樣靜靜地躺著,像睡著了一般玛迄。 火紅的嫁衣襯著肌膚如雪由境。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天蓖议,我揣著相機與錄音虏杰,去河邊找鬼。 笑死勒虾,一個胖子當著我的面吹牛纺阔,可吹牛的內容都是我干的。 我是一名探鬼主播修然,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼笛钝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了愕宋?” 一聲冷哼從身側響起玻靡,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎中贝,沒想到半個月后囤捻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡雄妥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年最蕾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片老厌。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘟则,死狀恐怖,靈堂內的尸體忽然破棺而出枝秤,到底是詐尸還是另有隱情醋拧,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站丹壕,受9級特大地震影響庆械,放射性物質發(fā)生泄漏。R本人自食惡果不足惜菌赖,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一缭乘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琉用,春花似錦堕绩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晶丘,卻和暖如春黍氮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浅浮。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工沫浆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脑题。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓件缸,卻偏偏與公主長得像铜靶,于是被迫代替她去往敵國和親喷户。 傳聞我的和親對象是個殘疾皇子雷蹂,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容