從壹開始微服務 [ DDD ] 之七 ║項目第一次實現(xiàn) & CQRS初探

前言

哈嘍大家周五好臼寄,我們又見面了霸奕,感謝大家在這個周五讀我的文章,經(jīng)過了三周的時間吉拳,當然每周兩篇的速度的情況下质帅,咱們簡單說了下DDD領域驅(qū)動設計的第一部分,主要包括了留攒,《項目入門DDD架構(gòu)淺析》煤惩,《領域、子領域炼邀、限界上下文》魄揉,《DDD使用意義》,《實體與值對象》拭宁,《聚合與聚合根》這五部分內(nèi)容洛退,主要的是以解釋為主,舉例子Code為輔的形式杰标,總體來說還是得到一些肯定的兵怯,也是我最大的動力了。

上邊這五個知識點是DDD領域驅(qū)動設計的第一部分 —— D領域腔剂;

從今天開始媒区,咱們就說說DDD的第二個D,就是領域服務+領域命令的CQRS掸犬,這些偏重動作的一部分袜漩;

最后就是第三部分,通過 領域事件登渣、事件源與事件回溯噪服,配合著權(quán)限管理,再統(tǒng)一說一下DDD胜茧,這一系列就是結(jié)束了。

其實通過我看到這里仇味,我發(fā)現(xiàn)了呻顽,我們在設計DDD的時候,重要的是思路丹墨,重要的是在如何進行領域設計廊遍,而不是在框架和技術上面,有時候就算是三層也能配和著實現(xiàn)領域設計贩挣,之前有小伙伴說到我些的是OOP喉前,嗯没酣,希望等系列寫完就可以稍微不一樣一些吧。

今天我們的主要工作卵迂,就是把前幾天在講述概念的同時裕便,對搭建的項目進行第一次的合圍,能運行起來见咒,當然這里還會涉及到之前我們第一個系列的知識偿衰,我們也進行復習下,比如:DI依賴注入改览、EFCore下翎、Automapper數(shù)據(jù)傳輸對象,當然還有前幾篇文章中的 實體和值對象的部分概念 宝当, 如果您是第一次看我的文章视事,可能這些今天不會詳細說明,可以去我的第一個系列開始學習庆揩,好啦俐东,馬上開始今天的講解。

零盾鳞、今天實現(xiàn)天青色的部分

image

一犬性、項目運行、復習系列一相關知識

1腾仅、Automapper定義Config配置文件

1乒裆、我們在項目應用層Christ3D.Application 的 AutoMapper 文件夾下,新建AutoMapperConfig.cs 配置文件推励,

    /// <summary>
    /// 靜態(tài)全局 AutoMapper 配置文件 /// </summary>
    public class AutoMapperConfig
    { public static MapperConfiguration RegisterMappings()
        { //創(chuàng)建AutoMapperConfiguration, 提供靜態(tài)方法Configure鹤耍,一次加載所有層中Profile定義 //MapperConfiguration實例可以靜態(tài)存儲在一個靜態(tài)字段中,也可以存儲在一個依賴注入容器中验辞。 一旦創(chuàng)建稿黄,不能更改/修改。
            return new MapperConfiguration(cfg => { //這個是領域模型 -> 視圖模型的映射跌造,是 讀命令
                cfg.AddProfile(new DomainToViewModelMappingProfile()); //這里是視圖模型 -> 領域模式的映射杆怕,是 寫 命令
                cfg.AddProfile(new ViewModelToDomainMappingProfile());
            });
        }
    }

這里你可能會問了,咱們之前在 Blog.Core 前后端分離中壳贪,為什么沒有配置這個Config文件陵珍,其實我實驗了下,不用配置文件我們也可以達到映射的目的违施,只不過互纯,我們平時映射文件Profile 比較少,項目啟動的時候磕蒲,每次都會調(diào)取下這個配置文件留潦,你可以實驗下只盹,如果幾十個表,上百個數(shù)據(jù)庫表兔院,啟動會比較慢殖卑,可以使用創(chuàng)建AutoMapperConfiguration, 提供靜態(tài)方法Configure,一次加載所有層中Profile定義秆乳,大概就是這個意思懦鼠,這里我先存?zhèn)€疑,有不同意見的歡迎來說我屹堰,哈哈歡迎批評肛冶。

2、上邊代碼中 DomainToViewModelMappingProfile 咱們很熟悉扯键,就是平時用到的睦袖,但是下邊的那個是什么呢,那個就是我們 視圖模型 -> 領域模式 的時候的映射荣刑,寫法和反著的是一樣的馅笙,你一定會說,那為啥不直接這么寫呢厉亏,

image

你的想法很棒董习!這種平時也是可以的,只不過在DDD領域驅(qū)動設計中爱只,這個是是視圖模型轉(zhuǎn)領域模型皿淋,那一定是對領域模型就行命令操作,沒錯恬试,就是在領域命令中窝趣,會用到這里,所以兩者不能直接寫在一起训柴,這個以后馬上會在下幾篇文章中說到哑舒。

image

3、將 AutoMapper 服務在 Startup 啟動

在 Christ3D.UI.Web 項目下幻馁,新建 Extensions 擴展文件夾洗鸵,以后我們的擴展啟動服務都寫在這里。

新建 AutoMapperSetup.cs

    /// <summary>
    /// AutoMapper 的啟動服務 /// </summary>
    public static class AutoMapperSetup
    { public static void AddAutoMapperSetup(this IServiceCollection services)
        { if (services == null) throw new ArgumentNullException(nameof(services)); //添加服務
 services.AddAutoMapper(); //啟動配置
 AutoMapperConfig.RegisterMappings();
        }
    }

2仗嗦、依賴注入 DI

之前我們在上個系列中预麸,是用的Aufac 將整個層注入,今天咱們換個方法儒将,其實之前也有小伙伴提到了,微軟自帶的 依賴注入方法就可以对蒲。

因為這一塊屬于我們開發(fā)的基礎钩蚊,而且也與數(shù)據(jù)有關贡翘,所以我們就新建一個 IoC 層,來進行統(tǒng)一注入

1砰逻、新建 Christ3D.Infra.IoC 層鸣驱,添加統(tǒng)一注入類 NativeInjectorBootStrapper.cs

    public static void RegisterServices(IServiceCollection services)
     { // 注入 Application 應用層
            services.AddScoped<IStudentAppService, StudentAppService>(); // 注入 Infra - Data 基礎設施數(shù)據(jù)層
            services.AddScoped<IStudentRepository, StudentRepository>();
            services.AddScoped<StudyContext>();//上下文

      }

具體的使用方法和我們Autofac很類型,這里就不說了蝠咆,相信大家已經(jīng)很了解依賴注入了踊东。

image

2、在ConfigureServices 中進行服務注入

 // .NET Core 原生依賴注入 // 單寫一層用來添加依賴項刚操,可以將IoC與展示層 Presentation 隔離
 NativeInjectorBootStrapper.RegisterServices(services);

3闸翅、EFCore Code First

1、相信大家也都用過EF菊霜,這里的EFCore 也是一樣的坚冀,如果我們想要使用 CodeFirst 功能的話,就可以直接對其進行配置鉴逞,

    public class StudyContext : DbContext
    { public DbSet<Student> Students { get; set; } /// <summary>
        /// 重寫自定義Map配置 /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        { //對 StudentMap 進行配置
            modelBuilder.ApplyConfiguration(new StudentMap()); base.OnModelCreating(modelBuilder);
        } /// <summary>
        /// 重寫連接數(shù)據(jù)庫 /// </summary>
        /// <param name="optionsBuilder"></param>
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        { // 從 appsetting.json 中獲取配置信息
            var config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .Build(); //定義要使用的數(shù)據(jù)庫 //正確的是這樣记某,直接連接字符串即可 //optionsBuilder.UseSqlServer(config.GetConnectionString("DefaultConnection")); //我是讀取的文件內(nèi)容,為了數(shù)據(jù)安全
            optionsBuilder.UseSqlServer(File.ReadAllText(config.GetConnectionString("DefaultConnection")));
        }
    }

2构捡、然后我們就可以配置 StudentMap 了液南,針對不同的領域模型進行配置,但是這里有一個重要的知識點勾徽,請往下看:

    /// <summary>
    /// 學生map類 /// </summary>
    public class StudentMap : IEntityTypeConfiguration<Student> { /// <summary>
        /// 實體屬性配置 /// </summary>
        /// <param name="builder"></param>
        public void Configure(EntityTypeBuilder<Student> builder)
        { //實體屬性Map
            builder.Property(c => c.Id)
                .HasColumnName("Id");

            builder.Property(c => c.Name)
                .HasColumnType("varchar(100)")
                .HasMaxLength(100)
                .IsRequired();

            builder.Property(c => c.Email)
                .HasColumnType("varchar(100)")
                .HasMaxLength(11)
                .IsRequired();

            builder.Property(c => c.Phone)
                .HasColumnType("varchar(100)")
                .HasMaxLength(20)
                .IsRequired(); //處理值對象配置滑凉,否則會被視為實體
            builder.OwnsOne(p => p.Address); //可以對值對象進行數(shù)據(jù)庫重命名,還有其他的一些操作捂蕴,請參考官網(wǎng) //builder.OwnsOne( // o => o.Address, // sa => // { // sa.Property(p => p.County).HasColumnName("County"); // sa.Property(p => p.Province).HasColumnName("Province"); // sa.Property(p => p.City).HasColumnName("City"); // sa.Property(p => p.Street).HasColumnName("Street"); // } //); //注意:這是EF版本的寫法譬涡,Core中不能使用!I侗妗涡匀! //builder.Property(c => c.Address.City) // .HasColumnName("City") // .HasMaxLength(20); //builder.Property(c => c.Address.Street) // .HasColumnName("Street") // .HasMaxLength(20); //如果想忽略當前值對象,可直接 Ignore //builder.Ignore(c => c.Address);
 }
    }

重要知識點:

我們以前用的時候溉知,都是每一個實體對應一個數(shù)據(jù)庫表陨瘩,或者有一些關聯(lián),比如一對多的情況级乍,就拿我們現(xiàn)在項目中使用到的來說舌劳,我們的 Student 實體中,有一個 Address 的值對象玫荣,值對象大家肯定都知道的甚淡,是沒有狀態(tài),保證不變性的一個值捅厂,但是在EFCore 的Code First 中贯卦,系統(tǒng)會需要我們提供一個 Address 的主鍵资柔,因為它會認為這是一個表結(jié)構(gòu),如果我們?yōu)?Address 添加主鍵撵割,那就是定義成了實體贿堰,這個完全不是我們想要的,我們設計的原則是一切以領域設計為核心啡彬,不能為了數(shù)據(jù)庫而修改模型羹与。

如果把 Address 當一個實體,增加主鍵庶灿,就可以Code First通過纵搁,但是這個對我們來說是不行的,我們是從領域設計中考慮跳仿,需要把它作為值對象诡渴,是作為數(shù)據(jù)庫字段,你也許會想著直接把 Address 拆開成多個字段放到 Student 實體類中作為屬性菲语,我感覺這樣也是不好的妄辩,這樣就達不到我們領域模型的作用了。

我通過收集資料山上,我發(fā)現(xiàn)可以用上邊注釋的方法眼耀,直接在 StudentMap 中配置,但是我失敗了佩憾,一直報錯

//builder.Property(c => c.Address.City)
// .HasColumnName("City")
// .HasMaxLength(20);

The property 'Student.Address' is of type 'Address' which is not supported by current database provider. Either change the property CLR type or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

本來想放棄的時候哮伟,還是強大的博客園博文功能,讓我找到一個大神妄帘,然后我參考官網(wǎng)楞黄,找到了這個方法。https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities

builder.OwnsOne(p => p.Address);//記得在 Address 值對象上增加一個 [Owned] 特性抡驼。

3鬼廓、Code First 到數(shù)據(jù)庫

我們可以通過以下nuget 命令來控制,這里就不細說了致盟,相信大家用的很多了

//1碎税、初始化遷移記錄 Init 自定義
Add-Migration Init //2、將當前 Init 的遷移記錄更新到數(shù)據(jù)庫
update-database Init 

然后就可以看到我們的的數(shù)據(jù)庫已經(jīng)生成:

image

4馏锡、添加頁面雷蹂,運行

1、到這里我們就已經(jīng)把整體調(diào)通了杯道,然后新建 StudentController.cs 匪煌,添加 CURD 頁面

 //還是構(gòu)造函數(shù)注入
 private readonly IStudentAppService _studentAppService; public StudentController(IStudentAppService studentAppService)
 {
     _studentAppService = studentAppService;
 } // GET: Student
 public ActionResult Index()
 { return View(_studentAppService.GetAll());
 }

2、運行項目,就能看到結(jié)果

image

這個時候虐杯,我們已經(jīng)通過了 DI 進行注入玛歌,然后通過Dtos 將我們的領域模型,轉(zhuǎn)換成了視圖模型擎椰,進行展示,也許這個時候你會發(fā)現(xiàn)创肥,這個很正常呀达舒,平時都是這么做的,也沒有看到有什么高端的地方叹侄,聰明的你一定會想到更遠的地方巩搏,這里我們是用領域模型 -> 視圖模型的DTO,也就是我們平時說的查詢模式趾代,

那有查詢贯底,肯定有編輯模式,我們就會有 視圖模型撒强,傳入禽捆,然后轉(zhuǎn)換領域模型,中間當然還有校驗等等(不是簡單的視圖模型的判空飘哨,還有其他的復雜校驗胚想,比如年齡,字符串)芽隆,這個時候浊服,如果我們直接用 視圖模型 -> 領域模型的話,肯定會有污染胚吁,至少會把讀和寫混合在一起牙躺,

 public void Register(StudentViewModel StudentViewModel)
 { //這里引入領域設計中的寫命令 還沒有實現(xiàn) //請注意這里如果是平時的寫法,必須要引入Student領域模型腕扶,會造成污染
 _StudentRepository.Add(_mapper.Map<Student>(StudentViewModel));
 }

那該怎么辦呢孽拷,這個時候CQRS 就登場了!請往下看蕉毯。

二乓搬、CQRS 讀寫分離初探

從上邊的問題中,我們發(fā)現(xiàn)代虾,在DDD領域驅(qū)動設計中进肯,我們是一起以領域模型為核心的,這個時候出現(xiàn)了幾個概念:

1棉磨、DDD中四種模型

如果你是從我的系列的第一篇開始讀江掩,你應該已經(jīng)對這兩個模型很熟悉了,領域模型,視圖模型环形,當然策泣,還有咱們一直開發(fā)中使用到的數(shù)據(jù)模型,那第四個是什么呢抬吟?

  1. 數(shù)據(jù)模型:面向持久化萨咕,數(shù)據(jù)的載體。
  2. 領域模型:面向業(yè)務火本,行為的載體危队。
  3. 視圖模型:面向UI(向外),數(shù)據(jù)的載體钙畔。
  4. 命令模型:面向UI(向內(nèi))茫陆,數(shù)據(jù)的載體。

這個命令模型Command擎析,就是解決了我們的 視圖模型到領域模型中簿盅,出現(xiàn)污染的問題。其他 命令模型揍魂,就和我們的領域模型桨醋、視圖模型是一樣的,也是一個數(shù)據(jù)載體愉烙,這不過它可以配和著事件讨盒,進行復雜的操作控制,這個以后會慢慢說到步责。

如果你要問寫到哪里返顺,這里簡單說一下,具體的搭建下次會說到蔓肯,就是在我們的 應用層 AutoMapper 文件夾下遂鹊,我們的 ViewModelToDomainMappingProfile.cs

 public class ViewModelToDomainMappingProfile : Profile
 { public ViewModelToDomainMappingProfile()
     { //這里以后會寫領域命令,所以不能和DomainToViewModelMappingProfile寫在一起蔗包。 //學生視圖模型 -> 添加新學生命令模型
         CreateMap<StudentViewModel, RegisterNewStudentCommand>()
             .ConstructUsing(c => new RegisterNewStudentCommand(c.Name, c.Email, c.BirthDate)); //學生視圖模型 -> 更新學生信息命令模型
         CreateMap<StudentViewModel, UpdateStudentCommand>()
             .ConstructUsing(c => new UpdateStudentCommand(c.Id, c.Name, c.Email, c.BirthDate));
     }

2秉扑、傳統(tǒng) CURD 命令有哪些問題

1、使用同一個對象實體來進行數(shù)據(jù)庫讀寫可能會太粗糙调限,大多數(shù)情況下舟陆,比如編輯的時候可能只需要更新個別字段,但是卻需要將整個對象都穿進去耻矮,有些字段其實是不需要更新的秦躯。在查詢的時候在表現(xiàn)層可能只需要個別字段,但是需要查詢和返回整個實體對象裆装。

2踱承、使用同一實體對象對同一數(shù)據(jù)進行讀寫操作的時候倡缠,可能會遇到資源競爭的情況,經(jīng)常要處理的鎖的問題茎活,在寫入數(shù)據(jù)的時候昙沦,需要加鎖。讀取數(shù)據(jù)的時候需要判斷是否允許臟讀载荔。這樣使得系統(tǒng)的邏輯性和復雜性增加盾饮,并且會對系統(tǒng)吞吐量的增長會產(chǎn)生影響。

3身辨、同步的丐谋,直接與數(shù)據(jù)庫進行交互在大數(shù)據(jù)量同時訪問的情況下可能會影響性能和響應性,并且可能會產(chǎn)生性能瓶頸煌珊。

4、由于同一實體對象都會在讀寫操作中用到泌豆,所以對于安全和權(quán)限的管理會變得比較復雜定庵。

這里面很重要的一個問題是,系統(tǒng)中的讀寫頻率比踪危,是偏向讀蔬浙,還是偏向?qū)懀腿缤话愕臄?shù)據(jù)結(jié)構(gòu)在查找和修改上時間復雜度不一樣贞远,在設計系統(tǒng)的結(jié)構(gòu)時也需要考慮這樣的問題畴博。解決方法就是我們經(jīng)常用到的對數(shù)據(jù)庫進行讀寫分離。 讓主數(shù)據(jù)庫處理事務性的增蓝仲,刪俱病,改操作(Insert,Update,Delete)操作,讓從數(shù)據(jù)庫處理查詢操作(Select操作)袱结,數(shù)據(jù)庫復制被用來將事務性操作導致的變更同步到集群中的從數(shù)據(jù)庫亮隙。這只是從DB角度處理了讀寫分離,但是從業(yè)務或者系統(tǒng)上面讀和寫仍然是存放在一起的垢夹。他們都是用的同一個實體對象溢吻。

要從業(yè)務上將讀和寫分離,就是接下來要介紹的命令查詢職責分離模式果元。

3促王、什么是 CQRS 讀寫分離

以下信息來自@寒江獨釣的博文,我看著寫的很好:

CQRS最早來自于Betrand Meyer(Eiffel語言之父而晒,開-閉原則OCP提出者)提到的一種 命令查詢分離 (Command Query Separation,CQS) 的概念蝇狼。其基本思想在于,任何一個對象的方法可以分為兩大類:

  • 命令(Command):不返回任何結(jié)果(void)欣硼,但會改變對象的狀態(tài)题翰。
  • 查詢(Query):返回結(jié)果恶阴,但是不會改變對象的狀態(tài),對系統(tǒng)沒有副作用豹障。

根據(jù)CQS的思想冯事,任何一個方法都可以拆分為命令和查詢兩部分,比如:

  public StudentViewModel Update(StudentViewModel StudentViewModel)
  { //更新操作
      _StudentRepository.Update(_mapper.Map<Student>(StudentViewModel)); //查詢操作
      return _mapper.Map<StudentViewModel>(_StudentRepository.GetById(StudentViewModel.Id));
  }

這個方法血公,我們執(zhí)行了一個命令即對更新Student昵仅,同時又執(zhí)行了一個Query,即查詢返回了Student的值累魔,如果按照CQS的思想摔笤,該方法可以拆成Command和Query兩個方法,如下:

 public StudentViewModel GetById(Guid id)
 { return _mapper.Map<StudentViewModel>(_StudentRepository.GetById(id));
 } public void Update(StudentViewModel StudentViewModel)
 {
     _StudentRepository.Update(_mapper.Map<Student>(StudentViewModel));
 }

操作和查詢分離使得我們能夠更好的把握對象的細節(jié)垦写,能夠更好的理解哪些操作會改變系統(tǒng)的狀態(tài)吕世。當然CQS也有一些缺點,比如代碼需要處理多線程的情況梯投。

CQRS是對CQS模式的進一步改進成的一種簡單模式命辖。 它由Greg Young在CQRS, Task Based UIs, Event Sourcing agh! 這篇文章中提出》直停“CQRS只是簡單的將之前只需要創(chuàng)建一個對象拆分成了兩個對象尔艇,這種分離是基于方法是執(zhí)行命令還是執(zhí)行查詢這一原則來定的(這個和CQS的定義一致)”。

CQRS使用分離的接口將數(shù)據(jù)查詢操作(Queries)和數(shù)據(jù)修改操作(Commands)分離開來么鹤,這也意味著在查詢和更新過程中使用的數(shù)據(jù)模型也是不一樣的终娃。這樣讀和寫邏輯就隔離開來了。

使用CQRS分離了讀寫職責之后蒸甜,可以對數(shù)據(jù)進行讀寫分離操作來改進性能棠耕,可擴展性和安全。如下圖:

image

4迅皇、CQRS 的應用場景

在下場景中昧辽,可以考慮使用CQRS模式:

  1. 當在業(yè)務邏輯層有很多操作需要相同的實體或者對象進行操作的時候。CQRS使得我們可以對讀和寫定義不同的實體和方法登颓,從而可以減少或者避免對某一方面的更改造成沖突搅荞;
  2. 對于一些基于任務的用戶交互系統(tǒng),通常這類系統(tǒng)會引導用戶通過一系列復雜的步驟和操作框咙,通常會需要一些復雜的領域模型咕痛,并且整個團隊已經(jīng)熟悉領域驅(qū)動設計技術。寫模型有很多和業(yè)務邏輯相關的命令操作的堆喇嘱,輸入驗證茉贡,業(yè)務邏輯驗證來保證數(shù)據(jù)的一致性。讀模型沒有業(yè)務邏輯以及驗證堆者铜,僅僅是返回DTO對象為視圖模型提供數(shù)據(jù)腔丧。讀模型最終和寫模型相一致放椰。
  3. 適用于一些需要對查詢性能和寫入性能分開進行優(yōu)化的系統(tǒng),尤其是讀/寫比非常高的系統(tǒng)愉粤,橫向擴展是必須的砾医。比如,在很多系統(tǒng)中讀操作的請求時遠大于寫操作衣厘。為適應這種場景如蚜,可以考慮將寫模型抽離出來單獨擴展,而將寫模型運行在一個或者少數(shù)幾個實例上影暴。少量的寫模型實例能夠減少合并沖突發(fā)生的情況
  4. 適用于一些團隊中错邦,一些有經(jīng)驗的開發(fā)者可以關注復雜的領域模型,這些用到寫操作型宙,而另一些經(jīng)驗較少的開發(fā)者可以關注用戶界面上的讀模型撬呢。
  5. 對于系統(tǒng)在將來會隨著時間不段演化,有可能會包含不同版本的模型妆兑,或者業(yè)務規(guī)則經(jīng)常變化的系統(tǒng)
  6. 需要和其他系統(tǒng)整合倾芝,特別是需要和事件溯源Event Sourcing進行整合的系統(tǒng),這樣子系統(tǒng)的臨時異常不會影響整個系統(tǒng)的其他部分箭跳。

這里我只是把CQRS的初衷簡單說了一下,下一節(jié)我們會重點來講解 讀寫分離 的過程潭千,以及命令是怎么配合著 Validations 進行驗證的谱姓。

三、結(jié)語

今天暫時就寫到這里吧刨晴,通過今天的學習屉来,我們復習了第一系列中的依賴注入DI、DTO數(shù)據(jù)傳輸對象以及EFCore 的相關操作狈癞,重點說明了下茄靠,我們在DDD領域驅(qū)動設計中,如何在領域?qū)嶓w和值對象中蝶桶,通過Code First生成數(shù)據(jù)庫慨绳,并且強調(diào)了在領域設計中,一切要以領域模型為核心真竖。最后簡單引入了 CQRS 讀寫分離模式的簡單概念脐雪,我會在下一節(jié)繼續(xù)深入對其進行研究。

四恢共、GitHub & Gitee

https://github.com/anjoy8/ChristDDD

https://gitee.com/laozhangIsPhi/ChristDDD

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末战秋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子讨韭,更是在濱河造成了極大的恐慌脂信,老刑警劉巖癣蟋,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異狰闪,居然都是意外死亡疯搅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門尝哆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秉撇,“玉大人,你說我怎么就攤上這事秋泄∷龉荩” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵恒序,是天一觀的道長瘦麸。 經(jīng)常有香客問我,道長歧胁,這世上最難降的妖魔是什么滋饲? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮喊巍,結(jié)果婚禮上屠缭,老公的妹妹穿的比我還像新娘。我一直安慰自己崭参,他們只是感情好呵曹,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著何暮,像睡著了一般奄喂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上海洼,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天跨新,我揣著相機與錄音,去河邊找鬼坏逢。 笑死域帐,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的词疼。 我是一名探鬼主播俯树,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贰盗!你這毒婦竟也來了铛漓?” 一聲冷哼從身側(cè)響起儒飒,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤孤个,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后球化,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡瓦糟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年筒愚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菩浙。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡巢掺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出劲蜻,到底是詐尸還是另有隱情陆淀,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布先嬉,位于F島的核電站轧苫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏疫蔓。R本人自食惡果不足惜含懊,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衅胀。 院中可真熱鬧岔乔,春花似錦、人聲如沸滚躯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哀九。三九已至,卻和暖如春搅幅,著一層夾襖步出監(jiān)牢的瞬間阅束,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工茄唐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留息裸,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓沪编,卻偏偏與公主長得像呼盆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蚁廓,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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