DDD理論學(xué)習(xí)系列(12)-- 倉儲(chǔ)

DDD理論學(xué)習(xí)系列——案例及目錄


1. 引言

DDD中的Repository,主要有兩種翻譯:資源庫倉儲(chǔ)尚胞,本文取倉儲(chǔ)之譯喻粹。

說到倉儲(chǔ),我們肯定就想到了倉庫井佑,倉庫一般用來存放貨物属铁,而倉庫一般由倉庫管理員來管理。當(dāng)工廠生產(chǎn)了一批貨物時(shí)躬翁,只需交給倉庫管理員即可红选,他負(fù)責(zé)貨物的堆放;當(dāng)需要發(fā)貨的時(shí)候姆另,倉庫管理員負(fù)責(zé)從倉庫中撿貨進(jìn)行貨物出庫處理。當(dāng)需要庫存盤點(diǎn)時(shí)坟乾,倉庫管理員負(fù)責(zé)核實(shí)貨物狀態(tài)和庫存迹辐。換句話說,倉庫管理員負(fù)責(zé)了貨物的出入庫管理甚侣。通過倉庫管理員這個(gè)角色明吩,保證了倉庫和工廠的獨(dú)立性,工廠只需要負(fù)責(zé)生產(chǎn)即可殷费,而至于貨物如何存放工廠無需關(guān)注印荔。

而我們要講的倉儲(chǔ)就類似于倉庫管理員,只不過它負(fù)責(zé)的不再是貨物的管理详羡,而是聚合的管理仍律,倉儲(chǔ)介于領(lǐng)域模型和數(shù)據(jù)模型之間,主要用于聚合的持久化和檢索实柠。它隔離了領(lǐng)域模型和數(shù)據(jù)模型水泉,以便我們關(guān)注于領(lǐng)域模型而不需要考慮如何進(jìn)行持久化。

2. DDD中的倉儲(chǔ)

2.1. 倉儲(chǔ)的集合特性

倉儲(chǔ)代表一個(gè)聚合的集合,其行為與.Net集合一樣草则,倉儲(chǔ)用來存儲(chǔ)和刪除聚合钢拧,但同時(shí)提供針對(duì)聚合的顯式查詢以及匯總。

2.2. 倉儲(chǔ)與數(shù)據(jù)訪問層的區(qū)別

  1. 倉儲(chǔ)限定了只能通過聚合根來持久化和檢索領(lǐng)域?qū)ο罂缓幔源_保所有改動(dòng)和不變性由聚合處理源内。
  2. 倉儲(chǔ)通過隱藏聚合持久化和檢索的底層技術(shù)實(shí)現(xiàn)領(lǐng)域?qū)拥牡某志没療o關(guān)性(即領(lǐng)域?qū)硬恍枰廊绾纬志没I(lǐng)域?qū)ο螅?/li>
  3. 倉儲(chǔ)在數(shù)據(jù)模型和領(lǐng)域模型定義了一個(gè)邊界。

2.3. 倉儲(chǔ)舉例

下面我們首先來看一個(gè)簡單倉儲(chǔ)的定義:

namespace DomainModel
{
 public interface ICustomerRepository
 {
 Customer FindBy(Guid id);
 void Add(Customer customer);
 void Remove(Customer customer);
 }
}

通常來說份殿,倉儲(chǔ)由應(yīng)用服務(wù)層調(diào)用膜钓。倉儲(chǔ)定義應(yīng)用服務(wù)執(zhí)行業(yè)務(wù)用例時(shí)需要的所有的數(shù)據(jù)訪問方法。而倉儲(chǔ)的實(shí)現(xiàn)通常位于基礎(chǔ)架構(gòu)層伯铣,由持久化框架來支撐呻此。以下的倉儲(chǔ)實(shí)現(xiàn)是借助于ORM框架Nhibernate的ISession接口,它扮演一個(gè)的網(wǎng)關(guān)角色腔寡,負(fù)責(zé)領(lǐng)域模型和數(shù)據(jù)模型的映射焚鲜。

namespace Infrastructure.Persistence {
    public class CustomerRepository : ICustomerRepository {
        private ISession _session;
        public CustomerRepository (ISession session) {
            _session = session;
        }
        public IEnumerable<Customer> FindBy (Guid id)
            return _session.Load<Order> (id);
        }

        public void Add (Customer customer) {
            _session.Save (customer);
        }

        public void Remove (Customer customer) {
            _session.Delete (customer);
        }
    }
}

從上面我們可以看出,將領(lǐng)域模型的持久化轉(zhuǎn)移到基礎(chǔ)設(shè)施層放前,隱藏了領(lǐng)域模型的技術(shù)復(fù)雜性忿磅,從而使領(lǐng)域?qū)ο竽軌驅(qū)W⒂跇I(yè)務(wù)概念和邏輯。

2.4. 倉儲(chǔ)的誤解

倉儲(chǔ)也存在很多誤解凭语,許多人認(rèn)為其是不必要的抽象葱她。當(dāng)應(yīng)用于簡單的領(lǐng)域模型時(shí),可以直接使用持久化框架來進(jìn)行數(shù)據(jù)訪問似扔。然而當(dāng)對(duì)復(fù)雜的領(lǐng)域模型進(jìn)行建模時(shí)吨些,倉儲(chǔ)是模型的擴(kuò)展,它表明聚合檢索的意圖炒辉,可以對(duì)領(lǐng)域模型進(jìn)行有意義的讀寫豪墅,而不是一個(gè)技術(shù)框架。

也有很多人認(rèn)為倉儲(chǔ)是一種反模式黔寇,因?yàn)槠潆[藏了基礎(chǔ)持久化框架的功能偶器。而恰巧這正是倉儲(chǔ)的要點(diǎn)》炜悖基礎(chǔ)持久化框架提供了開放的接口用于對(duì)數(shù)據(jù)模型的查找和修改屏轰,而倉儲(chǔ)通過使用定義的命名查詢方法來限制對(duì)聚合的訪問。通過使查詢顯式化憋飞,就更容易調(diào)整查詢霎苗,且更重要的是倉儲(chǔ)明確了查詢的意圖,便于領(lǐng)域?qū)<依斫獠笳浮Ee個(gè)例子:我們?cè)趥}儲(chǔ)中定義了一個(gè)方法GetAllActiveUsers()與sql語句select * from users where isactive = 1var users =db.Users.Where(u=>u.IsActive ==1)相比叨粘,很明顯倉儲(chǔ)的方法命名就能讓我們明白了查詢的意圖:查詢所有處于Active狀態(tài)的用戶猾编。除了查詢,倉儲(chǔ)僅暴露必要的持久化方法而不是提供所有的CURD方法升敲。

2.5. 倉儲(chǔ)的要點(diǎn)

倉儲(chǔ)的要點(diǎn)并不是使代碼更容易測(cè)試答倡,也不是為了便于切換底層的持久化存儲(chǔ)方式。當(dāng)然驴党,在某種程度上瘪撇,這也的確是倉儲(chǔ)所帶來的利好。倉儲(chǔ)的要點(diǎn)是保持你的領(lǐng)域模型和技術(shù)持久化框架的獨(dú)立性港庄,這樣你的領(lǐng)域模型可以隔離來自底層持久化技術(shù)的影響倔既。如果沒有倉儲(chǔ)這一層,你的持久化基礎(chǔ)設(shè)施可能會(huì)泄露到領(lǐng)域模型中鹏氧,并影響領(lǐng)域模型完整性和最終一致性渤涌。

3. 領(lǐng)域模型 VS 數(shù)據(jù)模型

如果選擇關(guān)系型數(shù)據(jù)庫作為持久化存儲(chǔ),我們可以借助于ORM框架來實(shí)現(xiàn)領(lǐng)域模型和數(shù)據(jù)模型之間的映射和持久化操作把还。

而ORM又是什么呢实蓬?

按照文章開頭中的例子,如果倉儲(chǔ)對(duì)應(yīng)倉庫管理員的角色吊履,那ORM就相當(dāng)于倉庫機(jī)器人安皱,而倉庫就相當(dāng)于數(shù)據(jù)庫。為了方便不同商品的歸類存放艇炎,對(duì)倉庫進(jìn)行分區(qū)酌伊,分區(qū)就相當(dāng)于數(shù)據(jù)表。當(dāng)公司接到一筆訂單做發(fā)貨處理時(shí)缀踪,銷售員將發(fā)貨通知單告知倉庫管理員居砖,倉庫管理員再分配ORM機(jī)器人進(jìn)行撿貨。很顯然驴娃,ORM機(jī)器人必須能夠識(shí)別發(fā)貨通知單悯蝉,將發(fā)貨通知單中的商品對(duì)應(yīng)到倉庫中存儲(chǔ)的貨物。這里面發(fā)貨通知單就相當(dāng)于領(lǐng)域模型托慨,而倉庫中存儲(chǔ)的貨物就屬于數(shù)據(jù)模型。

相信基于上面的比喻暇榴,我們對(duì)ORM有了基本的認(rèn)識(shí)厚棵。ORM,全稱是Object Relational Mapping蔼紧,對(duì)象關(guān)系映射婆硬。ORM的前提是,將對(duì)象的屬性映射到數(shù)據(jù)庫字段奸例,將對(duì)象之間的引用映射到數(shù)據(jù)庫表的關(guān)系彬犯。換句話說向楼,ORM負(fù)責(zé)將代碼中定義的對(duì)象和關(guān)系映射到數(shù)據(jù)庫的表結(jié)構(gòu)中去,并在進(jìn)行數(shù)據(jù)訪問時(shí)再將表數(shù)據(jù)映射到代碼中定義的對(duì)象谐区,借助ORM我們不需要去手動(dòng)寫SQL語句就可以完成數(shù)據(jù)的增刪改查湖蜕。ORM僅僅抽象了關(guān)系數(shù)據(jù)模型,它只是以面向?qū)ο蟮姆绞絹肀硎緮?shù)據(jù)模型宋列,以方便我們?cè)诖a中輕松地處理數(shù)據(jù)昭抒。

下面我們來探討一下數(shù)據(jù)模型與領(lǐng)域模型的異同。關(guān)系數(shù)據(jù)庫中的數(shù)據(jù)模型炼杖,它由表和列組成灭返,它只是簡單的存儲(chǔ)結(jié)構(gòu),用于保存領(lǐng)域模型某個(gè)時(shí)間點(diǎn)的狀態(tài)坤邪。數(shù)據(jù)模型可以分散在幾個(gè)表甚至幾個(gè)數(shù)據(jù)庫中熙含。此外,可以使用多種形式的持久化存儲(chǔ)艇纺,例如文件怎静、web服務(wù)器、關(guān)系數(shù)據(jù)庫或NoSQL喂饥。領(lǐng)域模型是對(duì)問題域的抽象消约,具有豐富的語言和行為,由實(shí)體和值對(duì)象組成员帮。對(duì)于一些領(lǐng)域模型或粮,可能與數(shù)據(jù)模型相似,甚至相同捞高,但在概念上它們是非常不同的氯材。ORM與領(lǐng)域模型無關(guān)。倉儲(chǔ)的作用就是將領(lǐng)域模型與數(shù)據(jù)模型分開硝岗,而不是讓它們模糊成一個(gè)模型氢哮。ORM不是倉儲(chǔ),但是倉儲(chǔ)可以使用ORM來持久化領(lǐng)域?qū)ο蟮臓顟B(tài)型檀。

如果你的領(lǐng)域模型與你的數(shù)據(jù)模型類似冗尤,ORM可以直接映射領(lǐng)域模型到數(shù)據(jù)存儲(chǔ),否則胀溺,則需要對(duì)ORM進(jìn)行額外的映射配置裂七。

4. 倉儲(chǔ)的定義和實(shí)現(xiàn)

上面也提到過,我們一般在領(lǐng)域?qū)佣x倉儲(chǔ)接口仓坞,在基礎(chǔ)設(shè)施層實(shí)現(xiàn)倉儲(chǔ)背零,以隔離領(lǐng)域模型和數(shù)據(jù)模型。

4.1. 倉儲(chǔ)方法需明確

倉儲(chǔ)是原則上是領(lǐng)域模型與持久化存儲(chǔ)之間明確的契約无埃,倉儲(chǔ)定義的接口方法不僅僅是CURD方法徙瓶。它是領(lǐng)域模型的擴(kuò)展毛雇,并以領(lǐng)域?qū)<宜斫獾男g(shù)語編寫。倉儲(chǔ)接口的定義應(yīng)該根據(jù)應(yīng)用程序的用例需求來創(chuàng)建侦镇,而不是從類似CURD的數(shù)據(jù)訪問角度來構(gòu)建灵疮。

我們來看一段代碼:

namespace DomainModel {
    public interface ICustomerRepository {
        Customer FindBy (Guid id);
        IEnumerable<Customer> FindAllThatMatch (Query query);
        IEnumerable<Customer> FindAllThatMatch (String hql);
        void Add (Customer customer);
    }
}

以上倉儲(chǔ)定義了一個(gè)FindAllThatMatch方法以支持客戶端以任何方式查詢領(lǐng)域?qū)ο蟆_@個(gè)方法的設(shè)計(jì)思想無可置否虽缕,靈活且可以擴(kuò)展始藕,但是它并沒有明確的表明查詢的意圖,我們就失去了對(duì)查詢的控制氮趋。為了真正了解如何使用這些方法伍派,開發(fā)人員需要跟蹤相關(guān)調(diào)用堆棧,才能知悉方法的意圖剩胁,更別說出現(xiàn)性能問題時(shí)如何著手優(yōu)化了诉植。因?yàn)閭}儲(chǔ)定義的接口方法過于寬泛且不具體,它模糊了領(lǐng)域的的概念昵观,所以定義這樣的一個(gè)接口方法是無意義的晾腔。

我們可以如下改造:

namespace DomainModel {
    public interface ICustomerRepository {
        Customer FindBy (Guid id);
        IEnumerable<Customer> FindAllThatAreDeactivated ();
        IEnumerable<Customer> FindAllThatAreOverAllowedCredit ();
        void Add (Customer customer);
    }
}

通過以上改造,我們通過方法的命名來明確查詢的意圖啊犬,符合通用語言的規(guī)范灼擂。

4.2. 泛型倉儲(chǔ)

在實(shí)踐中我們可能會(huì)發(fā)現(xiàn),為每一個(gè)聚合定義一個(gè)倉儲(chǔ)會(huì)導(dǎo)致重復(fù)代碼觉至,因?yàn)榇蟛糠值臄?shù)據(jù)操作都是類似的剔应。為了代碼重用,泛型倉儲(chǔ)就應(yīng)時(shí)而生语御。

泛型倉儲(chǔ)舉例:

namespace DomainModel {
    public interface IRepository<T> where T : EntityBase {
        T GetById (int id);
        IEnumerable<T> List ();
        IEnumerable<T> List (Expression<Func<T, bool>> predicate);
        void Add (T entity);
        void Delete (T entity);
        void Edit (T entity);
    }

    public abstract class EntityBase {
        public int Id { get; protected set; }
    }
}

泛型倉儲(chǔ)實(shí)現(xiàn):

namespace Infrastructure.Persistence {
    public class Repository<T> : IRepository<T> where T : EntityBase {
        private readonly ApplicationDbContext _dbContext;
        public Repository (ApplicationDbContext dbContext) {
            _dbContext = dbContext;
        }
        public virtual T GetById (int id) {
            return _dbContext.Set<T> ().Find (id);
        }

        public virtual IEnumerable<T> List () {
            return _dbContext.Set<T> ().AsEnumerable ();
        }

        public virtual IEnumerable<T> List (Expression<Func<T, bool>> predicate) {
            return _dbContext.Set<T> ()
                .Where (predicate)
                .AsEnumerable ();
        }

        public void Insert (T entity) {
            _dbContext.Set<T> ().Add (entity);
            _dbContext.SaveChanges ();
        }

        public void Update (T entity) {
            _dbContext.Entry (entity).State = EntityState.Modified;
            _dbContext.SaveChanges ();
        }

        public void Delete (T entity) {
            _dbContext.Set<T> ().Remove (entity);
            _dbContext.SaveChanges ();
        }
    }
}

通過定義泛型倉儲(chǔ)和默認(rèn)的實(shí)現(xiàn)峻贮,很大程度上進(jìn)行了代碼重用。但是应闯,嘗試將泛型倉儲(chǔ)應(yīng)用所有倉儲(chǔ)并不是一個(gè)好的主意纤控。對(duì)于簡單的聚合我們可以直接使用泛型倉儲(chǔ)來簡化代碼。但對(duì)于復(fù)雜的聚合碉纺,泛型倉儲(chǔ)可能就會(huì)不太適合船万,如果基于泛型倉儲(chǔ)的方法進(jìn)行數(shù)據(jù)訪問,就會(huì)模糊對(duì)聚合的訪問意圖骨田。

對(duì)于復(fù)雜的聚合唬涧,我們可以重新定義:

namespace DomainModel {
    public interface ICustomerRepository {
        Customer FindBy (Guid id);
        IEnumerable<Customer> FindAllThatAreDeactivated ();
        void Add (Customer customer);
    }
}

在實(shí)現(xiàn)時(shí),我們可以引用泛型倉儲(chǔ)來避免代碼重復(fù)盛撑。

namespace Infrastructure.Persistence {
    public class CustomerRepository : ICustomerRepository {
        private IRepository<Customer> _customersRepository;
        public Customers (IRepository<Customer> customersRepository) {
            _customersRepository = customersRepository;
        }
        // ....
        public IEnumerable<Customer> FindAllThatAreDeactivated () {
            _customersRepository.List(c => c.IsActive == false);
        }
        public void Add (Customer customer) {
            _customersRepository.Add (customer);
        }
    }
}

通過這種方式,我們即明確了查詢了意圖捧搞,又簡化了代碼抵卫。

4.3. IQueryable Vs IEnumerable

在定義倉儲(chǔ)方法的返回值時(shí)狮荔,我們可能會(huì)比較疑惑,是應(yīng)該直接返回?cái)?shù)據(jù)(IEnumerable)還是返回查詢(IQueryable)以便進(jìn)行進(jìn)一步的細(xì)化查詢介粘?返回IEnumerable會(huì)比較安全殖氏,但IQueryable提供了更好的靈活性。事實(shí)上姻采,如果使用IQueryable作為返回值雅采,我們僅提供一種讀取數(shù)據(jù)的方法即可進(jìn)行各種查詢。
但是這種方式就會(huì)引入一個(gè)問題慨亲,就是業(yè)務(wù)邏輯會(huì)滲透到應(yīng)用層中去婚瓜,并出現(xiàn)大量重復(fù)。比如刑棵,在實(shí)體中我們一般使用IsActiveIsDeleted屬性來表示軟刪除巴刻,而一旦實(shí)體中的某條數(shù)據(jù)被刪除,那么UI中基本不會(huì)再顯示這條數(shù)據(jù)蛉签,那對(duì)于實(shí)體的查詢都需要包含類似Where(c=> c.IsActive)的linq表達(dá)式胡陪。對(duì)于這種問題,我們最好在倉儲(chǔ)中的方法中碍舍,比如List()或者ListActive()做默認(rèn)處理柠座,而不是在應(yīng)用服務(wù)層每次去指定查詢條件。
但具體是返回 IQueryable還是IEnumerable每個(gè)人的看法不一片橡,具體可參考Repository 返回 IQueryable妈经?還是 IEnumerable?锻全。

5. 事務(wù)管理和工作單元

事務(wù)管理主要是應(yīng)用服務(wù)層的關(guān)注點(diǎn)狂塘。然而,因?yàn)閭}儲(chǔ)和事務(wù)管理緊密相關(guān)的鳄厌。倉儲(chǔ)僅關(guān)注單一聚合的管理荞胡,而一個(gè)業(yè)務(wù)用例可能會(huì)涉及到多種的聚合。

事務(wù)管理由UOW(Unit of Work)處理了嚎。UOW模式的作用是在業(yè)務(wù)用例的操作中跟蹤聚合的所有更改泪漂。一旦發(fā)生了更改,UOW就使用事務(wù)來協(xié)調(diào)持久化存儲(chǔ)歪泳。為了確保數(shù)據(jù)的完整性萝勤,如果提交數(shù)據(jù)失敗,則會(huì)回滾所有更改呐伞,以確保數(shù)據(jù)保持有效狀態(tài)敌卓。

而關(guān)于UOW又是一個(gè)復(fù)雜的話題,我們后續(xù)再講伶氢。

6. 倉儲(chǔ)的反模式(注意事項(xiàng))

  1. 不要支持臨時(shí)查詢(ad hoc query)
    倉儲(chǔ)不應(yīng)該開放擴(kuò)展趟径,不要為了支持多種形式的查詢瘪吏,定義比較寬泛的查詢方法,它不僅不能明確表達(dá)倉儲(chǔ)查詢的意圖蜗巧,更可能會(huì)導(dǎo)致查詢性能掌眠。

  2. 延遲加載是一種設(shè)計(jì)臭味
    聚合應(yīng)圍繞不變性構(gòu)建,并包含所有必需的屬性去支持不變性幕屹。 因此蓝丙,當(dāng)加載聚合時(shí),要么加載所有望拖,要么一個(gè)也不加載渺尘。 如果您有一個(gè)關(guān)系數(shù)據(jù)庫并且正在使用ORM作為數(shù)據(jù)模型,那么您可能能夠延遲加載一些領(lǐng)域?qū)ο髮傩钥坑椋@樣就可以推遲加載不需要的聚合部分沧烈。但是,這樣做的問題是像云,如果您只能部分加載聚合锌雀,可能會(huì)導(dǎo)致您的聚合邊界錯(cuò)誤。

  3. 不要使用聚合來實(shí)現(xiàn)報(bào)表需求
    報(bào)表可能會(huì)涉及到多個(gè)類型的聚合迅诬,而倉儲(chǔ)是處理單一聚合的腋逆。另外倉儲(chǔ)是基于事務(wù)的,可能會(huì)導(dǎo)致報(bào)表的性能問題侈贷。

7. 總結(jié)

  1. 倉儲(chǔ)作為領(lǐng)域模型和數(shù)據(jù)模型的中介惩歉,它負(fù)責(zé)映射領(lǐng)域模型到持久化存儲(chǔ)。
  2. 倉儲(chǔ)實(shí)現(xiàn)了透明持久化俏蛮,即領(lǐng)域?qū)硬恍枰P(guān)注領(lǐng)域?qū)ο笕绾纬志没?/li>
  3. 倉儲(chǔ)是一個(gè)契約撑蚌,而不是數(shù)據(jù)訪問層。它明確表明聚合所必需的數(shù)據(jù)操作搏屑。
  4. ORM框架不是倉儲(chǔ)争涌。倉儲(chǔ)是一種架構(gòu)模式。ORM用來以面向?qū)ο蟮姆绞絹肀硎緮?shù)據(jù)模型辣恋。倉儲(chǔ)使用ORM來協(xié)調(diào)領(lǐng)域模型和數(shù)據(jù)模型亮垫。
  5. 倉儲(chǔ)適用于具有豐富領(lǐng)域模型的限界上下文。對(duì)于沒有復(fù)雜業(yè)務(wù)邏輯的簡單限界上下文伟骨,直接使用持久化框架即可饮潦。
  6. 使用UOW進(jìn)行事務(wù)管理。UOW負(fù)責(zé)跟蹤對(duì)象的狀態(tài)携狭,倉儲(chǔ)在UOW協(xié)調(diào)的事務(wù)中進(jìn)行實(shí)際的持久化工作继蜡。
  7. 倉儲(chǔ)用于管理單個(gè)聚合,它不應(yīng)該控制事務(wù)。

參考資料:
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)的實(shí)踐經(jīng)驗(yàn)分享之持久化透明
Repository Pattern--A data persistence abstraction
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)的實(shí)踐經(jīng)驗(yàn)分享之ORM的思考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稀并,一起剝皮案震驚了整個(gè)濱河市鲫剿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稻轨,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雕凹,死亡現(xiàn)場(chǎng)離奇詭異殴俱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)枚抵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門线欲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人汽摹,你說我怎么就攤上這事李丰。” “怎么了逼泣?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵趴泌,是天一觀的道長。 經(jīng)常有香客問我拉庶,道長嗜憔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任氏仗,我火速辦了婚禮吉捶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘皆尔。我一直安慰自己呐舔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布慷蠕。 她就那樣靜靜地躺著珊拼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪砌们。 梳的紋絲不亂的頭發(fā)上杆麸,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音浪感,去河邊找鬼昔头。 笑死,一個(gè)胖子當(dāng)著我的面吹牛影兽,可吹牛的內(nèi)容都是我干的揭斧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼讹开!你這毒婦竟也來了盅视?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤旦万,失蹤者是張志新(化名)和其女友劉穎闹击,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體成艘,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赏半,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了淆两。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片断箫。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秋冰,靈堂內(nèi)的尸體忽然破棺而出仲义,到底是詐尸還是另有隱情,我是刑警寧澤剑勾,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布埃撵,位于F島的核電站,受9級(jí)特大地震影響甥材,放射性物質(zhì)發(fā)生泄漏盯另。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一洲赵、第九天 我趴在偏房一處隱蔽的房頂上張望鸳惯。 院中可真熱鬧,春花似錦叠萍、人聲如沸芝发。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽龄寞。三九已至张惹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背襟铭。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工惨恭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牲平,地道東北人皂贩。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像自沧,于是被迫代替她去往敵國和親坟奥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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