SOA与DDD的定义

发布时间:2026/7/5 4:56:32
SOA与DDD的定义 SOA与DDD都是常用的系统架构但两者之间所针对的核心是不同的。SOA面向服务架构由Gartner 在1996年提出来它是一种分布式的软件架构它可以根据需求通过网络对松散耦合的粗粒度应用组件进行部署、组合和使用。简单来说SOA就是一种大型系统开发的体系架构在基于SOA架构的系统中具体应用程序的功能是由一些松耦合并且具有统一接口的组件也就是service组合构建起来的它是针对多核心多平台之间的数据交换。DDD领域驱动设计由Eric Evans在2004提出它的核心内容是“如何将业务领域概念映射到软件工程当中”。它推翻了“软件从数据层开发设计”的旧习惯强调领域模型在软件中发挥的强大力量注重如何把企业内部复杂的业务流程转化为软件。也许可以认为SOA针对的是大型系统的总体架构注重如何把系统进行项目分离隔离开发最后实现系统合并。而DDD是针对单个项目的开发管理过程注重如何利用领域模型把业务需求转化为软件。两者之间并没有存在理论上的冲突能把两者结合各展所长更能发挥各自的优势。回到目录二、DDD的分层结构1. 概念从概念上来说领域驱动设计架构主要分为基础设施层、领域层、应用层、表现层4个概念层。基础结构层是为各层提供各项通用技术能力而构建的它可以为领域层提供像Hibernate、LINQ、ADO.NET等持久化机制为应用层传递消息为表现层提供插件等等。领域层它是系统的核心部分代表业务的逻辑概念。它会根据业务的实际流程定制了业务信息以及业务规则并按一定的关系制定领域模型。领域模型尽管需要依赖基础结构层进行保存但领域模型之间的逻辑关系是跟基础结构层相隔离的。即使基础结构层从NHibernate技术转换成LINQ技术也不会影响到领域层的结构。领域模型只会依赖实际的业务逻辑它只会根据业务的转变而灵活变动。应用层它的任务是协调领域层与表现层之间的关系也可以作为系统与外界沟通的桥梁在这层里面不会包括任何的业务逻辑。在SOA面向服务架构这一层起着重要的作用在第七节将详细说明。表现层它是常用的界面开发可以以页面ASP.NET、JSP窗口WinForm、WPF、Swing等形式表现它的主要职责是负责与用户进行信息沟通。(注意在一般的项目开发中Web服务会作为与外界通讯的接口放置在表现层中但在SOA中Web服务会大多置于应用层中下面将会作进一步解释2. 开发实例在此先举个常见的订单管理例子在下面的章节里都会以这个实例为参考每个用户在Person表里面都会有一个对应的帐户里面记录了用户的姓名、地址、电话、积分Point等基本信息。在Order表里记录的是每次交易的过程每次商品的送货费Freightage为10元当商品价格Price超过98元可免费送货当用户Person积分Point超过2000分可获7折优惠Favorable1000~2000分可获8折1000分以下可有9折最后总体价格为为TotalPrice。在最后结单的时候Order表里会产生订单号码OrderNumber和下订日期DeliveryPerson表的积分也会加上订单总价的点数。最后OrderItem表包含了物品Goods、物品价格Price、购买数量Count等属性它主要记录每次订单的详细交易状况。上面的业务逻辑跟淘宝、当当等等大型购物网基本相似。之所以用这样一个例子作为参考是想表现一下DDD是如果利用领域模型去适应多变的业务逻辑关系。回到目录三、把业务关系转化为领域模型1. 概念模型驱动设计设计MODEL-DRIVEN-DESIGN是DDD里面的核心它代表的是各个对象之间的关系把复杂的逻辑关系转化为模型。模型主要分为实体Entity、值对象Value Object与服务Service三种。实体实体所包含的不单止是一连串的属性更重要的是与事件的联系在一个生命周期中环境的变化与事件发生将引起实体内部产生变化。好像在实体Order里面Person的积分Point和OrderItem的价格Price都会直接影响总体价格TotalPrice的大小而总体价格也会影响到运费Freightage的多少等等。在Order实体的一切都会受到Person、OrderItem等这些外部因数的影响这样的对象被视为实体。在不同的时刻实体会有不同的状态所以在开发过程中我们需要为实体加上一个“标识符”来区分对象的身份它是实体的生命周期里的唯一标志。值对象当所用到的对象只有属性而没有其他逻辑关系的时候我们就可以把它视为是值对象。值对象没有状态也不需要有 “标识符”。在多数情况下它可以作为一个属性存在于一个实体的内部。一般情况下值对象的属性是不可改变的当需要更改属性时可以把整个对象删除然后重新加入一个新对象。服务当实体之间存在某些操作它们并不单一地附属于某一个实体而是跟多个实体都有关联的时候就可以使用服务来封装这些操作。值得注意的是服务并非单独指Web Service, 也并非单单存在于领域层而是在各个层当中都会存在服务每一层的服务都有着不同的职能。在基础结构层服务可能是用于构建身份验证、电子邮件、错误处理等等操作在领域层服务更多时候是一种操作它用于协调多个实体之间的关系处理各类的业务问题;在应用层特别是在分布式开发系统内服务多以Web Service、TCP/IP套接字、MSMQ等等方式实现服务在此处会作为一个与外界通讯的接口;备注 这里面也存在一定的争义Eric 认为实体所代表的只是多个对象之间的关系而它们的动作将会由服务来体现出来这被称为贫血型模型。但在开发过程中越来越多人会把动作加入到实体里面这被称为充血型模型。其实不同的问题应该客观分析分别对待在这个例子里面将会以按照 Eric 的定义来开发服务在后面的开发过程中大家也可以从中体现一下服务层所带来的好处。2. 实例说明先以ADO.NET Entity Framework实现模型Person、Order分别属于两个实体它们都将继承Root接口在它们的生命周期内都会生成一个Guid作为标志。此处把OrderItem作为一个值对象置于Order实体内这意味着OrderItem会通过Order来获取外界不能跨越Order直接获取OrderItem。当然这应该由具体的业务情况来确定当外界需要单独调用OrderItem类的时候就应该考虑把OrderItem独立成为一个实体类。在这里可利用分部类为实体增加Guid属性关于分部类于分部方法的详细介绍可参考C#综合揭秘——分部类和分部方法namespace Business.DomainModel { public interface Root { } public partial class Order:Root { private Guid _guid; public Order() { _guid System.Guid.NewGuid(); } //为根对象设置唯一的Guid; public Guid GUID { get { return _guid; } } } public partial class Person:Root { public Person() { _guid System.Guid.NewGuid(); } //为根对象设置唯一的Guid; private Guid _guid; public Guid GUID { get { return _guid; } } } }回到目录四、细说Repository1.概念Repository是把持久化对象转换成领域模型的一种方式可用于获取、更新持久对象并管理它们的生命周期。它使应用程序与持久化技术实现解耦程序无需受制于使用Oracle还是MySql数据库也不会受到Hibernate、LINQ、ADO.NET等数据层的约束使开发人员可以把注意力集中到领域模型当中。Repository与传统三层模式的DAL层有点相似但Repository针对的是每一个根对象来划分边界的。在这个例子当中Person与Order都会有对应的PersonRepository、OrderRepository。而OrderItem只是Order的子属性所以它的插入、更新、删除都会包含在OrderRepository当中。当多个对象之间建立起联系后关系将是复杂的特别是在LINQ里面程序可以轻易通过Person的导航属性里获取OrderItem的值最后很容易使代码变得混乱。所以确立Repository的边界可以在有效管理每个Repository的职能。2.实例说明注意OrderItem的存取、删除都包含在OrderRepository里面。在获取、修改Order的时候也会利用“显式加载” context.Order.Include(OrderItem) 的方法,使OrderItem实现同步更新。而通过PersonRepository.GetPersonint )获取的Person对象它内部的Order属性将是null值这必须清晰按照领域模型的边界划分的。当LINQ面世以后数据的获取变得简单特别在一些小型的系统开发时很多人会不自觉地把这种领域模型的分界规则打破。但随着系统的复杂化问题就会逐渐呈现。比如当Order对象的属性被更新使用OrderRepository.UpdateOrder)更新数据库后页面的Person对象未能同步实现更新在Person与数据库交换数据的时候Order又被变回旧值。在混乱的数据层开发中这种情况非常常见所以在下会坚持Repository的原则把Repository的职能清晰按照领域模型划分。namespace Business.IRepository { public interface IOrderRepository { Order GetOrder(int id); IListOrder GetList(); IListOrder GetListByPerson(int personID); int AddOrder(Order order); int DeleteOrder(int id); int UpdateOrder(Order order); int AddOrderItem(OrderItem orderItem); int DeleteOrderItem(int id); } public interface IPersonRepository { int AddPerson(Person person); int AttachPerson(Person person); int UpdatePerson(Person person); Person GetPerson(int id); IListPerson GetList(); } } namespace Business.Repository { public class OrderRepository:IOrderRepository { //根据ID获取单个Order public Order GetOrder(int id) { BusinessContext _context new BusinessContext(); Order order null; try { using (TransactionScope scope new TransactionScope()) { //由于OrderItem是Order实体中的一个属性必须通过OrderRepository同步获取 var list _context.Order.Include(OrderItem) .Where(x x.ID id); if (list.Count() 0) order list.First(); else order new Order(); scope.Complete(); } } catch (Exception ex) { //出错处理并返回一个空对象 Business.Common.ExceptionManager.DataException.DealWith(ex); order new Order(); } _context.Dispose(); return order; } .................. .................. } public class PersonRepository:IPersonRepository { public int AddPerson(Person person) { return LinqHelp.AddPerson(person); } public Person GetPerson(int id) { return LinqHelp.GetPerson(id); } ................. ................. } }在更新Order这种复杂的领域模型时如果要分辨单个OrderItem属性是新建值还是更新值然后分别处理那将是比较麻烦的而且OrderItem只是一个值对象ID编码等属性对它没有任何实在意义。所以在更新ListOrderItem属性时都会先把它全部删除然后重新加载在OrderItem数量不多的时候这是一种十分有效的方法。namespace Business.Repository { public class OrderRepository:IOrderRepository { ................. ................. //更新Order因为难以别哪些是原有的OrderItem,哪些OrderItem是新插入 //使用简单的方法会先把原有的OrderItem的删除再重新插入 public int UpdateOrder(Order order) { int returnValue -1; BusinessContext _context new BusinessContext(); try { using (TransactionScope scope new TransactionScope()) { var list _context.Order.Include(OrderItem) .Where(x x.ID order.ID); if (list.Count() 0) { //更新Order列 Order _order list.First(); _order.Count order.Count; _order.Delivery order.Delivery; _order.Favorable order.Favorable; _order.Freightage order.Freightage; _order.OrderNumber order.OrderNumber; _order.PersonID order.PersonID; _order.Price order.Price; _order.TotalPrice order.TotalPrice; //删除原有的订单明细项OrderItem if (list.First().OrderItem.Count ! 0) foreach (var item in list.First().OrderItem) DeleteOrderItem(item.ID); //加入新的订单明细项OrderItem if (order.OrderItem.Count ! 0) { foreach (var item in order.OrderItem) { var _orderItem new OrderItem(); _orderItem.Count item.Count; _orderItem.Goods item.Goods; _orderItem.OrderID item.OrderID; _orderItem.Price item.Price; AddOrderItem(_orderItem); } } returnValue _context.SaveChanges(); } else returnValue 0; scope.Complete(); } } catch (Exception ex) { Business.Common.ExceptionManager.DataException.DealWith(ex); returnValue-1; } _context.Dispose(); return returnValue; } //插入OrderItem public int AddOrderItem(OrderItem orderItem) { return LinqHelp.AddOrderItem(orderItem); } //删除OrderItem public int DeleteOrderItem(int id) { EntityKey key new EntityKey(BusinessContext.OrderItem, ID, id); return LinqHelp.Delete(key); } } }