LLM 輔助畫領(lǐng)域模型圖

什么是領(lǐng)域模型

在領(lǐng)域驅(qū)動設(shè)計(Domain-Driven Design,DDD)中簿晓,領(lǐng)域?qū)ο蠓譃閷嶓w(Entity)和值對象(Value Object)。實體指的是能夠通過唯一標識符標識出來的對象谍倦,有生命周期管理越妈,而值對象僅僅表示一個值。實體的屬性是可以變的边败,只要標識符不變袱衷,它就還是那個實體捎废。但值對象的屬性卻不能變笑窜,一旦變了,它就不再是那個對象登疗,所以排截,我們會把值對象設(shè)置成一個不變的對象。在 DDD 中我們?yōu)槭裁匆獙㈩I(lǐng)域?qū)ο蠓譃閷嶓w和值對象辐益?其實主要是為了分出值對象断傲,也就是把變的對象和不變的對象區(qū)分開。

對于領(lǐng)域?qū)ο笾钦纳芷诠芾戆ǎ?/p>

  • 使用工廠(Factory)模式來創(chuàng)建和銷毀領(lǐng)域?qū)ο螅?/li>
  • 使用聚合(Aggregate)模式來封裝領(lǐng)域?qū)ο螅?/li>
  • 使用倉儲(Repository)來查找和持久化領(lǐng)域?qū)ο蟆?/li>

工廠和倉儲理解起來一點都不難认罩,我們重點看一下聚合。

聚合就是多個實體或值對象的組合续捂,它們共同構(gòu)成了一個業(yè)務(wù)邊界垦垂。聚合里可以包含很多個對象宦搬,每個對象里還可以繼續(xù)包含其它的對象,就像一棵大樹一層層展開劫拗。但重點是间校,這是一棵樹,所以页慷,它只能有一個樹根憔足,這個根就是聚合根(Aggregate Root)。聚合根必須是一個實體酒繁,是從外部訪問這個聚合的起點滓彰。可見州袒,最簡單的聚合僅包含一個實體找蜜。

有了聚合模式后,我們所說的領(lǐng)域?qū)ο蟠蠖鄶?shù)情況下特指的是聚合稳析,也有時指的是聚合內(nèi)部的實體或值對象洗做,這個可以通過所在的上下文來判斷。

領(lǐng)域模型是關(guān)于統(tǒng)一語言的軟件模型彰居,存在于限界上下文(Bounded Context诚纸,BC)這個顯式的邊界之內(nèi),是 DDD 戰(zhàn)術(shù)設(shè)計的目標陈惰。領(lǐng)域模型通過領(lǐng)域建模得到畦徘。領(lǐng)域建模簡單來說就是識別領(lǐng)域?qū)ο螅I(lǐng)域?qū)ο笾g的關(guān)系抬闯,以及領(lǐng)域?qū)ο蟮年P(guān)鍵屬性井辆。

領(lǐng)域模型是 DDD 的核心,主要作用有兩個:

  • 將領(lǐng)域知識可視化溶握,準確杯缺、深刻的反映領(lǐng)域知識,并且在業(yè)務(wù)和技術(shù)人員之間達成一致睡榆;
  • 指導(dǎo)系統(tǒng)的設(shè)計和編碼萍肆。

團隊成員不管是頭腦里想的、交流中用的胀屿,還是文檔中寫的塘揣、UML圖中畫的、代碼里表達的都是對領(lǐng)域模型的直接映射宿崭,所以我們說領(lǐng)域模型是團隊所有角色在腦海里對業(yè)務(wù)知識構(gòu)建的一致畫面亲铡。


domain-model.png

如何畫領(lǐng)域模型

領(lǐng)域模型用領(lǐng)域模型圖來呈現(xiàn),通常用UML類圖和包圖來畫。

領(lǐng)域模型的表達包括領(lǐng)域?qū)ο箨P(guān)系的表達和領(lǐng)域?qū)ο蟮谋磉_奖蔓,其中領(lǐng)域?qū)ο蟮谋磉_又包括實體的表達琅摩、值對象的表達和聚合的表達。

express.png

領(lǐng)域?qū)ο箨P(guān)系的表達

領(lǐng)域?qū)ο蟮年P(guān)系主要有兩種:關(guān)聯(lián)和泛化锭硼。

靶子和靶標是兩個實體房资,你問我:“它們之間是否有關(guān)系?”檀头,我回答說:“靶標是靶子的答案轰异,肯定是有關(guān)系的∈钍迹”于是你在它們兩個之間畫了一條線搭独,表示它們之間有關(guān)系。

你又問我:“一個靶子最多可以有幾個靶標廊镜?”我回答說:“一個靶子只能有一個靶標”牙肝。于是你在靶標那一端寫了一個“1”來表示。你接著反問:“一個靶標最多可以屬于幾個靶子嗤朴?”我回答說:“一個靶標最多屬于一個靶子配椭。”于是你在另一邊也寫上“1”雹姊。


1-1-association.png

我們可以說股缸,靶子和靶標具有一對一的關(guān)系。這里的兩個 “1” 吱雏,在 UML 中稱為多重性(multiplicity)敦姻。那么,這種關(guān)系整體上呢歧杏,在 UML 的術(shù)語里叫做“關(guān)聯(lián)”(association)镰惦。后面我們都用這種嚴格的說法,說成一對一關(guān)聯(lián)犬绒。

同理就有一對多關(guān)聯(lián)和多對多關(guān)聯(lián)旺入,其中多用 “*”來表達。

一個語言規(guī)范可以對應(yīng)多個靶子懂更,一個靶子只能歸屬一個規(guī)范:


1-*-association.png

一個靶場可以包含多個靶子眨业,一個靶子可以屬于多個靶場:


*-*-association.png

解釋完關(guān)聯(lián)的含義后,我們再來看泛化的概念:如果 A 類和 B 類可以統(tǒng)稱為 C 類的話沮协,C 類和 A、B 兩個類就具有泛化關(guān)系卓嫂,其中 C 是父類慷暂,A 和 B 是子類。泛化關(guān)系用一個空心箭頭表示,由子類指向父類行瑞。

除了“統(tǒng)稱”以外奸腺,泛化關(guān)系轉(zhuǎn)換成自然語言,還可以有另外三種說法血久,我們以教練為例進行說明:

  • 對于教練來說突照,可以分成兩類:一類是管理教練,另一類是技術(shù)教練氧吐。也就是說讹蘑,泛化表示的是一種“分類”關(guān)系。
  • 管理教練是教練筑舅,技術(shù)教練也是教練座慰。也就是所謂“是一個”(is-a)的關(guān)系。
  • 管理教練和技術(shù)教練具有共性翠拣,那就是教導(dǎo)和實操能力版仔,我們把這個共性的概念提取出來,稱為“教練”误墓。另一方面蛮粮,管理教練和技術(shù)教練又具有“個性”,也就是兩者有差別谜慌。


    image.png

“統(tǒng)稱”蝉揍、“分類”、“是一個”以及“共性 / 個性”這四種說法畦娄,雖然從表面上看不同又沾,背后的含義卻是完全一樣的。在領(lǐng)域模型里熙卡,不論哪種說法杖刷,都可以用泛化來表達〔蛋總的來說滑燃,泛化是一種強大的抽象機制,能夠同時表現(xiàn)出不同對象間的共性和個性颓鲜。

領(lǐng)域?qū)ο蟮谋磉_

領(lǐng)域?qū)ο蠓譃閷嶓w和值對象表窘,其中值對象用類圖來表達,通過<<value>>衍型(stereotype)來標識甜滨。比如時間段是一個值對象乐严,它的類圖如下所示:


value.png

需不需要用<<entity>>衍型來標識實體?這樣做當然也沒有錯衣摩,但一般來說必有性不大昂验,因為對于領(lǐng)域?qū)ο螅^值對象都是實體。

聚合使用包圖來表達既琴,內(nèi)部有一個實體為聚合根占婉,通過<<aggregate root>>衍型來標識。聚合是對一組實體和值對象的封裝甫恩,表示整體和部分的關(guān)系逆济,可以使用空心菱形(原書中 Eric Evans 用錯了,故將錯就錯)表示磺箕,也可以使用實心菱形(更符合UML奖慌,但命名有混淆)表示,但團隊內(nèi)需保持一致滞磺。筆者更傾向使用空心菱形來表示整體部分關(guān)系升薯,后續(xù)的例子都采用這種方式。整體部分關(guān)系是關(guān)聯(lián)關(guān)系的一種特例击困,原來聚合這一端的 “1” 被刪掉了涎劈,因為對于這種整體部分關(guān)系而言,這一端必然是 “1”阅茶,出于簡潔的原因蛛枚,所以就可以不寫了。

員工是一個聚合脸哀,其中一個員工實體作為聚合根代表整體蹦浦,另外兩個實體技能和工作經(jīng)驗作為整體的部分,與員工關(guān)聯(lián)撞蜂,一個員工可以有多種技能和多段工作經(jīng)驗盲镶,如下圖所示:


aggregate.png

使用drawIO畫領(lǐng)域模型圖

假設(shè)我們已經(jīng)完成了靶場管理上下文的領(lǐng)域建模,成果如下:

  • 共有 3 個聚合蝌诡,包括靶子溉贿、規(guī)范和靶場;
  • 聚合根靶子聚合了值對象源文件和值對象靶標浦旱,其中靶標又由值對象靶標項組成宇色;
  • 聚合根規(guī)范聚合了實體版本規(guī)范,版本規(guī)范由值對象語言規(guī)范組成颁湖,同時語言規(guī)范又由值對象語言規(guī)范項組成宣蠕;
  • 聚合根靶場可以泛化為定標靶場、練習靶場和比賽靶場甥捺;
  • 聚合根靶子與聚合根規(guī)范是多對一關(guān)聯(lián)抢蚀,聚合根靶子和聚合根靶場是多對多關(guān)聯(lián)。

我們使用 drawIO 來畫靶場管理上下文的領(lǐng)域模型涎永,如下圖所示:


domain-model-drawio.png

使用 plantUML 畫領(lǐng)域模型圖

在 AI 2.0 時代思币,使用 drawIO 畫的領(lǐng)域模型圖不太方便作為業(yè)務(wù)上下文與大語言模型(Large Language Model鹿响,LLM)交流羡微,于是我們考慮使用 plantUML 來重畫領(lǐng)域模型圖谷饿。

plantUML 使用簡單的描述性語言來定義圖表,這使得用戶能夠通過編寫文本來生成圖形表示妈倔,而無需使用復(fù)雜的圖形編輯工具博投。

我們使用 plantUML 來描述靶場管理上下文的領(lǐng)域模型,如下所示:


@startuml
hide methods
hide circle
package "靶子" {
  class 靶子 <<aggregate root>>  {
    工作空間
    版本
    語言
    是否共享
    靶標模式
    狀態(tài)
    可見用戶組
  }

  class 源文件 <<value>>{
 
  }
  class 靶標 <<value>>{
   
  }

  class 靶標項 <<value>>{
    文件名
    起始行號
    結(jié)束行號
    缺陷編碼
    缺陷大類
    缺陷小類
    缺陷細項
  }
  
  靶子  o-- "*" 源文件
  靶子  o-- "*" 靶標
  靶標 “1” -- "*" 靶標項

}

package "靶場" {
  class 靶場 <<aggregate root>>  {
    語言
    組織
    成績
    記錄
  }

  class 定標靶場 {
    靶標負責人
    靶標專家組
  }

  class 練習靶場 {
    靶標脫敏時間
  }

   class 比賽靶場 {
    開始時間
    結(jié)束時間
    靶標脫敏時間
  }

  靶場 <|-- 定標靶場
  靶場 <|-- 練習靶場
  靶場 <|-- 比賽靶場
}

package "規(guī)范" {
  class 規(guī)范 <<aggregate root>>  {
    工作空間
    已啟用版本列表
  }

  class 版本規(guī)范 {
    版本
  }

  class 語言規(guī)范  <<value>>{
    語言
  }

   class 語言規(guī)范項  <<value>>{
    缺陷編碼
    缺陷大類
    缺陷小類
    缺陷細項
  }

  規(guī)范 o-- "*" 版本規(guī)范
  版本規(guī)范 “1” -- "*" 語言規(guī)范
  語言規(guī)范 “1” -- "*" 語言規(guī)范項
}

靶子.靶子 "*" -left- "*" 靶場.靶場
靶子.靶子 "*" -right- “1” 規(guī)范.規(guī)范

@enduml


在 VSCode 中使用 plantUML 插件生成領(lǐng)域模型圖如下所示:


domain-model-plantuml.png

說明:在有的系統(tǒng)中盯蝴,使用 plantUML 表達聚合根的關(guān)聯(lián)關(guān)系時毅哗,聚合根的格式必須為類名,而不是本文中的包名.類名捧挺,否則生成的領(lǐng)域模型圖將與上圖不同楼熄。

LLM 輔助畫領(lǐng)域模型圖

既然已經(jīng)可以使用 plantUML 畫領(lǐng)域模型圖了子漩,我們考慮后續(xù)降低畫其他領(lǐng)域模型圖的成本:沉淀畫領(lǐng)域模型圖的 Prompt 模版,注入目標領(lǐng)域模型邏輯,讓 LLM 生成 plantUML 文本描述亚享,然后在 VSCode 中使用 plantUML 插件生成領(lǐng)域模型圖。

我們直接給出畫領(lǐng)域模型圖的 Prompt 模版眨层,如下所示:

# 目標領(lǐng)域模型邏輯
%question%

# 輸出要求
- 使用 plantUML 文本描述锁右;
- 使用類圖和包圖來表達領(lǐng)域模型;
- 屬性僅保留中文描述很魂;
- 一對多關(guān)聯(lián)用 plantUML 語法表達就是在線的一端寫 “1”另一端寫"*" 扎酷,多對一關(guān)聯(lián)就是在線的一端寫 “*”另一端寫"1" ,多對多關(guān)聯(lián)就是在線的兩端都寫 “*” 遏匆;
- 當表達聚合根之間的關(guān)聯(lián)關(guān)系時法挨,聚合根格式必須為**包名.類名**,比如對于聚合根靶子來說幅聘,類名和包名均為靶子凡纳,則描述的格式為**靶子.靶子** ;
- 排版緊湊整齊喊暖。

# 示例
```plantuml

@startuml
hide methods
hide circle
package "靶子" {
  class 靶子 <<aggregate root>>  {
    工作空間
    版本
    語言
    是否共享
    靶標模式
    狀態(tài)
    可見用戶組
  }

  class 源文件 <<value>>{
 
  }
  class 靶標 <<value>>{
   
  }

  class 靶標項 <<value>>{
    文件名
    起始行號
    結(jié)束行號
    缺陷編碼
    缺陷大類
    缺陷小類
    缺陷細項
  }
  
  靶子  o-- "*" 源文件
  靶子  o-- "*" 靶標
  靶標 “1” -- "*" 靶標項

}

package "靶場" {
  class 靶場 <<aggregate root>>  {
    語言
    組織
    成績
    記錄
  }

  class 定標靶場 {
    靶標負責人
    靶標專家組
  }

  class 練習靶場 {
    靶標脫敏時間
  }

   class 比賽靶場 {
    開始時間
    結(jié)束時間
    靶標脫敏時間
  }

  靶場 <|-- 定標靶場
  靶場 <|-- 練習靶場
  靶場 <|-- 比賽靶場
}

package "規(guī)范" {
  class 規(guī)范 <<aggregate root>>  {
    工作空間
    已啟用版本列表
  }

  class 版本規(guī)范 {
    版本
  }

  class 語言規(guī)范  <<value>>{
    語言
  }

   class 語言規(guī)范項  <<value>>{
    缺陷編碼
    缺陷大類
    缺陷小類
    缺陷細項
  }

  規(guī)范 o-- "*" 版本規(guī)范
  版本規(guī)范 “1” -- "*" 語言規(guī)范
  語言規(guī)范 “1” -- "*" 語言規(guī)范項
}

靶子.靶子 "*" -left- "*" 靶場.靶場
靶子.靶子 "*" -right- “1” 規(guī)范.規(guī)范

@enduml

# 任務(wù)描述
假如你是一名 DDD 專家惫企,請參考示例,根據(jù)領(lǐng)域模型邏輯來畫領(lǐng)域模型圖陵叽。

兩點說明:

  • Prompt 模版中的 %question% 變量就是待用戶注入的目標領(lǐng)域模型邏輯狞尔;
  • Prompt 模版中的輸出要求可根據(jù)需要靈活擴充。

假設(shè)我們已經(jīng)完成了日常評審上下文的領(lǐng)域建模巩掺,其領(lǐng)域模型邏輯如下所示:

- 共有 4 個聚合偏序,包括評審組、缺陷擴展胖替、工程配置和評審研儒;
- 聚合根評審組有 2 個關(guān)鍵屬性豫缨,即名稱和成員列表,沒有聚合其他實體和值對象端朵;
- 聚合根缺陷擴展有 2 個關(guān)鍵屬性好芭,即名稱和自定義標簽, 沒有聚合其他實體和值對象冲呢;
- 聚合根工程配置有 3 個關(guān)鍵屬性舍败,即路徑、工作空間和規(guī)范版本敬拓;
- 聚合根評審有 1 個關(guān)鍵屬性邻薯,即評審人員,同時泛化了個人評審和集體評審兩個子類乘凸,并且聚合了一個值對象工程(有 2 個關(guān)鍵屬性厕诡,即具體路徑和CommitId),聚合根評審與值對象工程是一對一關(guān)聯(lián);
- 聚合根工程配置與聚合根評審組是多對一關(guān)聯(lián)营勤,聚合根工程配置與聚合根缺陷擴展是多對一關(guān)聯(lián)灵嫌,聚合根工程配置與聚合根評審是一對多關(guān)聯(lián)。

當我們要畫日常評審上下文的領(lǐng)域模型圖時冀偶,僅需將該上下文的領(lǐng)域模型邏輯注入到 Prompt 模版中的變量 %question% 即可醒第,這個過程叫模版實例化。

我們將實例化后的 Prompt 模版發(fā)送給 LLM(比如 ChatGPT):


gpt4-resp.png

LLM 生成的 plantUML 文本格式的領(lǐng)域模型圖如下所示:

@startuml
hide methods
hide circle

package "評審組" {
  class 評審組 <<aggregate root>> {
    名稱
    成員列表
  }
}

package "缺陷擴展" {
  class 缺陷擴展 <<aggregate root>> {
    名稱
    自定義標簽
  }
}

package "工程配置" {
  class 工程配置 <<aggregate root>> {
    路徑
    工作空間
    規(guī)范版本
  }
}

package "評審" {
  class 評審 <<aggregate root>> {
    評審人員
  }
  
  class 個人評審 {
  }

  class 集體評審 {
  }

  class 工程 <<value>> {
    具體路徑
    CommitId
  }

  評審 <|-- 個人評審
  評審 <|-- 集體評審
  評審 o-- "1" 工程
}

' 關(guān)聯(lián)關(guān)系
工程配置.工程配置 "1" -down- "*" 評審.評審
工程配置.工程配置 "*" -left- "1" 評審組.評審組
工程配置.工程配置 "*" -right- "1" 缺陷擴展.缺陷擴展

@enduml


在 VSCode 中使用 plantUML 插件生成目標領(lǐng)域模型如下圖所示:


target-domain-model.png

小結(jié)

領(lǐng)域模型是 DDD 的核心进鸠,修改模型就是修改代碼稠曼,修改代碼就是修改模型。軟件研發(fā)的核心難度在于處理隱藏在業(yè)務(wù)知識中的復(fù)雜度客年,那么模型就是對這種復(fù)雜度的簡化與精煉霞幅。

本文詳細闡述了領(lǐng)域模型的概念和表達方法,同時沉淀了一個畫領(lǐng)域模型的 Prompt 模版量瓜。當給 LLM 注入目標領(lǐng)域模型邏輯后司恳,可以直接生成 plantUML 文本格式的目標領(lǐng)域模型圖。LLM 輔助畫領(lǐng)域模型圖的實踐绍傲,不僅降低了我們畫領(lǐng)域模型圖的成本(節(jié)省了時間)扔傅,而且提高了我們向 LLM 注入業(yè)務(wù)知識的效率(LLM 容易理解 plantUML 文本格式的領(lǐng)域模型圖),希望對讀者有一定的收益烫饼!

參考資料

  • 極客時間專欄猎塞,《手把手教你落地 DDD》,鐘敬
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末杠纵,一起剝皮案震驚了整個濱河市荠耽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌比藻,老刑警劉巖铝量,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倘屹,死亡現(xiàn)場離奇詭異,居然都是意外死亡慢叨,警方通過查閱死者的電腦和手機纽匙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來插爹,“玉大人哄辣,你說我怎么就攤上這事请梢≡玻” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵毅弧,是天一觀的道長气嫁。 經(jīng)常有香客問我,道長够坐,這世上最難降的妖魔是什么寸宵? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮元咙,結(jié)果婚禮上梯影,老公的妹妹穿的比我還像新娘。我一直安慰自己庶香,他們只是感情好甲棍,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赶掖,像睡著了一般感猛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奢赂,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天陪白,我揣著相機與錄音,去河邊找鬼膳灶。 笑死咱士,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的轧钓。 我是一名探鬼主播序厉,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼聋迎!你這毒婦竟也來了脂矫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤霉晕,失蹤者是張志新(化名)和其女友劉穎庭再,沒想到半個月后捞奕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡拄轻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年颅围,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恨搓。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡院促,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斧抱,到底是詐尸還是另有隱情常拓,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布辉浦,位于F島的核電站弄抬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宪郊。R本人自食惡果不足惜掂恕,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弛槐。 院中可真熱鬧懊亡,春花似錦、人聲如沸乎串。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灌闺。三九已至艰争,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間桂对,已是汗流浹背甩卓。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蕉斜,地道東北人逾柿。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像宅此,于是被迫代替她去往敵國和親机错。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

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