[.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店...
一、前言
在前面專題一中,我已經(jīng)介紹了我寫(xiě)這系列文章的初衷了。由于dax.net中的DDD框架和Byteart Retail案例并沒(méi)有對(duì)其形成過(guò)程做一步步分析,而是把整個(gè)DDD的實(shí)現(xiàn)案例展現(xiàn)給我們,這對(duì)于一些剛剛接觸領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的朋友可能會(huì)非常迷茫,從而覺(jué)得領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)很難,很復(fù)雜,因?yàn)閷W(xué)習(xí)中要消化一個(gè)整個(gè)案例的知識(shí),這樣未免很多人消化不了就打退堂鼓,就不繼續(xù)研究下去了,所以這樣也不利于DDD的推廣。然而本系列可以說(shuō)是剛接觸領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)朋友的福音,本系列將結(jié)合領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的思想來(lái)一步步構(gòu)建一個(gè)網(wǎng)上書(shū)店,從而讓大家學(xué)習(xí)DDD不再枯燥和可以看到一個(gè)DDD案例的形成歷程。最后,再DDD案例完成之后,將從中抽取一個(gè)領(lǐng)域驅(qū)動(dòng)的框架,從而大家也可以看到一個(gè)DDD框架的形成歷程,這樣就不至于一下子消化一整個(gè)框架和案例的知識(shí),而是一步步消化。接下來(lái),該專題將介紹的是:結(jié)合領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的SOA架構(gòu)來(lái)構(gòu)建網(wǎng)上書(shū)店,本專題中并沒(méi)有完成網(wǎng)上書(shū)店的所有頁(yè)面和覆蓋DDD中的所有內(nèi)容,而只是一部分,后面的專題將會(huì)在本專題的網(wǎng)上書(shū)店進(jìn)行一步步完善,通過(guò)一步步引入DDD的內(nèi)容和重構(gòu)來(lái)完成整個(gè)項(xiàng)目。
二、DDD分層架構(gòu)
從概念上說(shuō),領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)架構(gòu)主要分為四層,分別為:基礎(chǔ)設(shè)施層、領(lǐng)域?qū)印?yīng)用層和表現(xiàn)層。
- 基礎(chǔ)結(jié)構(gòu)層:該層專為其他各層提供各項(xiàng)通用技術(shù)框架支持。像一些配置文件處理、緩存處理,事務(wù)處理等都可以放在這里。
- 領(lǐng)域?qū)?#xff1a;簡(jiǎn)單地說(shuō)就是業(yè)務(wù)所涉及的領(lǐng)域?qū)ο?#xff08;包括實(shí)體、值對(duì)象)、領(lǐng)域服務(wù)等。該層就是所謂的領(lǐng)域模型了,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)提倡是富領(lǐng)域模型,富領(lǐng)域模型指的是:盡量將業(yè)務(wù)邏輯放在歸屬于它的領(lǐng)域?qū)ο笾小6暗娜龑蛹軜?gòu)中的領(lǐng)域模型都是貧血領(lǐng)域模型,因?yàn)樵谌龑又械念I(lǐng)域模型只包含業(yè)務(wù)屬性,而不包含任何業(yè)務(wù)邏輯。本專題的網(wǎng)上書(shū)店領(lǐng)域模型目前還沒(méi)有包含任何業(yè)務(wù)邏輯,在后期將會(huì)完善。
實(shí)體可以認(rèn)為對(duì)應(yīng)于數(shù)據(jù)庫(kù)的表,而值對(duì)象一般定義在實(shí)體類中。
- 應(yīng)用層:該層不包含任何領(lǐng)域邏輯,它主要用來(lái)對(duì)任務(wù)進(jìn)行協(xié)調(diào),它構(gòu)建了表現(xiàn)層和領(lǐng)域?qū)拥臉蛄骸OA架構(gòu)就是在該層進(jìn)行實(shí)現(xiàn)的。
- 表現(xiàn)層:指的是用戶界面,例如Asp.net mvc網(wǎng)站,WPF、Winform和控制臺(tái)等。它主要用來(lái)想用戶展現(xiàn)內(nèi)容。
下面用一個(gè)圖來(lái)形象展示DDD的分層架構(gòu):
本系列介紹的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)戰(zhàn),則自然少了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)分層架構(gòu)的實(shí)現(xiàn)了,上面簡(jiǎn)單介紹了領(lǐng)域驅(qū)動(dòng)的分層架構(gòu),接下來(lái)將詳細(xì)介紹在網(wǎng)上書(shū)店中各層是如何去實(shí)現(xiàn)的。
三、網(wǎng)上書(shū)店領(lǐng)域模型層的實(shí)現(xiàn)
? 在應(yīng)用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的思想來(lái)構(gòu)建一個(gè)項(xiàng)目,則第一步就是了解需求,明白項(xiàng)目的業(yè)務(wù)邏輯,了解清楚業(yè)務(wù)邏輯后,則把業(yè)務(wù)邏輯抽象成領(lǐng)域?qū)ο?#xff0c;領(lǐng)域?qū)ο笏旁诘奈恢靡簿褪穷I(lǐng)域模型層了。該專題介紹的網(wǎng)上書(shū)店主要完成了商品所涉及的頁(yè)面,包括商品首頁(yè),單個(gè)商品的詳細(xì)信息等。所以這里涉及的領(lǐng)域?qū)嶓w包括2個(gè),一個(gè)是商品類,另外一個(gè)就是類別類,因?yàn)樵谏唐肥醉?yè)中,需要顯示所有商品的類別。在給出領(lǐng)域?qū)ο蟮膶?shí)現(xiàn)之前,這里需要介紹領(lǐng)域?qū)又兴婕暗膸讉€(gè)概念。
- 聚合根:聚合根也是實(shí)體,但與實(shí)體不同的是,聚合根是由實(shí)體和值對(duì)象組成的系統(tǒng)邊界對(duì)象。舉個(gè)例子來(lái)說(shuō),例如訂單和訂單項(xiàng),根據(jù)業(yè)務(wù)邏輯,我們需要跟蹤訂單和訂單項(xiàng)的狀態(tài),所以設(shè)計(jì)它們都為實(shí)體,但只有訂單才是聚合根對(duì)象,而訂單項(xiàng)不是,因?yàn)橛唵雾?xiàng)只有在訂單中才有意義,意思就是說(shuō):用戶不能直接看到訂單項(xiàng),而是先查詢到訂單,然后再看到該訂單下的訂單項(xiàng)。所以聚合根可以理解為用戶直接操作的對(duì)象。在這里商品類和類別類都是一個(gè)聚合根。
根據(jù)面向接口編程原則,我們?cè)陬I(lǐng)域模型中應(yīng)該定義一個(gè)實(shí)體接口和聚合根接口,而因?yàn)榫酆细彩菍儆趯?shí)體,所以聚合根接口繼承于實(shí)體接口,而商品類和類別類都是聚合根,所以它們都實(shí)現(xiàn)聚合根接口。如果像訂單項(xiàng)只是實(shí)體不是聚合根的類則實(shí)現(xiàn)實(shí)體接口。有了上面的分析,則領(lǐng)域模型層的實(shí)現(xiàn)也就自然出來(lái)了,下面是領(lǐng)域?qū)ο蟮木唧w實(shí)現(xiàn):
// 領(lǐng)域?qū)嶓w接口public interface IEntity{// 當(dāng)前領(lǐng)域?qū)嶓w的全局唯一標(biāo)識(shí)Guid Id { get; }} // 聚合根接口,繼承于該接口的對(duì)象是外部唯一操作的對(duì)象public interface IAggregateRoot : IEntity{} // 商品類public class Product : AggregateRoot{public string Name { get; set; }public string Description { get; set; }public decimal UnitPrice { get; set; }public string ImageUrl { get; set; }public bool IsNew{ get; set; }public override string ToString(){return Name;}} // 類別類public class Category : AggregateRoot{public string Name { get; set; }public string Description { get; set; }public override string ToString(){return this.Name;}}另外,領(lǐng)域?qū)映藢?shí)現(xiàn)領(lǐng)域?qū)ο笸?#xff0c;還需要定義倉(cāng)儲(chǔ)接口,而倉(cāng)儲(chǔ)層則是對(duì)倉(cāng)儲(chǔ)接口的實(shí)現(xiàn)。倉(cāng)儲(chǔ)可以理解為在內(nèi)存中維護(hù)一系列聚合根的集合,而聚合根不可能一直存在于內(nèi)存中,當(dāng)它不活動(dòng)時(shí)會(huì)被持久化到數(shù)據(jù)中。而倉(cāng)儲(chǔ)層完成的任務(wù)是持久化聚合根對(duì)象到數(shù)據(jù)或從數(shù)據(jù)庫(kù)中查詢存儲(chǔ)的對(duì)象來(lái)重新創(chuàng)建領(lǐng)域?qū)ο蟆?/span>
倉(cāng)儲(chǔ)層有幾個(gè)需要明確的概念:
介紹完倉(cāng)儲(chǔ)之后,接下來(lái)就在領(lǐng)域?qū)又卸x倉(cāng)儲(chǔ)接口,因?yàn)楸緦n}中涉及到2個(gè)聚合根,則自然需要實(shí)現(xiàn)2個(gè)倉(cāng)儲(chǔ)接口。根據(jù)面向接口編程原則,我們讓這2個(gè)倉(cāng)儲(chǔ)接口都實(shí)現(xiàn)與一個(gè)公共的接口:IRepository接口。另外倉(cāng)儲(chǔ)接口還需要定義一個(gè)倉(cāng)儲(chǔ)上下接口,因?yàn)樵贓ntity Framework中有一個(gè)DbContex類,所以我們需要定義一個(gè)EntityFramework上下文對(duì)象來(lái)對(duì)DbContex進(jìn)行包裝。也就自然有了倉(cāng)儲(chǔ)上下文接口了。經(jīng)過(guò)上面的分析,倉(cāng)儲(chǔ)接口的實(shí)現(xiàn)也就一目了然了。
// 倉(cāng)儲(chǔ)接口public interface IRepository<TAggregateRoot> where TAggregateRoot :class, IAggregateRoot{void Add(TAggregateRoot aggregateRoot);IEnumerable<TAggregateRoot> GetAll();// 根據(jù)聚合根的ID值,從倉(cāng)儲(chǔ)中讀取聚合根 TAggregateRoot GetByKey(Guid key);} public interface IProductRepository : IRepository<Product>{IEnumerable<Product> GetNewProducts(int count = 0);} public interface IProductRepository : IRepository<Product>{IEnumerable<Product> GetNewProducts(int count = 0);} // 倉(cāng)儲(chǔ)上下文接口public interface IRepositoryContext{}這樣我們就完成了領(lǐng)域?qū)拥拇罱?#xff0c;接下面,我們就需要對(duì)領(lǐng)域?qū)又卸x的倉(cāng)儲(chǔ)接口進(jìn)行實(shí)現(xiàn)了。我這里將倉(cāng)儲(chǔ)接口的實(shí)現(xiàn)單獨(dú)弄出了一個(gè)層,當(dāng)然你也可以放在基礎(chǔ)設(shè)施層中的Repositories文件夾中。不過(guò)我看很多人都直接拎出來(lái)的。我這里也是直接作為一個(gè)層。
四、網(wǎng)上書(shū)店Repository(倉(cāng)儲(chǔ))層的實(shí)現(xiàn)
? 定義完倉(cāng)儲(chǔ)接口之后,接下來(lái)就是在倉(cāng)儲(chǔ)層實(shí)現(xiàn)這些接口,完成領(lǐng)域?qū)ο蟮男蛄谢J紫仁钱a(chǎn)品倉(cāng)儲(chǔ)的實(shí)現(xiàn):
// 商品倉(cāng)儲(chǔ)的實(shí)現(xiàn)public class ProductRepository : IProductRepository{#region Private Fieldsprivate readonly IEntityFrameworkRepositoryContext _efContext;#endregion #region Public Propertiespublic IEntityFrameworkRepositoryContext EfContext{get { return this._efContext; }}#endregion #region Ctorpublic ProductRepository(IRepositoryContext context){var efContext = context as IEntityFrameworkRepositoryContext;if (efContext != null)this._efContext = efContext;}#endregion public IEnumerable<Product> GetNewProducts(int count = 0){var ctx = this.EfContext.DbContex as OnlineStoreDbContext;if (ctx == null)return null;var query = from p in ctx.Productswhere p.IsNew == trueselect p;if (count == 0)return query.ToList();elsereturn query.Take(count).ToList();}public void Add(Product aggregateRoot){throw new NotImplementedException();}public IEnumerable<Product> GetAll(){var ctx = this.EfContext.DbContex as OnlineStoreDbContext;if (ctx == null)return null;var query = from p in ctx.Productsselect p;return query.ToList();}public Product GetByKey(Guid key){return EfContext.DbContex.Products.First(p => p.Id == key);}} View Code接下來(lái)是類別倉(cāng)儲(chǔ)的實(shí)現(xiàn):
// 類別倉(cāng)儲(chǔ)的實(shí)現(xiàn)public class CategoryRepository :ICategoryRepository{#region Private Fieldsprivate readonly IEntityFrameworkRepositoryContext _efContext;public CategoryRepository(IRepositoryContext context){var efContext = context as IEntityFrameworkRepositoryContext;if (efContext != null)this._efContext = efContext;}#endregion#region Public Propertiespublic IEntityFrameworkRepositoryContext EfContext{get { return this._efContext; }}#endregion public void Add(Category aggregateRoot){throw new System.NotImplementedException();}public IEnumerable<Category> GetAll(){var ctx = this.EfContext.DbContex as OnlineStoreDbContext;if (ctx == null)return null;var query = from c in ctx.Categoriesselect c;return query.ToList();}public Category GetByKey(Guid key){return this.EfContext.DbContex.Categories.First(c => c.Id == key);}} View Code由于后期除了實(shí)現(xiàn)基于EF倉(cāng)儲(chǔ)的實(shí)現(xiàn)外,還想實(shí)現(xiàn)基于MongoDb倉(cāng)儲(chǔ)的實(shí)現(xiàn),所以在倉(cāng)儲(chǔ)層中創(chuàng)建了一個(gè)EntityFramework的文件夾,并定義了一個(gè)IEntityFrameworkRepositoryContext接口來(lái)繼承于IRepositoryContext接口,由于EF中持久化數(shù)據(jù)主要是由DbContext對(duì)象來(lái)完成了,為了有自己框架模型,我在這里定義了OnlineStoreDbContext來(lái)繼承DbContext,從而用OnlineStoreDbContext來(lái)對(duì)DbContext進(jìn)行了一次包裝。經(jīng)過(guò)上面的分析之后,接下來(lái)對(duì)于實(shí)現(xiàn)也就一目了然了。首先是OnlineStoreDbContext類的實(shí)現(xiàn):
public sealed class OnlineStoreDbContext : DbContext{#region Ctorpublic OnlineStoreDbContext(): base("OnlineStore"){this.Configuration.AutoDetectChangesEnabled = true;this.Configuration.LazyLoadingEnabled = true;}#endregion #region Public Propertiespublic DbSet<Product> Products { get { return this.Set<Product>(); }}public DbSet<Category> Categories{get { return this.Set<Category>(); }}// 后面會(huì)繼續(xù)添加屬性,針對(duì)每個(gè)聚合根都會(huì)定義一個(gè)DbSet的屬性// ...#endregion}接下來(lái)就是IEntityFrameworkRepositoryContext接口的定義以及它的實(shí)現(xiàn)了。具體代碼如下所示:
public interface IEntityFrameworkRepositoryContext : IRepositoryContext{#region PropertiesOnlineStoreDbContext DbContex { get; }#endregion } public class EntityFrameworkRepositoryContext : IEntityFrameworkRepositoryContext{// 引用我們定義的OnlineStoreDbContext類對(duì)象public OnlineStoreDbContext DbContex{get { return new OnlineStoreDbContext(); }}}這樣,我們的倉(cāng)儲(chǔ)層也就完成了。接下來(lái)就是應(yīng)用層的實(shí)現(xiàn)。
五、網(wǎng)上書(shū)店應(yīng)用層的實(shí)現(xiàn)
? 應(yīng)用層應(yīng)用了面向服務(wù)結(jié)構(gòu)進(jìn)行實(shí)現(xiàn),采用了微軟面向服務(wù)的實(shí)現(xiàn)WCF來(lái)完成的。網(wǎng)上書(shū)店的整個(gè)架構(gòu)完全遵循著領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的分層架構(gòu),用戶通過(guò)UI層(這里實(shí)現(xiàn)的是Web頁(yè)面)來(lái)進(jìn)行操作,然后UI層調(diào)用應(yīng)用層來(lái)把服務(wù)進(jìn)行分發(fā),通過(guò)調(diào)用基礎(chǔ)設(shè)施層中倉(cāng)儲(chǔ)實(shí)現(xiàn)來(lái)對(duì)領(lǐng)域?qū)ο筮M(jìn)行持久化和重建。這里應(yīng)用層主要采用WCF來(lái)實(shí)現(xiàn)的,其中引用了倉(cāng)儲(chǔ)接口。針對(duì)服務(wù)而言,首先就需要定義服務(wù)契約了,這里我把服務(wù)契約的定義單獨(dú)放在了一個(gè)服務(wù)契約層,當(dāng)然你也可以在應(yīng)用層中創(chuàng)建一個(gè)服務(wù)契約文件夾。首先就去看看服務(wù)契約的定義:
// 商品服務(wù)契約的定義[ServiceContract(Namespace="")]public interface IProductService{#region Methods// 獲得所有商品的契約方法 [OperationContract]IEnumerable<Product> GetProducts();// 獲得新上市的商品的契約方法 [OperationContract]IEnumerable<Product> GetNewProducts(int count);// 獲得所有類別的契約方法 [OperationContract]IEnumerable<Category> GetCategories();// 根據(jù)商品Id來(lái)獲得商品的契約方法 [OperationContract]Product GetProductById(Guid id);#endregion} View Code接下來(lái)就是服務(wù)契約的實(shí)現(xiàn),服務(wù)契約的實(shí)現(xiàn)我放在應(yīng)用層中,具體的實(shí)現(xiàn)代碼如下所示:
// 商品服務(wù)的實(shí)現(xiàn)public class ProductServiceImp : IProductService{#region Private Fieldsprivate readonly IProductRepository _productRepository;private readonly ICategoryRepository _categoryRepository;#endregion #region Ctorpublic ProductServiceImp(IProductRepository productRepository, ICategoryRepository categoryRepository){_categoryRepository = categoryRepository;_productRepository = productRepository;}#endregion#region IProductService Memberspublic IEnumerable<Product> GetProducts(){return _productRepository.GetAll();}public IEnumerable<Product> GetNewProducts(int count){return _productRepository.GetNewProducts(count);}public IEnumerable<Category> GetCategories(){return _categoryRepository.GetAll();}public Product GetProductById(Guid id){var product = _productRepository.GetByKey(id);return product;}#endregion } View Code最后就是創(chuàng)建WCF服務(wù)來(lái)調(diào)用服務(wù)契約實(shí)現(xiàn)了。創(chuàng)建一個(gè)后綴為.svc的WCF服務(wù)文件,WCF服務(wù)的具體實(shí)現(xiàn)如下所示:
// 商品WCF服務(wù)public class ProductService : IProductService{// 引用商品服務(wù)接口private readonly IProductService _productService;public ProductService(){_productService = ServiceLocator.Instance.GetService<IProductService>();}public IEnumerable<Product> GetProducts(){return _productService.GetProducts();}public IEnumerable<Product> GetNewProducts(int count){return _productService.GetNewProducts(count);}public IEnumerable<Category> GetCategories(){return _productService.GetCategories();}public Product GetProductById(Guid id){return _productService.GetProductById(id);}}到這里我們就完成了應(yīng)用層面向服務(wù)架構(gòu)的實(shí)現(xiàn)了。從商品的WCF服務(wù)實(shí)現(xiàn)可以看到,我們有一個(gè)ServiceLocator的類。這個(gè)類的實(shí)現(xiàn)采用服務(wù)定位器模式,關(guān)于該模式的介紹可以參考dax.net的服務(wù)定位器模式的介紹。該類的作用就是調(diào)用方具體的實(shí)例,簡(jiǎn)單地說(shuō)就是通過(guò)服務(wù)接口定義具體服務(wù)接口的實(shí)現(xiàn),將該實(shí)現(xiàn)返回給調(diào)用者的。這個(gè)類我這里放在了基礎(chǔ)設(shè)施層來(lái)實(shí)現(xiàn)。目前基礎(chǔ)設(shè)施層只有這一個(gè)類的實(shí)現(xiàn),后期會(huì)繼續(xù)添加其他功能,例如緩存功能的支持。
另外,在這里使用了Unity依賴注入容器來(lái)對(duì)接口進(jìn)行注入。主要的配置文件如下所示:
<configuration><configSections><section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework"/><section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/></configSections><!-- Entity Framework 配置信息--><entityFramework><defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework"><parameters><parameter value="Data Source=(LocalDb)\v11.0; Initial Catalog=OnlineStore; Integrated Security=True; Connect Timeout=120; MultipleActiveResultSets=True; AttachDBFilename=|DataDirectory|\OnlineStore.mdf"/></parameters></defaultConnectionFactory></entityFramework><!--Unity的配置信息--><unity xmlns="http://schemas.microsoft.com/practices/2010/unity"><container><!--倉(cāng)儲(chǔ)接口的注冊(cè)--><register type="OnlineStore.Domain.Repositories.IRepositoryContext, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.EntityFrameworkRepositoryContext, OnlineStore.Repositories"/><register type="OnlineStore.Domain.Repositories.IProductRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.ProductRepository, OnlineStore.Repositories"/><register type="OnlineStore.Domain.Repositories.ICategoryRepository, OnlineStore.Domain" mapTo="OnlineStore.Repositories.EntityFramework.CategoryRepository, OnlineStore.Repositories"/><!--應(yīng)用服務(wù)的注冊(cè)--><register type="OnlineStore.ServiceContracts.IProductService, OnlineStore.ServiceContracts" mapTo="OnlineStore.Application.ServiceImplementations.ProductServiceImp, OnlineStore.Application"/></container></unity><appSettings><add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/></appSettings><system.web><compilation debug="true" targetFramework="4.5"/><httpRuntime targetFramework="4.5.1"/></system.web><!--WCF 服務(wù)的配置信息--><system.serviceModel><behaviors><serviceBehaviors><behavior name=""><serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/><serviceDebug includeExceptionDetailInFaults="true"/></behavior></serviceBehaviors></behaviors><services><service name="OnlineStore.Application.ServiceImplementations.ProductServiceImp" behaviorConfiguration=""><endpoint address="" binding="wsHttpBinding" contract="OnlineStore.ServiceContracts.IProductService"/><!--<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />--></service></services><serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/></system.serviceModel><system.webServer><modules runAllManagedModulesForAllRequests="true"/><!--To browse web app root directory during debugging, set the value below to true.Set to false before deployment to avoid disclosing web app folder information.--><directoryBrowse enabled="true"/></system.webServer> </configuration> View Code六、基礎(chǔ)設(shè)施層的實(shí)現(xiàn)
基礎(chǔ)設(shè)施層在本專題中只包含了服務(wù)定位器的實(shí)現(xiàn),后期會(huì)繼續(xù)添加對(duì)其他功能的支持,ServiceLocator類的具體實(shí)現(xiàn)如下所示:
// 服務(wù)定位器的實(shí)現(xiàn)public class ServiceLocator : IServiceProvider{private readonly IUnityContainer _container;private static ServiceLocator _instance = new ServiceLocator();private ServiceLocator(){_container = new UnityContainer();_container.LoadConfiguration();}public static ServiceLocator Instance{get { return _instance; }}#region Public Methodspublic T GetService<T>(){return _container.Resolve<T>();}public IEnumerable<T> ResolveAll<T>(){return _container.ResolveAll<T>();}public T GetService<T>(object overridedArguments){var overrides = GetParameterOverrides(overridedArguments);return _container.Resolve<T>(overrides.ToArray());}public object GetService(Type serviceType, object overridedArguments){var overrides = GetParameterOverrides(overridedArguments);return _container.Resolve(serviceType, overrides.ToArray());}#endregion #region Private Methodsprivate IEnumerable<ParameterOverride> GetParameterOverrides(object overridedArguments){var overrides = new List<ParameterOverride>();var argumentsType = overridedArguments.GetType();argumentsType.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList().ForEach(property =>{var propertyValue = property.GetValue(overridedArguments, null);var propertyName = property.Name;overrides.Add(new ParameterOverride(propertyName, propertyValue));});return overrides;}#endregion #region IServiceProvider Memberspublic object GetService(Type serviceType){return _container.Resolve(serviceType);}#endregion } View Code七、UI層的實(shí)現(xiàn)
根據(jù)領(lǐng)域驅(qū)動(dòng)的分層架構(gòu),接下來(lái)自然就是UI層的實(shí)現(xiàn)了,這里UI層的實(shí)現(xiàn)采用Asp.net MVC 技術(shù)來(lái)實(shí)現(xiàn)的。UI層主要包括商品首頁(yè)的實(shí)現(xiàn),和詳細(xì)商品的實(shí)現(xiàn),另外還有一些附加頁(yè)面的實(shí)現(xiàn),例如,關(guān)于頁(yè)面,聯(lián)系我們頁(yè)面等。關(guān)于UI層的實(shí)現(xiàn),這里就不一一貼出代碼實(shí)現(xiàn)了,大家可以在最后的源碼鏈接自行下載查看。
八、系統(tǒng)總體架構(gòu)
經(jīng)過(guò)上面的所有步驟,本專題中的網(wǎng)上書(shū)店構(gòu)建工作就基本完成了,接下來(lái)我們來(lái)看看網(wǎng)上書(shū)店的總體架構(gòu)圖(這里架構(gòu)圖直接借鑒了dax.net的圖了,因?yàn)楸鞠盗形恼乱彩菍?duì)其Byteart Retail項(xiàng)目的剖析過(guò)程):
最后附上整個(gè)解決方案的結(jié)構(gòu)圖:
九、網(wǎng)上書(shū)店運(yùn)行效果
實(shí)現(xiàn)完之后,大家是不是都已經(jīng)迫不及待地想看到網(wǎng)上書(shū)店的運(yùn)行效果呢?下面就為大家來(lái)揭曉,目前網(wǎng)上書(shū)店主要包括2個(gè)頁(yè)面,一個(gè)是商品首頁(yè)的展示和商品詳細(xì)信息的展示。首先看下商品首頁(yè)的樣子吧:
圖書(shū)的詳細(xì)信息頁(yè)面:
十、總結(jié)
到這里,本專題的內(nèi)容就介紹完了, 本專題主要介紹面向領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的分層架構(gòu)和面向服務(wù)架構(gòu)。然后結(jié)合它們?cè)诰W(wǎng)上書(shū)店中進(jìn)行實(shí)戰(zhàn)演練。在后面的專題中我會(huì)在該項(xiàng)目中一直進(jìn)行完善,從而形成一個(gè)完整了DDD案例。在接下來(lái)的專題會(huì)對(duì)倉(cāng)儲(chǔ)的實(shí)現(xiàn)應(yīng)用規(guī)約模式,在應(yīng)用之前,我會(huì)先寫(xiě)一個(gè)專題來(lái)介紹規(guī)約模式來(lái)作為一個(gè)準(zhǔn)備工作。
GitHub 開(kāi)源地址:https://github.com/lizhi5753186/OnlineStore。
?
總結(jié)
以上是生活随笔為你收集整理的[.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 保障危险品的物流安全问题,大数据扮演了重
- 下一篇: 中国人工智能学会通讯——智能系统测评:挑