【道德經】漫談實體、對象、DTO及AutoMapper的使用

寫在前面

  實體(Entity)、對象(Object)、DTO(Data Transfer Object)數據傳輸對象,老生常談話題,簡單的概念,換個角度你會發現更多的東西。我的拙見,勿喜請噴。html

實體和值對象

  在常規開發中(事務腳本),咱們所說的實體只是一些數據庫映射的字段,對象只不過是包含業務功能描述的集合而已,在DDD(領域驅動設計)中,實體(Entity)和值對象(Value Object)是基本元素之一,事務腳本中所說的對象概念大概就是領域模型中領域(Domain)的概念了,但並非只是業務功能描述的集合而已,不針對功能實現,而是針對業務協做完成的一種流程,DDD中的實體是一種領域對象,區別實體和值對象的方法就是判斷是否有惟一標示,而不是屬性,即便屬性徹底相同也多是兩個不一樣的對象。同時實體自己有狀態的,並且有本身的生命週期,實體自己會體現出相關的業務行爲,業務行爲會對實體屬性或狀態形成影響和改變。好比雙胞胎假設全部的屬性都同樣,仍然是兩個不一樣的人。從哲學角度講,這正是實體的原本含義。它是除了全部屬性以外還不足以表達的「那個」東西,不依賴其它而自存的東西。git

  如何區分實體和值對象,好比在城市社保系統中,參與社保人就是一個實體,在業務系統中,社保是一種概念,針對的是參與社保人,因此咱們要在業務中來區分參與社保人,人有可能同名同姓,因此不能用名字來區分,這裏的名字就是參與社保人的一個屬性,因此咱們用身份證號來區分參與社保人,這裏的身份證號就是參與社保人的惟一標示,用來講明:實體是什麼?實體是哪一個?github

  還有有一種業務場景是這樣,好比在全國社保統計系統中,統計各個城市的參與社保的比率,由於咱們只要知道這我的是否是參與了城市社保?而並不須要知道他是哪一個人,因此這裏面的參與社保人就是一個值對象,只是用來講明:值對象是什麼?數據庫

  能夠看出區分實體和值對象只是在特定的業務場景下,同一種特定對象可能會有不一樣的方式看待,這裏面就是一個邊界的問題,並且特定的業務場景中的實體是有本身的狀態和生命週期,這和值對象也有明顯的區分。編程

實體和對象

道可道,很是道。名可名,很是名。 無名天地之始,有名萬物之母。故常無慾以觀其妙; 常有欲以觀其徼(jiào)。 此二者同出而異名,同謂之玄,玄之又玄,衆妙之門。    --《道德經》安全

  上面這段話出自老子的道德經的開篇,簡單說下前兩段話的意思:道彷佛有具體的定義,但總不是咱們所想象出的定義,名取出了一個名,但不必定咱們會一直使用。下面幾段就是對有(名)和無(道)的辯證關係,最後得出:「無」是天地的來處,「有」是衍生萬物的結果,這二者之間,同出爲意義不同,一樣好似玄妙務必,無窮無盡,切是研究一切的門經。數據結構

  爲何會引用道德經?其實在我看來,老子不作軟件開發真是太虧了(哈哈),什麼是實體和對象?可能每一個人都有本身的解讀,就像上面討論的實體和值對象,其實某種意義上來講應該是領域對象和對象屬性,這裏說對象屬性也並非準確,就好比項目中咱們使用的屬性字典,並非任何一種對象的屬性,只是一個特定的值,不依附於任何對象。實體雖然稱做實體,實際上是一個對象,值對象雖然稱做對象,但其實只是一個特定值,意義就像」道可道,很是道,名可名,很是名「同樣,app

  老子探討的有無關係,其實在軟件編程中就是實體和對象的關係,「有」能夠看作是「實體」,「無」能夠看作是「對象」,無衍生出有,有體現出無,就像實體和對象之間的關係,實體是否是對象?對象是否是實體?實體和對象到底什麼?這其實只是存在一個邊界問題。正如DDD中,實體便是領域對象,對象便是實體模型,咱們生活中經常討論:「是先有的雞蛋?仍是先有的雞?」最後都沒有得出一個準確的結果,若是老子來回答這個問題,就六個字:「雞生蛋,蛋生雞」,至於解釋,老子揮一揮衣袖,騎上青牛遠去-「本身去琢磨吧」。分佈式

故常無慾以觀其妙,常有欲以觀其徼

故常無慾以觀其妙,常有欲以觀其徼(jiào)。    --《道德經》工具

  這段話我以爲是道德經開篇的精髓,你可能從字面上能夠體會獲得一些內容,這其實一種態度,一種生活態度,一種編程態度。

  「故常無慾以觀其妙」,這句話在咱們的現實生活中能夠很好的去解讀,世間萬物是如此的大,咱們還有不少的事物沒有去認知,因此咱們就會抱着征服的慾望去探尋,看到美好的事物就想去掠奪佔有,就像當你偶然發現一朵很是漂亮的鮮花,不少人不會停留下去欣賞它,而是去採摘它,而後據爲己有,生活中的例子比比皆是,就像文章會犯錯同樣。

  老子的所提倡的就是咱們應該保持「無慾」的心態去看待事件萬物,去觀察,去體會它的玄妙,上面所說的掠奪、佔有,就不是「觀」所蘊含的意義了。咱們在作項目中,項目前期需求尚未肯定好就去開發,到最後弄得進退兩難,在DDD中,業務需求是很重要的一環,咱們應該花更多的時間去了解它、體會它、肯定它,而不是想固然的瞭解後就去開發項目,這也是建模專家所必備的基本條件。

  「常有欲以觀其徼」,這句話的中的精髓就一個字「」,徼翻譯爲邊界的意思,「有欲」在現實生活中能夠指一些有名望、有地位、有財富的人,這些人當擁有了一些常人所不能擁有的東西后,並不懂得收斂和知足,反而使本身的慾望心更大,想獲得更大的知足,獲得後還想獲得,沒有一個界限,最後的下場通常都是很慘,就像和珅貪得無厭同樣。

  老子所提倡的就是咱們在「有欲」以後,要觀察、體會一個界限,要使本身的「欲」控制在這個界限中,水滿則溢就是這個道理。就像在DDD中,實體和值對象邊界的肯定同樣。

初始實體和演化實體

含德之厚,比於赤子。
專氣致柔,能如嬰兒乎?
爲天下豁,常德不離,復歸於嬰兒。
--《道德經》

  老子在道德經中屢次提到有關嬰兒的話題,就像上面幾句,老是拿一些事物和嬰兒進行比較,難道說老子喜歡嬰兒?準確的應該說,老子推崇嬰兒的那種狀態,何種狀態?無慾無求、迴歸天然、保持天性。。。

  人的進化史進行了千百萬年,從最初的簡單生存原則,發展到如今複雜的人世關係,越進化越複雜,致使咱們如今越活越累。新出生的嬰兒沒有任何外界的摻雜,是如此的純淨,正如一碗清水通常,但隨着成長,慢慢的接觸外界事物,清水也會被染成五光十色,而失去了原本固有的一些東西,這也就是爲何老子推崇嬰兒的緣由。有時候咱們離開喧囂的城市,置身於寧靜的山坳,你會發現身心是如此的舒暢,其實這纔是咱們所固有的東西,只是處在亂世中,把那一抹清明掩蓋罷了。

  什麼是初始實體和演化實體?這只不過是我本身定義的,這裏面的實體也能夠看作是對象,只是在DDD中稱做爲實體,如上面所說,嬰兒就像初始實體同樣,長大後的咱們就是演化實體,初始實體只有一種狀態,也就是一種原始狀態或者稱做是無狀態,特定的場景下初始實體只有一個抽象出來的對象,可是演化實體有不少種,但都是從初始實體演化出來的,因此稱爲演化實體,有直接的關係也有間接的關係,若是把嬰兒看作是初始實體,長大後的咱們是演化實體,但演化實體並不僅有長大後咱們,汽車、成績單、衣服等等一些與咱們相關的事物均可以稱爲演化實體,但長大後的咱們只是和初始實體有直接關係,其餘的和初始實體都是間接關係,這個特定的場景就是人類進化史。

  固然有人看到這可能有些想法,認爲你這說的什麼亂七八糟的東西,沒有一點實際的意義。個人意思並非說明初始實體和演化實體是個什麼東西,而是說在咱們作項目的過程當中要找到那個「初始實體」,好比物流業務系統場景,在這個系統中哪一個是初始實體?調度?帳單?掃描?都不是,準確的說應該是運單,由於全部的業務操做都是圍繞它來展開,或者是由它演生而來,雖然初始實體咱們找到了,可是要仔細的揣摩它,肯定是出生的嬰兒仍是長大後的咱們?

  聚合(Aggregate)和聚合根(Aggregate Root)是DDD中的重要概念,什麼是聚合?它經過定義對象之間清晰的所屬關係和邊界來實現領域模型的內聚,並避免了錯綜複雜的難以維護的對象關係網的造成,聚合定義了一組具備內聚關係的相關對象的集合,咱們把聚合看做是一個修改數據的單元。若是把人類社會看作是領域模型,聚合看作是一個國家,國家中的人是一個實體,那這個國家的人就是一個聚合根,可是每一個國家的人都是人,只不過膚色、語言、習俗會有些不一樣,能夠把人看作這個場景中的初始實體,也就是初始聚合根,而不是國家的人。

代碼中的DTO

  DTO(Data Transfer Object)數據傳輸對象,注意關鍵字「數據」兩個字,並非對象傳輸對象(Object Transfer Object),因此只是傳輸數據,並不包含領域業務處理,雖然用途只是傳輸數據,但自己其實也是對象,完成與領域對象之間的轉換,就像上面說的值對象同樣,某種意義上DTO能夠看作是值對象的集合,只不過是和領域對象之間的映射,不包含任何的業務邏輯。

  爲何要使用DTO?主要緣由是隔離Domain Model,使改動領域模型而不影響UI,還有就是保持領域模型的安全,不暴露業務邏輯。還有就是在分佈式模式下,不一樣的場景使用相同的數據結構有不一樣的需求,而咱們又不得不作一些數據轉化,這是很繁瑣的,若是咱們在項目初期,作DTO的分析,這樣咱們就會省不少的事,並且還不會影響整個項目的業務流程,也方便之後對項目進行擴展。

  下面咱們虛擬一個簡單「文章」領域模型:

 1         public class Article : IEntity 
 2         {
 3             public Article()
 4             {
 5                 this.Id = Guid.NewGuid();
 6             }
 7             public string Title { get; set; }
 8             public string Content { get; set; }
 9             public string Author { get; set; }
10             public DateTime PostTime { get; set; }
11             public string Remark { get; set; }
12             #region IEntity Members
13             /// <summary>       
14             /// 讀取或設置文章的編號    
15             /// </summary>       
16             public Guid Id { get; set; }
17             #endregion
18         }    

  文章領域模型對應DTO:

 1         public class ArticleDTO
 2         {
 3             /// <summary>
 4             /// 文章惟一編碼
 5             /// </summary>
 6             public string ArticleID { get; set; }
 7             /// <summary>
 8             /// 文章標題
 9             /// </summary>
10             public string Title { get; set; }
11             /// <summary>
12             /// 文章摘要
13             /// </summary>
14             public string Summary { get; set; }
15             /// <summary>
16             /// 文章內容
17             /// </summary>
18             public string Content { get; set; }
19             /// <summary>
20             /// 文章做者
21             /// </summary>
22             public string Author { get; set; }
23             /// <summary>
24             /// 文章發表日期
25             /// </summary>
26             public DateTime PostTime { get; set; }
27             /// <summary>
28             /// 文章發表年份
29             /// </summary>
30             public int PostYear { get; set; }
31             /// <summary>
32             /// 文章備註
33             /// </summary>
34             public string Remark { get; set; }
35         }

AutoMapper實體轉換

  從上面ArticleDTO中能夠看到多了兩個屬性:Summary(文章摘要)和PostYear(文章發表年份),這就是咱們在特定的業務場景中須要的,並不會影響到領域模型,如何實現領域模型和DTO之間的轉換?咱們可使用AutoMapper能夠很方便的對他們進行轉換,一個強大的Object-Object Mapping工具。

  工具-庫程序包管理器-程序包管理控制平臺,輸入「Install-Package AutoMapper」命令,就能夠把AutoMapper添加到項目中,有關AutoMapper相關文檔能夠參考:https://github.com/AutoMapper/AutoMapper/wiki,這邊簡單演示下Article到ArticleDTO之間的轉換。

 1         static void Main(string[] args)
 2         {
 3             Article article = new Article
 4             {
 5                 Title = "漫談實體、對象、DTO及AutoMapper的使用",
 6                 Content = "實體(Entity)、對象(Object)、DTO(Data Transfer Object)數據傳輸對象,老生常談話題,簡單的概念,換個角度你會發現更多的東西。我的拙見,勿喜請噴。",
 7                 Author = "xishuai",
 8                 PostTime = DateTime.Now,
 9                 Remark = "文章備註"
10             };
11             //配置AutoMapper 
12             AutoMapper.Mapper.Initialize(cfg =>
13             {
14                 cfg.CreateMap<Article, ArticleDTO>()//建立映射
15                .ForMember(dest => dest.ArticleID, opt => opt.MapFrom(src => src.Id))//指定映射規則
16                .ForMember(dest => dest.Summary, opt => opt.MapFrom(src => src.Content.Substring(0, 10)))//指定映射規則
17                .ForMember(dest => dest.PostYear, opt => opt.MapFrom(src => src.PostTime.Year))//指定映射規則
18                .ForMember(dest => dest.Remark, opt => opt.Ignore());//指定映射規則 忽視沒有的屬性
19             });
20 
21             //調用映射
22             ArticleDTO form = AutoMapper.Mapper.Map<Article, ArticleDTO>(article);
23         }

  轉換效果:

後記

  其實這篇文章本來只是想簡單寫下DTO及AutoMapper的用法,可是不知怎的寫着寫着就寫跑偏了,如今回過頭看也不知道本身寫了些什麼東西,正如張三丰教張無忌太極劍法,問他記得了多少,最後什麼都沒記得。

  隨着DDD(領域驅動設計)的學習和對道德經的感悟,就會發覺:此二者同出而異名,同謂之玄,玄之又玄,衆妙之門。

  若是你以爲本篇文章對你有所幫助,請點擊右下部「推薦」,^_^

  參考資料:

相關文章
相關標籤/搜索