領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的實(shí)踐 – CQRS & Event Sourcing

1莽龟、前言

領(lǐng)域驅(qū)動(dòng)(Domain – Driven Design)設(shè)計(jì)的理念在于建立一系列既符合軟件所處領(lǐng)域本身又適合軟件分析開(kāi)發(fā)需要的領(lǐng)域模型畔裕。命令查詢(xún)與職責(zé)分離(Command Query Responsibility Segregation)和事件溯源(E央碟、vent Sourcing)是為一種領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的實(shí)踐序无。

本文旨在簡(jiǎn)要介紹CQRS & Event Sourcing械念, 希望能夠給大家在設(shè)計(jì)業(yè)務(wù)系統(tǒng)上提供一種新的思路和選擇蒸健。

2座享、領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)

在開(kāi)始介紹CQRS之前婉商,有必要先了解DDD中的一些基本思想和概念。

各行各業(yè)都有業(yè)務(wù)系統(tǒng)和軟件開(kāi)發(fā)的需求渣叛。比如Fintech公司會(huì)開(kāi)發(fā)貸款業(yè)務(wù)系統(tǒng)丈秩,證券公司會(huì)開(kāi)發(fā)股票行情交易軟件,旅行社會(huì)開(kāi)發(fā)在線旅游網(wǎng)站淳衙。雖然作為程序員蘑秽,我贊成大家都能博學(xué)多才,上曉天文箫攀,下知地理肠牲。但是畢竟術(shù)業(yè)有專(zhuān)攻,做貸款業(yè)務(wù)系統(tǒng)時(shí)靴跛,我們需要請(qǐng)教信貸專(zhuān)家缀雳;研發(fā)股票行情交易系統(tǒng)時(shí),我們會(huì)和交易員一起討論梢睛;實(shí)踐旅游網(wǎng)站時(shí)肥印,興許我們需要請(qǐng)教資深的旅行家……

那么當(dāng)我們和領(lǐng)域?qū)<覈黄穑哒勯熣撝畷r(shí)绝葡,我們?cè)鯓硬拍茏龅接行У臏贤ㄉ罴睿皇请u同鴨講呢?DDD給了我們?nèi)缦乱恍﹩l(fā):

1藏畅、確定的領(lǐng)域模型(Domain Model)

明確的領(lǐng)域模型是一切的基礎(chǔ)敷硅。

一個(gè)良好定義的領(lǐng)域模型一般會(huì)有以下幾個(gè)特點(diǎn):

該模型應(yīng)該包含所有來(lái)自領(lǐng)域?qū)<业闹R(shí)

該模型可以讓開(kāi)發(fā)團(tuán)隊(duì)很清楚的界定領(lǐng)域邊界,并且判斷知識(shí)的上下文一致性

開(kāi)發(fā)者可以將該模型以代碼的形式進(jìn)行表述

該模型可以方便地應(yīng)對(duì)來(lái)自領(lǐng)域的變化

2墓赴、通用的語(yǔ)言(Ubiquitous Language)

對(duì)于領(lǐng)域中的名詞竞膳,概念,所有的開(kāi)發(fā)者和領(lǐng)域?qū)<視?huì)采用同樣的詞匯诫硕,并且有著同樣的理解坦辟。

比如在支付系統(tǒng)中,“渠道”可以是“微信支付”章办,“連連支付”锉走,而不是“工商銀行”, “招商銀行”藕届。如果開(kāi)發(fā)者和領(lǐng)域?qū)<业睦斫獠煌膊洌亲詈笤O(shè)計(jì)出來(lái)的產(chǎn)品勢(shì)必南轅北轍。

3休偶、實(shí)體(Entities)梁厉,值(value), 服務(wù)(Services)

實(shí)體有一個(gè)全局唯一的標(biāo)識(shí),并且在整個(gè)生命周期中不變。

比如在支付系統(tǒng)中词顾,一個(gè)交易(Transaction)就是一個(gè)實(shí)體八秃。每個(gè)交易都有自己獨(dú)有的交易ID。

值沒(méi)有一個(gè)唯一的標(biāo)識(shí)肉盹。比如在支付系統(tǒng)中昔驱。

交易的狀態(tài)可以分為:“創(chuàng)建中”,“處理中”上忍,“成功”骤肛,“失敗”∏侠叮 所以交易的狀態(tài)就是一個(gè)值腋颠。

服務(wù):除了實(shí)體和值之外,對(duì)于描述的動(dòng)作它抱,領(lǐng)域驅(qū)動(dòng)的設(shè)計(jì)認(rèn)為這是一個(gè)服務(wù)秕豫。

比如在支付系統(tǒng)中,與第三方支付綁定銀行卡的行為观蓄,我們就可以認(rèn)為是一種服務(wù)混移。

4、聚合(Aggregate)和聚合根(Aggregate Root)

相對(duì)于實(shí)體侮穿,值和服務(wù)是用來(lái)進(jìn)行領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中的建模模型歌径。聚合和聚合根則是根據(jù)領(lǐng)域的原則分割并且描述實(shí)體之間的組合。

想象一下亲茅,如果一個(gè)系統(tǒng)中有許多用戶(hù)回铛,每個(gè)用戶(hù)都可以修改部分?jǐn)?shù)據(jù)。那么如何保證數(shù)據(jù)的一致性問(wèn)題:

當(dāng)每一個(gè)用戶(hù)修改數(shù)據(jù)時(shí)克锣,將數(shù)據(jù)庫(kù)中所有的表都鎖定茵肃。確實(shí),這樣可以保證強(qiáng)一致性袭祟,但是這肯定不是一個(gè)用戶(hù)體驗(yàn)好的系統(tǒng)验残,并且性能十分糟糕

當(dāng)每一個(gè)用戶(hù)修改數(shù)據(jù)時(shí),只將部分?jǐn)?shù)據(jù)鎖定巾乳。如此在用戶(hù)的可用性和系統(tǒng)的一致性上能取到權(quán)衡

這里的問(wèn)題就在于您没,如何界定B方案中的“部分”, DDD認(rèn)為聚合就是在考量系統(tǒng)一致性后胆绊,相關(guān)的實(shí)體和值組合在一起的最小不可分割的集合氨鹏。而聚合根本質(zhì)仍然是一個(gè)實(shí)體, 在DDD中認(rèn)為聚合根是訪問(wèn)聚合的唯一方式压状。

可能說(shuō)的比較抽象仆抵,仍然以支付系統(tǒng)為例:我們認(rèn)為“訂單”就是一個(gè)聚合。訂單可以包含多個(gè)“交易”, 同時(shí)訂單也是一個(gè)實(shí)體肢础,因?yàn)橛唵翁?hào)是訂單的唯一“標(biāo)識(shí)”还栓,訂單本身也可以作為訂單自身聚合的聚合根碌廓,外界通過(guò)訪問(wèn)訂單才能訪問(wèn)訂單中的交易传轰。

以上便是DDD的一些基本概念,作為開(kāi)發(fā)者而言谷婆,我并不贊同概念的堆砌和教條主義慨蛙,其實(shí)很多時(shí)候,我們已經(jīng)不自覺(jué)的使用了DDD的一些概念潛移默化的指導(dǎo)我們平時(shí)的軟件開(kāi)發(fā)纪挎,比如我們會(huì)在開(kāi)發(fā)的小組內(nèi)期贫,使用約定俗稱(chēng)的名詞,開(kāi)發(fā)者和業(yè)務(wù)員都能明白這些沒(méi)有歧義的名詞(通用語(yǔ)言)异袄, 開(kāi)發(fā)者也會(huì)站在業(yè)務(wù)員的角度思考軟件系統(tǒng)內(nèi)部設(shè)計(jì)分割的原則(聚合/聚合根的設(shè)計(jì))通砍。我相信DDD不是軟件設(shè)計(jì)的條條框框,而是大量軟件設(shè)計(jì)實(shí)踐后烤蜕,對(duì)于良好設(shè)計(jì)范式的一個(gè)總結(jié)和提煉封孙。

3、命令查詢(xún)與職責(zé) & 事件溯源的系統(tǒng)架構(gòu)

首先我們來(lái)看一個(gè)經(jīng)典的基于數(shù)據(jù)驅(qū)動(dòng)(Data Driven)的系統(tǒng)設(shè)計(jì)結(jié)構(gòu):

這是一個(gè)非常經(jīng)典的系統(tǒng)設(shè)計(jì)讽营,數(shù)據(jù)驅(qū)動(dòng)的架構(gòu)有很多現(xiàn)代ORM可以方便地實(shí)現(xiàn)基本的功能虎忌,優(yōu)點(diǎn)不言而喻。我們就來(lái)談?wù)勥@樣的系統(tǒng)的局限性:

1橱鹏、無(wú)法實(shí)踐領(lǐng)域驅(qū)動(dòng)編程

很明顯膜蠢,這樣系統(tǒng)對(duì)領(lǐng)域?qū)ο笞罨镜牟僮骶褪窃鰟h改查(CURD),但是增刪改查是計(jì)算機(jī)世界的術(shù)語(yǔ)莉兰,并不是一個(gè)領(lǐng)域的通用語(yǔ)言挑围。在領(lǐng)域世界中,通用的語(yǔ)言遠(yuǎn)遠(yuǎn)比增刪改查復(fù)雜的多糖荒。

還是以支付系統(tǒng)為例:比如創(chuàng)建“訂單”杉辙,這并不是一個(gè)簡(jiǎn)單的增加操作。因?yàn)椤坝唵巍笨赡馨鄠€(gè)“交易”寂嘉,所以創(chuàng)建“訂單”其實(shí)包括增加一個(gè)“訂單”信息以及增加該訂單下的多個(gè)“交易”信息桌肴,并且最終將訂單和交易增加(序列化)到數(shù)據(jù)存儲(chǔ)中。而查詢(xún)訂單鹊汛,則包括查詢(xún)訂單所屬的“交易”萧芙,最終組合出訂單并且返回。

如果最終的數(shù)據(jù)存儲(chǔ)是一個(gè)關(guān)系型的數(shù)據(jù)庫(kù)寓搬,則創(chuàng)建“訂單”和查詢(xún)“訂單”的操作珍昨,需要開(kāi)發(fā)人員理解訂單和交易的關(guān)系,并且轉(zhuǎn)換訂單/交易模型至一個(gè)關(guān)系型的數(shù)據(jù)庫(kù)。

2镣典、單一的對(duì)象實(shí)體作用于數(shù)據(jù)讀寫(xiě)

仍然以支付系統(tǒng)為例:無(wú)論是增加訂單還是查詢(xún)訂單兔毙,在這樣的系統(tǒng)中,訂單被建模成一個(gè)單一的實(shí)體兄春。無(wú)論是存儲(chǔ)還是讀取澎剥,都會(huì)將整個(gè)訂單對(duì)象序列化到數(shù)據(jù)存儲(chǔ)或者反序列出來(lái)。如果我們只是修改一個(gè)訂單的狀態(tài)赶舆,并且查詢(xún)最新的訂單狀態(tài)哑姚,真的需要將整個(gè)對(duì)象都寫(xiě)入數(shù)據(jù)存儲(chǔ)或者讀取出來(lái)嗎?撇開(kāi)系統(tǒng)性能而言芜茵,安全性也是一個(gè)值得考量的問(wèn)題叙量。

基于對(duì)現(xiàn)實(shí)世界的觀察,任何的方法九串,都可以拆解為兩類(lèi):命令和查詢(xún)绞佩。查詢(xún)負(fù)責(zé)返回?cái)?shù)據(jù),并且不改變數(shù)據(jù)的狀態(tài)猪钮。命令負(fù)責(zé)改變數(shù)據(jù)的狀態(tài)品山,產(chǎn)生事件,但是不返回任何數(shù)據(jù)躬贡。任何復(fù)雜的方法(DDD中的服務(wù))谆奥,都可以是命令和查詢(xún)的組合。

由此拂玻,我們來(lái)看一下基于CQRS & Event Sourcing的系統(tǒng)設(shè)計(jì):

在CQRS的架構(gòu)設(shè)計(jì)中酸些,客戶(hù)端可以發(fā)送命令,或者要求查詢(xún)檐蚜。對(duì)于命令而言魄懂,由命令總線負(fù)責(zé)分發(fā)給相應(yīng)的命令處理器。命令處理器通過(guò)事件溯源加載得到相應(yīng)的聚合根闯第,修改聚合市栗,并且產(chǎn)生相應(yīng)的事件。事件首先會(huì)被存儲(chǔ)咳短,繼而被事件總線分發(fā)給事件處理器填帽。由事件處理器根據(jù)相應(yīng)的事件將領(lǐng)域模型轉(zhuǎn)換成寫(xiě)數(shù)據(jù)庫(kù)中的存儲(chǔ)表現(xiàn)形式。

寫(xiě)數(shù)據(jù)庫(kù)可以以一種可靠的方式咙好,將數(shù)據(jù)同步到讀數(shù)據(jù)庫(kù)篡腌,對(duì)于接受最終一致性的系統(tǒng)而言,這是可以接受的同步方式勾效。

而另一方面嘹悼,對(duì)應(yīng)查詢(xún)的需求叛甫,可以由簡(jiǎn)單的查詢(xún)處理器接受查詢(xún)請(qǐng)求,將寫(xiě)數(shù)據(jù)庫(kù)中的數(shù)據(jù)轉(zhuǎn)換成查詢(xún)需要的形式予以返回杨伙。

事件溯源是一種通過(guò)采集所有的歷史事件還原一個(gè)聚合狀態(tài)的方法其监。

以一個(gè)支付系統(tǒng)的訂單(聚合)為例。訂單的生命周期可以是創(chuàng)建 -> 待計(jì)劃 -> 執(zhí)行中 -> 完成限匣。那么對(duì)應(yīng)的事件可以是訂單創(chuàng)建事件抖苦,訂單計(jì)劃執(zhí)行事件,訂單執(zhí)行事件膛腐,訂單完成事件睛约。

對(duì)于普通數(shù)據(jù)驅(qū)動(dòng)的設(shè)計(jì)而言,訂單的信息存儲(chǔ)可能是這樣的:(關(guān)系型數(shù)據(jù)庫(kù))

而對(duì)于支持事件溯源的系統(tǒng)而言哲身,訂單的存儲(chǔ)可以是這樣的:

{

“aggregateId” : “201609011005”

“eventPayload “: “Created”

“timeStamp” : “2016/09/01 10:50:01 ”

}

{

“aggregateId” : “201609011005”

“eventPayload “: “Scheduled”

“timeStamp” : “2016/09/01 10:51:11 ”

}

{

“aggregateId” : “201609011005”

“eventPayload “: “Executing”

“timeStamp” : “2016/09/01 18:02:59 ”

}

事件溯源將這三個(gè)事件依次加載處理,便可以還原出訂單的現(xiàn)在狀態(tài)贸伐。在聚合事件數(shù)量大的情況下勘天,采用事件快照(Event Snapshot)可以有效提高事件溯源的效率和速度。

經(jīng)過(guò)以上分析捉邢,在CQRS & Event Sourcing的設(shè)計(jì)中脯丝,我們可以看到以下優(yōu)點(diǎn):

讀寫(xiě)分離:不同于數(shù)據(jù)驅(qū)動(dòng)的設(shè)計(jì),讀寫(xiě)使用的同一個(gè)流程伏伐,甚至是同一個(gè)模型宠进。在CQRS中,領(lǐng)域模型根據(jù)事件序列化至數(shù)據(jù)庫(kù)藐翎。而查詢(xún)模塊則完全可以定義需要查詢(xún)領(lǐng)域模型材蹬。讀寫(xiě)是完全隔離的。如果使用數(shù)據(jù)庫(kù)同步的方式吝镣,讀寫(xiě)甚至可以使用不同的數(shù)據(jù)庫(kù)(取決于系統(tǒng)對(duì)一致性的需求)堤器。所以在這里,我們可以提高系統(tǒng)的吞吐量和性能末贾。并且可以分別對(duì)寫(xiě)數(shù)據(jù)庫(kù)和讀數(shù)據(jù)庫(kù)做出針對(duì)性的優(yōu)化闸溃。

符合領(lǐng)域設(shè)計(jì)的原則:無(wú)論是命令還是事件,都是基于對(duì)現(xiàn)實(shí)世界的觀察拱撵。不同于增刪改辉川,整個(gè)系統(tǒng)是由命令和事件驅(qū)動(dòng),由命令對(duì)相應(yīng)的聚合(實(shí)體)進(jìn)行修改拴测。而修改則產(chǎn)生了相應(yīng)的事件乓旗,事件可以再產(chǎn)生命令,如此往復(fù)昼扛。

我們的世界此刻不正是由無(wú)數(shù)個(gè)事件疊加產(chǎn)生的結(jié)果嗎寸齐?

整個(gè)系統(tǒng)的所有事件都有歷史記錄:對(duì)于任何聚合的生命周期中欲诺,如何被創(chuàng)建,修改直至回收的過(guò)程渺鹦,都可以通過(guò)一個(gè)又一個(gè)事件被回溯扰法,分析。我們不僅僅關(guān)心聚合最終狀態(tài)毅厚,對(duì)中間記錄的分析同樣也有價(jià)值塞颁。

同樣,CQRS & Event Sourcing 也有自身的局限性:

系統(tǒng)結(jié)構(gòu)相對(duì)于經(jīng)典的設(shè)計(jì)而言復(fù)雜吸耿。需要設(shè)計(jì)命令總線祠锣,命令分發(fā)器,事件總線咽安,事件分發(fā)器伴网。需要設(shè)計(jì)良好的事件存儲(chǔ)機(jī)制,以及事件溯源機(jī)制

對(duì)于簡(jiǎn)單妆棒,靜態(tài)的系統(tǒng)澡腾,或者是沒(méi)有復(fù)雜協(xié)作上下文(Bounded Context)的領(lǐng)域模型的系統(tǒng),引入CQRS并不會(huì)得到很多益處糕珊,相反會(huì)使得系統(tǒng)臃腫动分,龐大

因?yàn)橐肓瞬糠諨DD領(lǐng)域設(shè)計(jì)的概念,對(duì)于開(kāi)發(fā)人員也有一定的學(xué)習(xí)曲線

4红选、總結(jié)

CQRS & ES 給我們提供了一種有別于傳統(tǒng)經(jīng)典體系的設(shè)計(jì)思路澜公,在業(yè)務(wù)系統(tǒng)中分析哪里需要使用CQRS & ES 需要我們權(quán)衡實(shí)施這種新體系架構(gòu)所需的代價(jià)和長(zhǎng)期的回報(bào)。此文簡(jiǎn)要介紹了CQRS & ES 在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)內(nèi)的實(shí)踐喇肋,希望能拋磚引玉坟乾,與諸君共勉。

本文作者:王磊(點(diǎn)融黑幫)苟蹈,點(diǎn)融網(wǎng)高級(jí)軟件工程師糊渊,目前就職于Fincore部門(mén)。曾在投行工作四年慧脱,專(zhuān)注于支付業(yè)務(wù)渺绒。愛(ài)旅行,愛(ài)網(wǎng)球菱鸥,愛(ài)乒乓宗兼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市氮采,隨后出現(xiàn)的幾起案子殷绍,更是在濱河造成了極大的恐慌,老刑警劉巖鹊漠,帶你破解...
    沈念sama閱讀 221,331評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件主到,死亡現(xiàn)場(chǎng)離奇詭異茶行,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)登钥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)畔师,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人牧牢,你說(shuō)我怎么就攤上這事看锉。” “怎么了塔鳍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,755評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵伯铣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我轮纫,道長(zhǎng)腔寡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,528評(píng)論 1 296
  • 正文 為了忘掉前任蜡感,我火速辦了婚禮蹬蚁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘郑兴。我一直安慰自己,他們只是感情好贝乎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布情连。 她就那樣靜靜地躺著,像睡著了一般览效。 火紅的嫁衣襯著肌膚如雪却舀。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,166評(píng)論 1 308
  • 那天锤灿,我揣著相機(jī)與錄音挽拔,去河邊找鬼。 笑死但校,一個(gè)胖子當(dāng)著我的面吹牛螃诅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播状囱,決...
    沈念sama閱讀 40,768評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼术裸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了亭枷?” 一聲冷哼從身側(cè)響起袭艺,我...
    開(kāi)封第一講書(shū)人閱讀 39,664評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叨粘,沒(méi)想到半個(gè)月后猾编,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瘤睹,經(jīng)...
    沈念sama閱讀 46,205評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評(píng)論 3 340
  • 正文 我和宋清朗相戀三年答倡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轰传。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,435評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡苇羡,死狀恐怖绸吸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情设江,我是刑警寧澤锦茁,帶...
    沈念sama閱讀 36,126評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站叉存,受9級(jí)特大地震影響码俩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜歼捏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評(píng)論 3 333
  • 文/蒙蒙 一稿存、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瞳秽,春花似錦瓣履、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,276評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至腺晾,卻和暖如春燕锥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悯蝉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工归形, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鼻由。 一個(gè)月前我還...
    沈念sama閱讀 48,818評(píng)論 3 376
  • 正文 我出身青樓暇榴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嗡靡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子跺撼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容