從壹開始微服務(wù) [ DDD ] 之五 ║聚合:實體與值對象 (上)

前言

哈嘍维费,老張是周四放松又開始了果元,這些天的工作真的是繁重,三個項目同時啟動掩完,沒辦法噪漾,只能在深夜寫文章了硼砰,現(xiàn)在時間的周四凌晨且蓬,白天上班已經(jīng)沒有時間開始寫文章了,希望看到文章的小伙伴题翰,能給個辛苦贊??哈哈恶阴,當(dāng)然看心情很隨意。廢話不多說豹障,話說上次咱們對DDD簡單說明了下存在的意義冯事,還有就是基于教學(xué)上下文的第一次定義,今天咱們就繼續(xù)說說DDD領(lǐng)域驅(qū)動設(shè)計中的聚合相關(guān)知識血公,聚合這一塊比較多昵仅,我暫時決定用兩到三篇文章來說說,今天就主要說一下“實體和值對象”的相關(guān)概念,其實之前我在定計劃的時候摔笤,感覺這一塊應(yīng)該很好說够滑,但是晚上吃完飯搜索資料的時候,發(fā)現(xiàn)真的好多人對實體理解的還好吕世,但是對值對象真是各種不理解彰触,甚至嗤之以鼻,這一點我感覺是不好的命辖,希望我的讀者不要只會說這個不好况毅,那個不對,而是想尔艇,這個東西既然產(chǎn)生了尔许,并且一直被大家說著,也有在使用的终娃,肯定有存在的意義母债,舉個栗子,可能今天大家看完對值對象還是蒙朧朧尝抖,多想想毡们,多跟著DDD的思想走,也許就好多了昧辽,思想真的很難改變衙熔,不過只要努力了就是成功了。

好搅荞!咱們還是開篇一個小問題红氯,給大家正好一個思考的時間:

咱們從壹大學(xué)的后臺系統(tǒng)中,每個學(xué)生都有自己的家庭住址咕痛,肯定會有這樣或那樣的原因痢甘,會變化,那我們是如何設(shè)計 Student模型 和 Address 模型的呢茉贡,這里只是說代碼實現(xiàn)上塞栅,數(shù)據(jù)庫其實是對應(yīng)的。

1腔丧、在Students實體中放椰,添加家庭地址屬性:省、市愉粤、縣砾医、街道;

2衣厘、新建家庭地址Address實體如蚜,在Student中引入地址外鍵;

3、新建 Students 错邦、Address涎显、StuAdd三個表,在Students中引入List<Address>兴猩,一對多期吓;

這個就是我們平時的思路,無論是第一種的一對一(一個學(xué)生一個家庭地址)倾芝,還是第三種的一對多(一個學(xué)生多個家庭地址)讨勤,如果你對這個思路很熟悉,那就需要好好看看今天的文章了晨另,因為上邊的這種還是面向數(shù)據(jù)庫數(shù)據(jù)開發(fā)的潭千,希望下邊的說明,能讓你對DDD的思想有一定的體驗借尿。

零刨晴、今天要實現(xiàn)藍(lán)色的部分

image

一、實體 —— 唯一標(biāo)識

實體對應(yīng)的英語單詞為Entity路翻。提到實體狈癞,你可能立馬就想到了代碼中定義的實體類。在使用一些ORM框架時茂契,比如Entity Framework蝶桶,實體作為直接反映數(shù)據(jù)庫表結(jié)構(gòu)的對象,就更尤為重要掉冶。特別是當(dāng)我們使用EF Code First時真竖,我們首先要做的就是實體類的設(shè)計。在DDD中厌小,實體作為領(lǐng)域建模的工具之一恢共,也是十分重要的概念。

但DDD中的實體和我們以往開發(fā)中定義的實體是同一個概念嗎璧亚?
不完全是讨韭。在以往未實施DDD的項目中,我們習(xí)慣于將關(guān)注點放在數(shù)據(jù)上涨岁,而非領(lǐng)域上拐袜。這也就說明了為什么我們在軟件開發(fā)過程中會首先做數(shù)據(jù)庫的設(shè)計,進(jìn)而根據(jù)數(shù)據(jù)庫表結(jié)構(gòu)設(shè)計相應(yīng)的實體對象梢薪,這樣的實體對象是數(shù)據(jù)模型轉(zhuǎn)換的結(jié)果。
在DDD中尝哆,實體作為一個領(lǐng)域概念秉撇,在設(shè)計實體時,我們將從領(lǐng)域出發(fā)。

1琐馆、DDD中的實體是什么

許多對象不是由它們的屬性來定義规阀,而是通過一系列的連續(xù)性(continuity)和標(biāo)識(identity)來從根本上定義的。只要一個對象在生命周期中能夠保持連續(xù)性瘦麸,并且獨立于它的屬性(即使這些屬性對系統(tǒng)用戶非常重要)谁撼,那它就是一個實體。

對于實體Entity滋饲,實體核心是用唯一的標(biāo)識符來定義厉碟,而不是通過屬性來定義。即即使屬性完全相同也可能是兩個不同的對象屠缭。同時實體本身有狀態(tài)的箍鼓,實體又演進(jìn)的生命周期,實體本身會體現(xiàn)出相關(guān)的業(yè)務(wù)行為呵曹,業(yè)務(wù)行為會實體屬性或狀態(tài)造成影響和改變款咖。

如果從值對象本身無狀態(tài),不可變奄喂,并且不分配具體的標(biāo)識層面來看铐殃。那么值對象可以僅僅理解為實際的Entity對象的一個屬性結(jié)合而已。該值對象附屬在一個實際的實體對象上面跨新。值對象本身不存在一個獨立的生命周期背稼,也一般不會產(chǎn)生獨立的行為跟衅。

2寇壳、為什么要使用實體

當(dāng)我們需要考慮一個對象的個性特征凿试,或者要區(qū)分不同對象的時候庭猩,我們就需要一個實體這個領(lǐng)域概念厢蒜,一個實體是一個唯一的東西丸冕,并且可以長時間相當(dāng)長的一段時間內(nèi)持續(xù)的變化嚎研,但是無論我們做了多少變化筒繁,這個的實體對象可能也已經(jīng)變化的很多了许饿,但是因為他們都一個相同的身份標(biāo)識阳欲,所有還是同一個實體。很簡單陋率,就好像一個學(xué)生球化,無論手機(jī)號,姓名瓦糟,年齡筒愚,郵箱,是否畢業(yè)等等菩浙,全部變化了巢掺,因為唯一標(biāo)識的原因句伶,我們就可以認(rèn)為,變化前后的所有對象陆淀,都是同一個實體考余。隨著對象的改變,我們可能會一直跟蹤變化過程轧苫,什么時候楚堤,什么人,發(fā)生了什么變化:就比如學(xué)生因為學(xué)習(xí)太好含懊,學(xué)校研究通過身冬,提前畢業(yè),更新狀態(tài)為已畢業(yè)等绢要。

這個時候我們發(fā)現(xiàn)了吏恭,實體的兩大特性:

1、有唯一的標(biāo)識重罪,不受狀態(tài)屬性的影響樱哼。

2、可變性特征剿配,狀態(tài)信息一直可以變化搅幅。

二、定義一個實體

在我們之前的代碼中呼胚,我們定義了 Student 模型茄唐,我們是在當(dāng)前模型中,添加了唯一標(biāo)識

    public class Student
    {
        protected Student() { }
        public Student(Guid id, string name, string email, DateTime birthDate)
        {
            Id = id;
            Name = name;
            Email = email;
            BirthDate = birthDate;
        }

        public Guid Id { get; private set; }//模型的唯一標(biāo)識
        public string Name { get; private set; }
        public string Email { get; private set; }
        public string Phone { get; private set; }
        public DateTime BirthDate { get; private set; }
    }

我們平時用到的標(biāo)識都是 Int 類型蝇更,優(yōu)點是占位少沪编,內(nèi)存小等,當(dāng)然有時候受到長度的影響年扩,我們就用 long蚁廓,

1、唯一標(biāo)識都是什么類型

一般我們都是會傾向于使用int類型厨幻,映射到數(shù)據(jù)庫中的自增長int相嵌。它的優(yōu)勢是簡單,唯一性由數(shù)據(jù)庫保障况脆,占用空間小饭宾,查詢速度快。我之前也采用了很長時間格了,大部分時候很好用看铆,不過偶爾會很頭痛。由于實體標(biāo)識需要等到插入數(shù)據(jù)庫之后才創(chuàng)建出來笆搓,所以你在保存之前不可能知道標(biāo)識值是多少性湿,如果在保存之前需要拿到Id,唯一的方法是先插入數(shù)據(jù)庫纬傲,得到Id以后满败,再執(zhí)行另外的操作肤频,換句話說,需要把本來是同一個事務(wù)中的操作分成多個事務(wù)執(zhí)行算墨。除了這個問題宵荒,還有多個數(shù)據(jù)庫表合并的問題,如果兩個分表都是自增净嘀,那肯定需要單獨再一個字段來做標(biāo)識报咳,勞民傷財。

后來我就用string字符串來設(shè)置主鍵挖藏,最大的問題就出現(xiàn)了暑刃,就是有時候會出現(xiàn)一致的情況,倒是保存失敗膜眠,然后用戶反饋岩臣,當(dāng)測試的時候,又好了宵膨,這種幽靈事件架谎。所以我就決定使用 Guid 了。

它的主要優(yōu)勢是生成Guid非常容易辟躏,不論是Js,C#還是在數(shù)據(jù)庫中谷扣,都能輕易的生成出來。另外捎琐,Guid的唯一性很強(qiáng)会涎,基本不可能生成出兩個相同的Guid。

Guid類型的主要缺點是占用空間太大瑞凑。另外實體標(biāo)識一般映射到數(shù)據(jù)庫的主鍵末秃,而Sql Server會默認(rèn)把主鍵設(shè)成聚集索引,由于Guid的不連續(xù)性拨黔,這可能導(dǎo)致大量的頁拆分蛔溃,造成大量碎片從而拖慢查詢。一個解決辦法是使用Sql Server來生成Guid篱蝇,它可以生成連續(xù)的Guid值贺待,但這又回到了老路,只有插入數(shù)據(jù)庫你才知道具體的Id值零截,所以行不通麸塞。另一個解決辦法是把聚集索引移到其它列上,比如創(chuàng)建時間涧衙。如果你打算把聚集索引繼續(xù)放到Guid標(biāo)識列上哪工,可以觀察到碎片一般都在90%以上奥此,寫一個Sql腳本,定時在半夜整理一下碎片雁比,也算一個勉強(qiáng)的辦法稚虎。

如果生成一個有意義的流水號來作為標(biāo)識,這時候標(biāo)識類型就是一個字符串偎捎。

有些時候可能還要使用更復(fù)雜的組合標(biāo)識蠢终,這一般需要創(chuàng)建一個值對象作為標(biāo)識類型。

既然每個實體都有一個標(biāo)識茴她,那么為所有實體創(chuàng)建一個基類就顯得很有用了寻拂,這個基類就是層超類型,它為所有領(lǐng)域?qū)嶓w提供基礎(chǔ)服務(wù)丈牢。

2祭钉、創(chuàng)建領(lǐng)域核心類庫,并添加實體

在領(lǐng)域驅(qū)動設(shè)計中己沛,我們會有一些核心的公共的核心內(nèi)容慌核,所以類庫 Christ.Domain.Core 就是起到的這個作用,除了領(lǐng)域模型外泛粹,還有以后的事件遂铡、命令和通知等核心內(nèi)容類。

因為實體屬于領(lǐng)域模型內(nèi)容晶姊,所以我們新建一個 Models 文件夾扒接,并在其新建 Entity.cs 文件

這個時候,如果你問我们衙,為什么要單單定義一個 Entity 基類钾怔,而不把 Id 放到每一個實體中,嗯蒙挑,那就是還沒有命名領(lǐng)域設(shè)計中宗侦,基于業(yè)務(wù)的考慮,我們平時都是直接用面向數(shù)據(jù)庫數(shù)據(jù)的思想來考慮的忆蚀,duang duang設(shè)計表結(jié)構(gòu)矾利,自然而然的想到每一個表(實體模型)必須有一個Id,但是現(xiàn)在馋袜,我們是基于業(yè)務(wù)考慮的男旗,每一個業(yè)務(wù)下邊會有子領(lǐng)域,然后每個子領(lǐng)域都是聚合的欣鳖,通過一個聚合根來關(guān)聯(lián)察皇,把相似的功能或者根單獨拿出來,這個就是實體基類 Entity 的作用泽台,當(dāng)然除了 Id 還會有一些方法什荣,比如以下:

namespace Christ.Domain.Core.Models
{
    /// <summary>
    /// 定義領(lǐng)域?qū)嶓w基類
    /// </summary>
    public abstract class Entity
    {
        /// <summary>
        /// 唯一標(biāo)識
        /// </summary>
        public Guid Id { get; protected set; }

        /// <summary>
        /// 重寫方法 相等運算
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            var compareTo = obj as Entity;

            if (ReferenceEquals(this, compareTo)) return true;
            if (ReferenceEquals(null, compareTo)) return false;

            return Id.Equals(compareTo.Id);
        }
        /// <summary>
        /// 重寫方法 實體比較 ==
        /// </summary>
        /// <param name="a">領(lǐng)域?qū)嶓wa</param>
        /// <param name="b">領(lǐng)域?qū)嶓wb</param>
        /// <returns></returns>
        public static bool operator ==(Entity a, Entity b)
        {
            if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
                return true;

            if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
                return false;

            return a.Equals(b);
        }
        /// <summary>
        /// 重寫方法 實體比較 !=
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(Entity a, Entity b)
        {
            return !(a == b);
        }
        /// <summary>
        /// 獲取哈希
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return (GetType().GetHashCode() * 907) + Id.GetHashCode();
        }
        /// <summary>
        /// 輸出領(lǐng)域?qū)ο蟮臓顟B(tài)
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return GetType().Name + " [Id=" + Id + "]";
        }

    }
}
image

3矾缓、實體模型繼承該Entity

修改我們的 Student 模型,繼承 Entity稻爬,并把屬性 Id 去掉嗜闻。

image

這個時候,我們就已經(jīng)把實體說完了因篇,其實很簡單泞辐,我們平時也都在用笔横,總結(jié)來說以下兩點:

1竞滓、實體的2大特性:唯一標(biāo)識、可變性特性吹缔;

2商佑、通過業(yè)務(wù)的思維,去思考為什么定義 Entity 的作用厢塘,主要也是起到了一個聚合的目的茶没。

那實體我們現(xiàn)在已經(jīng)理解了它的概念,作用晚碾,產(chǎn)生以及意義抓半,剩下的還有一個是實體驗證支持,這個以后再說到格嘁,說到了實體笛求,與之對應(yīng)的是值對象,那值對象又是什么呢糕簿?請往下看探入。

三、值對象 —— 不變性

前面介紹了DDD分層架構(gòu)的實體懂诗,并完成了實體層超類型的開發(fā)( 就是Entity )蜂嗽,本篇將介紹另一個重要的構(gòu)造塊——值對象,它是聚合中的主要成分殃恒。在我們之前的開發(fā)中植旧,因為是基于數(shù)據(jù)庫數(shù)據(jù)的,所以我們基本都是通過數(shù)據(jù)表來建立模型离唐,這就是數(shù)據(jù)建模病附,然后依賴的是數(shù)據(jù)庫范式設(shè)計,這樣我們就把每一個數(shù)據(jù)庫表就對應(yīng)一個實體模型侯繁,每一個表字段就對應(yīng)應(yīng)該實體屬性胖喳。

在看我們文章開頭的那個問題,我們就常常用第一種方法贮竟,

    public class Student : Entity
    { protected Student() { } public Student(Guid id, string name, string email, DateTime birthDate)
        {
            Id = id;
            Name = name;
            Email = email;
            BirthDate = birthDate;
        } //public Guid Id { get; private set; }
        /// <summary>
        /// 姓名 /// </summary>
        public string Name { get; private set; } /// <summary>
        /// 郵箱 /// </summary>
        public string Email { get; private set; } /// <summary>
        /// 手機(jī) /// </summary>
        public string Phone { get; private set; } /// <summary>
        /// 生日 /// </summary>
        public DateTime BirthDate { get; private set; } /// <summary>
        /// 省份 /// </summary>
        public string Province { get; private set; } /// <summary>
        /// 城市 /// </summary>
        public string City { get; private set; } /// <summary>
        /// 區(qū)縣 /// </summary>
        public string County { get; private set; } /// <summary>
        /// 街道 /// </summary>
        public string Street { get; private set; }

    }

但是丽焊,為了考慮不該有的屬性较剃,比如家庭地址信息,不應(yīng)該出現(xiàn)在學(xué)生student的業(yè)務(wù)模型中技健,我們就拆開写穴,用兩個實體進(jìn)行表示,然后引入外鍵雌贱,就是我們第二種方法啊送。

    public class Student : Entity
    { //.....其他屬性

        /// <summary>
        /// 地址外鍵 /// </summary>
        public Address Address { get; private set; }

    } /// <summary>
    /// 地址 /// </summary>
    public class Address :Entity {/// <summary>
        /// 省份 /// </summary>
        public string Province { get; private set; } /// <summary>
        /// 城市 /// </summary>
        public string City { get; private set; }

    }
}

可以看到,對于這樣的簡單場景欣孤,一般有兩個選擇馋没,要么把屬性放到外部的實體中,只創(chuàng)建一張表降传,要么建立兩個實體篷朵,并相應(yīng)的創(chuàng)建兩張表。第一種方法的缺點是婆排,全部屬性值放到一切声旺,沒有了整體業(yè)務(wù)概念,不僅無法表達(dá)業(yè)務(wù)語義段只,而且使用起來非常困難腮猖,同時將很多不必要的業(yè)務(wù)知識泄露到調(diào)用端。第二種方法的問題是導(dǎo)致了不必要的復(fù)雜性赞枕。

更好的方法很簡單澈缺,就是把以上兩種方法結(jié)合起來。我們通過把地址建模成值對象鹦赎,而不是實體谍椅,然后把值對象的屬性值嵌入外部員工實體的表中,這種映射方式被稱為嵌入值模式古话。換句話說雏吭,你現(xiàn)在的數(shù)據(jù)庫表采用上面的第一種方式定義,而你在c#代碼中通過第二種方式使用陪踩,只是把實體改成值對象杖们。這樣做的好處是顯而易見的,既將業(yè)務(wù)概念表達(dá)得清楚肩狂,而且數(shù)據(jù)庫也沒有變得復(fù)雜摘完。

1、值對象的概念

值對象雖然有時候和實體特別想象傻谁,看上邊的學(xué)校家庭信息就可得知孝治,但是它卻有著自己獨有的好處,值對象很常見:比如數(shù)字,字符串谈飒,日期時間岂座,甚至一個人的信息,郵寄地址等等杭措,當(dāng)然還有更復(fù)雜的值對象费什,這些都是反映 通用語言 概念的值對象。

我們應(yīng)該盡量使用值對象來建模手素,而不是實體對象鸳址,你可能很想不通,即使上邊的學(xué)生的家庭地址信息泉懦,你一定要單放一個數(shù)據(jù)庫表稿黍,構(gòu)建實體模型,在設(shè)計的時候我們應(yīng)該也要更偏向作為一個值對象容器祠斧,而不是子實體容器闻察,因為這樣我們可以對值對象很好的創(chuàng)建,測試琢锋,使用,優(yōu)化和維護(hù)呢灶。

當(dāng)你決定一個領(lǐng)域概念是否是一個值對象的時候吴超,你需要考慮它是否有以下特性:

1、它描述了領(lǐng)域中的一個東西

2鸯乃、可以作為一個不變量鲸阻。

3、當(dāng)它被改變時缨睡,可以用另一個值對象替換鸟悴。

4、可以和別的值對象進(jìn)行相等性比較奖年。

在值對象中细诸,我們不關(guān)心標(biāo)識,只要我們能確定該值對象的屬性值都一樣陋守,我們就可以說這兩個值對象是相同的震贵,比如我們說兩個學(xué)生的家庭地址(省市縣街道門排)是一樣的,我們就可以認(rèn)為是同一個地址水评,這就是相等性比較猩系。

如果學(xué)生在修改地址的時候,我們不是僅僅的修改省中燥,或者市寇甸,或者縣,而且將整個值對象給覆蓋,這個就是值對象的不變性和可替換性拿霉。

四式塌、如何創(chuàng)建一個地址值對象

1、創(chuàng)建值對象基類

在 Christ3D.Domain.Core 類庫下的Models文件夾中友浸,新建 ValueObject.cs

namespace Christ3D.Domain.Core.Models
{ /// <summary>
    /// 定義值對象基類 /// 注意沒有唯一標(biāo)識了 /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class ValueObject<T> where T : ValueObject<T> { /// <summary>
        /// 重寫方法 相等運算 /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        { var valueObject = obj as T; return !ReferenceEquals(valueObject, null) && EqualsCore(valueObject);
        } protected abstract bool EqualsCore(T other); /// <summary>
        /// 獲取哈希 /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        { return GetHashCodeCore();
        } protected abstract int GetHashCodeCore(); /// <summary>
        /// 重寫方法 實體比較 == /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(ValueObject<T> a, ValueObject<T> b)
        { if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) return true; if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false; return a.Equals(b);
        } /// <summary>
        /// 重寫方法 實體比較 != /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(ValueObject<T> a, ValueObject<T> b)
        { return !(a == b);
        } /// <summary>
        /// 克隆副本 /// </summary>
        public virtual T Clone()
        { return (T)MemberwiseClone();
        }
    }
}

2峰尝、在 Christ3D.Domain 類庫下的Models文件夾中,新建 Address 值對象

namespace Christ3D.Domain.Models
{ public Address(string province, string city, string county, string street, string zip)
        { this.Province = province; this.City = city; this.County = county; this.Street = street;
        } /// <summary>
    /// 地址 /// </summary>
    public class Address : ValueObject<Address> { /// <summary>
        /// 省份 /// </summary>
        public string Province { get; private set; } /// <summary>
        /// 城市 /// </summary>
        public string City { get; private set; } /// <summary>
        /// 區(qū)縣 /// </summary>
        public string County { get; private set; } /// <summary>
        /// 街道 /// </summary>
        public string Street { get; private set; } protected override bool EqualsCore(Address other)
        { throw new NotImplementedException();
        } protected override int GetHashCodeCore()
        { throw new NotImplementedException();
        }
    }
}

至此收恢,我們的Address就具有了值的特征武学,我們可以直接使用Address address = new Address("北京市", "北京市", "海淀區(qū)", "一路 ");)來表示一個具體的通過屬性識別的不可變的位置概念。在DDD中伦意,我們稱這個Address為值對象火窒。

3、實體與值對象的區(qū)別:

  1. 實體擁有標(biāo)識驮肉,而值對象沒有熏矿。
  2. 相等性測試方式不同。實體根據(jù)標(biāo)識判等离钝,而值對象根據(jù)內(nèi)部所有屬性值判等票编。
  3. 實體允許變化,值對象不允許變化卵渴。
  4. 持久化的映射方式不同慧域。實體采用單表繼承、類表繼承和具體表繼承來映射類層次結(jié)構(gòu)浪读,而值對象使用嵌入值或序列化大對象方式映射昔榴。

五、結(jié)語(待續(xù))

今天因為時間的問題暫時就說這么多吧碘橘,這里只是把 實體 和值對象的概念和使用說明了下互订,具體的好處和強(qiáng)大的優(yōu)勢還沒有來得及說,下一篇文章痘拆,我會說繼續(xù)說聚合的內(nèi)容仰禽,包括實體驗證等,這篇文章也需要慢慢的潤潤色错负,加油吧

六坟瓢、Github & Gitee

https://github.com/anjoy8/ChristDDD

https://gitee.com/laozhangIsPhi/ChristDDD

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市犹撒,隨后出現(xiàn)的幾起案子折联,更是在濱河造成了極大的恐慌,老刑警劉巖识颊,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诚镰,死亡現(xiàn)場離奇詭異奕坟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)清笨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門月杉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抠艾,你說我怎么就攤上這事苛萎。” “怎么了检号?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵腌歉,是天一觀的道長。 經(jīng)常有香客問我齐苛,道長翘盖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任凹蜂,我火速辦了婚禮馍驯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘玛痊。我一直安慰自己汰瘫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布卿啡。 她就那樣靜靜地躺著吟吝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颈娜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天浙宜,我揣著相機(jī)與錄音官辽,去河邊找鬼。 笑死粟瞬,一個胖子當(dāng)著我的面吹牛同仆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播裙品,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼俗批,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了市怎?” 一聲冷哼從身側(cè)響起岁忘,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎区匠,沒想到半個月后干像,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帅腌,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年麻汰,在試婚紗的時候發(fā)現(xiàn)自己被綠了速客。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡五鲫,死狀恐怖溺职,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情位喂,我是刑警寧澤浪耘,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站忆某,受9級特大地震影響点待,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弃舒,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一癞埠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聋呢,春花似錦苗踪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至器贩,卻和暖如春颅夺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛹稍。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工吧黄, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唆姐。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓拗慨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親奉芦。 傳聞我的和親對象是個殘疾皇子赵抢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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