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); } } }

相关新闻

【Bug已解决】OpenClaw 报错 Error: Cannot find module ‘@larksuiteoapi/node-sdk‘ 解决方案

【Bug已解决】OpenClaw 报错 Error: Cannot find module ‘@larksuiteoapi/node-sdk‘ 解决方案

【Bug已解决】OpenClaw 报错 Error: Cannot find module larksuiteoapi/node-sdk 解决方案 1. 问题描述 给 OpenClaw 配置接入飞书(Lark)渠道后,启动服务时遇到模块加载失败: Error: Cannot find module larksuiteoapi/node-sdk R…

2026/7/5 4:56:39阅读更多 →
基于JEPA框架的轻量世界模型LeWorldModel:1GB显存实现AI环境预测

基于JEPA框架的轻量世界模型LeWorldModel:1GB显存实现AI环境预测

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 在探索人工智能的前沿领域时,我们常常被那些需要海量算力和显存的复杂模型所困扰。近期,一个名为 LeWorldMod…

2026/7/5 4:56:39阅读更多 →
数据转换过程

数据转换过程

前面已经解释了DTO的作用,但实现领域对象与DTO之间的转换是一件复杂的事件,因此可以建立一个数据转换器实现此功能。 在平常的工作里,不太多会把“订单管理系统”做成SOA的模式,因为在分布式系统中,数据的格式与定义大…

2026/7/5 4:56:39阅读更多 →
JPEXS Free Flash Decompiler:深入SWF逆向工程与资源提取实战指南

JPEXS Free Flash Decompiler:深入SWF逆向工程与资源提取实战指南

JPEXS Free Flash Decompiler:深入SWF逆向工程与资源提取实战指南 【免费下载链接】jpexs-decompiler JPEXS Free Flash Decompiler 项目地址: https://gitcode.com/gh_mirrors/jp/jpexs-decompiler 作为一名技术开发者或安全研究人员,当你面对遗…

2026/7/5 6:06:43阅读更多 →
ParsecVDisplay:Windows虚拟显示器终极配置指南

ParsecVDisplay:Windows虚拟显示器终极配置指南

ParsecVDisplay:Windows虚拟显示器终极配置指南 【免费下载链接】parsec-vdd ✨ Perfect virtual display for game streaming 项目地址: https://gitcode.com/gh_mirrors/pa/parsec-vdd 想要零成本扩展Windows电脑的显示空间?ParsecVDisplay为您…

2026/7/5 6:06:43阅读更多 →
中聚信财学院贵阳 AI 财务官实战营线下课顺利结束

中聚信财学院贵阳 AI 财务官实战营线下课顺利结束

2026/7/5 6:06:43阅读更多 →
Obsidian笔记图片本地化插件:如何彻底解决网络图片链接失效问题?

Obsidian笔记图片本地化插件:如何彻底解决网络图片链接失效问题?

Obsidian笔记图片本地化插件:如何彻底解决网络图片链接失效问题? 【免费下载链接】obsidian-local-images-plus This repo is a reincarnation of obsidian-local-images plugin which main aim was downloading images in md notes to local storage. …

2026/7/5 6:06:43阅读更多 →
WPS-Zotero插件:5分钟搞定跨平台文献引用,科研写作效率翻倍

WPS-Zotero插件:5分钟搞定跨平台文献引用,科研写作效率翻倍

WPS-Zotero插件:5分钟搞定跨平台文献引用,科研写作效率翻倍 【免费下载链接】WPS-Zotero An add-on for WPS Writer to integrate with Zotero. 项目地址: https://gitcode.com/gh_mirrors/wp/WPS-Zotero 还在为Windows和Linux之间切换文献管理软…

2026/7/5 6:06:43阅读更多 →
AAA小学期第五周学习笔记

AAA小学期第五周学习笔记

完成了发射端pcb的绘制,并下单

2026/7/5 6:01:43阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/5 0:01:08阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/5 0:01:08阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/5 0:01:08阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/5 0:01:08阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/5 0:01:08阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/5 0:01:08阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/5 1:30:27阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/5 3:48:10阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/5 3:48:09阅读更多 →