标签 标签 标签

  • 一句话的事儿:

前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储。领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加相关元素,使架构慢慢变得丰满。这篇打算分享下应用层的搭建。根据DDD的设计原则,应用层不包含任何领域逻辑,它主要的作用是协调任务,或者叫调度任务,维护应用程序状态。根据博主的理解,应用层是用来隔离领域层的,假设没有应用层,那么我们的界面层可以直接调用领域层的逻辑,也就是说可以直接访问领域的model,这样的坏处显而易见:一是领域model不是纯粹的数据model,它含有领域的行为,直接将其传到前台会造成调用的混乱;二是仓储是和数据持久化打交道了,界面直接调用仓储,也就是界面直接和数据打交道,也不符合一般分层的原则。所以我们引入应用层,本文应用层是一个以控制台项目为宿主的WCF服务。我们来看代码设计。
DDD领域驱动设计初探系列文章:

关于WCF的理论在此就不再展开,下面结合我们的项目代码我们从零开始一步一步来搭建一个自己的WCF服务吧。

二、WCF代码示例

1、代码结构图

DDD领域驱动设计初探(四):WCF搭建 - 图1
项目按照模块为单位划分服务,比如权限模块,我们就有一个权限的接口契约IPowerManageWCFService。IService文件夹里面放了3个接口,分别对应系统3个模块的接口契约,Service文件夹里面分别对应了3个接口的实现。ServiceAttribute.cs里面定义了两个特性,表示接口是WCF的服务。我们来看看具体的代码。

2、代码示例

2.1 ServiceAttribute.cs文件定义契约接口和实现的特性类:

  1. namespace ESTM.WCF.Service
  2. {
  3. //标记此特性的为WCF服务接口
  4. public class ServiceInterfaceAttribute : Attribute
  5. {
  6. }
  7. //标记此特性的为WCF服务接口实现类
  8. public class ServiceClassAttribute : Attribute
  9. {
  10. }
  11. }

2.2 接口契约代码:

  1. /// <summary>
  2. /// 工厂布局模块接口契约
  3. /// </summary>
  4. [ServiceInterface]
  5. [ServiceContract]
  6. public interface IFactoryLayoutWCFService
  7. {
  8. [OperationContract]
  9. List<DTO_TM_PLANT> GetAllPlant();
  10. }
  1. /// <summary>
  2. /// 权限管理模块接口契约
  3. /// </summary>
  4. [ServiceContract]
  5. [ServiceInterface]
  6. public interface IPowerManageWCFService
  7. {
  8. [OperationContract]
  9. IList<DTO_TB_DEPARTMENT> GetAllDepartment();
  10. }
  1. /// <summary>
  2. /// 产品管理模块接口契约
  3. /// </summary>
  4. [ServiceContract]
  5. [ServiceInterface]
  6. public interface IProductWCFService
  7. {
  8. [OperationContract]
  9. IList<DTO_TP_PRODUCT> GetAllProduct();
  10. }

接口契约[ServiceContract]表示该接口遵守接口契约协定,[OperationContract]操作契约,这两个特性都是WCF内置的东西。[ServiceInterface]的用处我们待会说。

2.3 接口实现代码

  1. [ServiceClass]
  2. public class FactoryLayoutWCFService : IFactoryLayoutWCFService
  3. {
  4. public List<DTO_TM_PLANT> GetAllPlant()
  5. {
  6. throw new NotImplementedException();
  7. }
  8. }
  9. [ServiceClass]
  10. public class ProductWCFService : IProductWCFService
  11. {
  12. public IList<DTO_TP_PRODUCT> GetAllProduct()
  13. {
  14. throw new NotImplementedException();
  15. }
  16. }
  17. [ServiceClass]
  18. public class PowerManageWCFService : IPowerManageWCFService
  19. {
  20. public IList<DTO_TB_DEPARTMENT> GetAllDepartment()
  21. {
  22. throw new NotImplementedException();
  23. }
  24. }

[ServiceClass]特性和接口上面的[ServiceInterface]特性对应,用于标记契约和实现。

2.4 Bootstrapper.cs里面定义了服务的启动方法

  1. public class Bootstrapper
  2. {
  3. private string strBaseServiceUrl = ConfigurationManager.AppSettings["ServiceUrl"].ToString();
  4. //启动所有的服务
  5. public void StartServices()
  6. {
  7. //1.读取此程序集里面的有服务契约的接口和实现类
  8. var assembly = Assembly.Load(typeof(Bootstrapper).Namespace);
  9. var lstType = assembly.GetTypes();
  10. var lstTypeInterface = new List<Type>();
  11. var lstTypeClass = new List<Type>();
  12. foreach (var oType in lstType)
  13. {
  14. //2.通过接口上的特性取到需要的接口和实现类
  15. var lstCustomAttr = oType.CustomAttributes;
  16. if (lstCustomAttr.Count() <= 0)
  17. {
  18. continue;
  19. }
  20. var oInterfaceServiceAttribute = lstCustomAttr.FirstOrDefault(x => x.AttributeType.Equals(typeof(ServiceInterfaceAttribute)));
  21. if (oInterfaceServiceAttribute != null)
  22. {
  23. lstTypeInterface.Add(oType);
  24. continue;
  25. }
  26. var oClassServiceAttribute = lstCustomAttr.FirstOrDefault(x => x.AttributeType.Equals(typeof(ServiceClassAttribute)));
  27. if (oClassServiceAttribute != null)
  28. {
  29. lstTypeClass.Add(oType);
  30. }
  31. }
  32. //3.启动所有服务
  33. foreach (var oInterfaceType in lstTypeInterface)
  34. {
  35. //通过反射找到接口的实现类,找到配对然后启动服务
  36. var lstTypeClassTmp = lstTypeClass.Where(x => x.GetInterface(oInterfaceType.Name) != null).ToList();
  37. if (lstTypeClassTmp.Count <= 0)
  38. {
  39. continue;
  40. }
  41. if(lstTypeClassTmp[0].GetInterface(oInterfaceType.Name).Equals(oInterfaceType))
  42. {
  43. var oTask = Task.Factory.StartNew(() =>
  44. {
  45. OpenService(strBaseServiceUrl + "/" + oInterfaceType.Name, oInterfaceType, lstTypeClassTmp[0]);
  46. });
  47. }
  48. }
  49. }
  50. //通过服务接口类型和实现类型启动WCF服务
  51. private void OpenService(string strServiceUrl, Type typeInterface, Type typeclass)
  52. {
  53. Uri httpAddress = new Uri(strServiceUrl);
  54. using (ServiceHost host = new ServiceHost(typeclass))
  55. {
  56. ///////////////////////////////////////添加服务节点///////////////////////////////////////////////////
  57. host.AddServiceEndpoint(typeInterface, new WSHttpBinding(), httpAddress);
  58. if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
  59. {
  60. ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
  61. behavior.HttpGetEnabled = true;
  62. behavior.HttpGetUrl = httpAddress;
  63. host.Description.Behaviors.Add(behavior);
  64. }
  65. host.Opened += delegate
  66. {
  67. Console.ForegroundColor = ConsoleColor.Green;
  68. Console.WriteLine("服务启动成功。服务地址:" + strServiceUrl);
  69. };
  70. host.Open();
  71. while (true)
  72. {
  73. Console.ReadLine();
  74. }
  75. }
  76. }
  77. }

对应的App.Config里面对应的服务的URL

  1. <appSettings>
  2. <add key="ServiceUrl" value="http://127.0.0.1:1234/MyWCF.Server"/>
  3. </appSettings>

StartServices()方法通过反射和两个特性[ServiceClass]与[ServiceInterface],依次启动三个服务。
然后再Program里面调用

  1. static void Main(string[] args)
  2. {
  3. var oBootstrapper = new Bootstrapper();
  4. oBootstrapper.StartServices();
  5. Console.ReadLine();
  6. }

得到结果:
DDD领域驱动设计初探(四):WCF搭建 - 图2
我们随便选择一个服务,通过浏览器访问,测试服务是否启动成功。
DDD领域驱动设计初探(四):WCF搭建 - 图3
至此,WCF服务基本完成。

三、DTO说明

DTO,全称Data Transfer Object,数据传输对象。DTO是一个贫血模型,也就是它里面基本没有方法,只有一堆属性,并且所有属性都具有public的getter和setter访问器。为什么需要一个DTO对象?这个问题在C#进阶系列——MEF实现设计上的“松耦合”(终结篇:面向接口编程)这篇里面介绍过,它的作用其实很单一,就是用于数据传递和数据绑定。至于DTO如何设计,博主的项目里,DTO是按照聚合来划分的,也就是一个聚合对应一个DTO,DTO里面属性的定义可以根据项目需求来定。我们来看看代码:
DDD领域驱动设计初探(四):WCF搭建 - 图4

  1. /// <summary>
  2. /// 所有DTO model的父类,用作泛型约束
  3. /// </summary>
  4. [DataContract]
  5. public class DTO_BASEMODEL
  6. {
  7. }
  8. /// <summary>
  9. /// TB_DEPARTMENT
  10. /// </summary>
  11. [DataContract]
  12. public class DTO_TB_DEPARTMENT : DTO_BASEMODEL
  13. {
  14. [DataMember]
  15. public string DEPARTMENT_ID { get; set; }
  16. [DataMember]
  17. public string DEPARTMENT_NAME { get; set; }
  18. [DataMember]
  19. public string PARENT_ID { get; set; }
  20. [DataMember]
  21. public string DEPARTMENT_LEVEL { get; set; }
  22. [DataMember]
  23. public string STATUS { get; set; }
  24. }

其他DTO都和这个类似,就不一一列举了。由于DTO需要由WCF传递到Web前台,所以要求这个对象可以序列化,需要标记[DataContract]和[DataMember]两个特性,DTO_BASEMODEL作为所有DTO的父类, 用作泛型约束和定义DTO的一些公用特性。到此,WCF的搭建基本完成,下篇我们来介绍下Automapper的使用。累死我了,今天先到这吧,也不早了,博主也要安歇了。晚安!

还是附上 源码 吧,要不然园友们要说博主小气呢~~有兴趣可以看看。


  • 本文作者:GeekPower - Felix Sun
  • 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!