云南网站建设工具,wordpress防御ip攻击,邯郸手机网站建设报价,php购物网站设计代码摘要#xff1a; 阿里妹导读#xff1a;你还在用面向对象的语言写面向过程的代码吗#xff1f;你是否正在被复杂的业务逻辑折磨#xff1f;是否有时觉得应用开发没意思、没挑战、技术含量低#xff1f;其实#xff0c;应用开发一点都不简单#xff0c;也不无聊#xff…摘要 阿里妹导读你还在用面向对象的语言写面向过程的代码吗你是否正在被复杂的业务逻辑折磨是否有时觉得应用开发没意思、没挑战、技术含量低其实应用开发一点都不简单也不无聊业务的变化比底层基础实施的变化要多得多封装这些变化需要很好的业务理解力抽象能力和建模能力。阿里妹导读你还在用面向对象的语言写面向过程的代码吗你是否正在被复杂的业务逻辑折磨是否有时觉得应用开发没意思、没挑战、技术含量低其实应用开发一点都不简单也不无聊业务的变化比底层基础实施的变化要多得多封装这些变化需要很好的业务理解力抽象能力和建模能力。
今天我们邀请阿里高级技术专家张建飞一起来聊聊为什么需要领域建模什么是好的模型又该如何搭建。
为什么要领域建模
软件的世界里没有银弹是用事务脚本还是领域模型没有对错之分关键看是否合适。实际上CQRS就是对事务脚本和领域模型两种模式的综合因为对于Query和报表的场景使用领域模型往往会把简单的事情弄复杂此时完全可以用奥卡姆剃刀把领域层剃掉直接访问Infrastructure。我个人也是坚决反对过度设计的因此对于简单业务场景我强力建议还是使用事务脚本其优点是简单、直观、易上手。但对于复杂的业务场景你再这么玩就不行了因为一旦业务变得复杂事务脚本就很难应对容易造成代码的“一锅粥”系统的腐化速度和复杂性呈指数级上升。
目前比较有效的治理办法就是领域建模因为领域模型是面向对象的在封装业务逻辑的同时提升了对象的内聚性和重用性因为使用了通用语言Ubiquitous Language使得隐藏的业务逻辑得到显性化表达使得复杂性治理成为可能。talk is cheap直接上一个银行转账的例子对事务脚本和领域模型进行比较孰优孰劣一目了然。
银行转账事务脚本实现
在事务脚本的实现中关于在两个账号之间转账的领域业务逻辑都被写在了MoneyTransferService的实现里面了而Account仅仅是getters和setters的数据结构也就是我们说的贫血模式。上面的代码大家看起来应该比较眼熟因为目前大部分系统都是这么写的。需求评审完工程师画几张UML图完成设计就开始向上面这样怼业务代码了这样写基本不用太费脑完全是面向过程的代码风格。有些同学可能会说我这样写也可以实现系统功能啊还是那句话“just because you can, doesn’t mean you should”。说句不好听的正是有这么多“没有追求”、“不求上进”的码农才造成了应用系统的混乱、败坏了应用开发的名声。这也是为什么很多应用开发工程师觉得工作没意思技术含量低觉得整天就是写if-else的业务逻辑代码系统又烂工作繁琐、无聊、没有成长、没有成就感所以转向去做中间件啊去写JDK啊觉得那个NB。
实际上应用开发一点都不简单也不无聊业务的变化比底层Infrastructure的变化要多得多解决的难度也丝毫不比写底层代码容易只是很多人选择了用无聊的方式去做。其实我们是有办法做的更优雅的这种优雅的方式就是领域建模唯有掌握了这种优雅你才能实现从工程师向应用架构的转型。同样的业务逻辑接下来就让我们看一下用DDD是怎么做的。
银行转账领域模型实现
如果用DDD的方式实现Account实体除了账号属性之外还包含了行为和业务逻辑比如debit( )和credit( )方法。而且透支策略OverdraftPolicy也不仅仅是一个Enum了而是被抽象成包含了业务规则并采用了策略模式的对象。而Domain Service只需要调用Domain Entity对象完成业务逻辑即可。通过上面的DDD重构后原来在事务脚本中的逻辑被分散到Domain ServiceDomain Entity和OverdraftPolicy三个满足SOLID的对象中在继续阅读之前我建议可以自己先体会一下DDD的好处。
领域建模的好处
面向对象 封装Account的相关操作都封装在Account Entity上提高了内聚性和可重用性。
多态采用策略模式的OverdraftPolicy多态的典型应用提高了代码的可扩展性。
业务语义显性化
通用语言“一个团队一种语言”将模型作为语言的支柱。确保团队在内部的所有交流中代码中画图写东西特别是讲话的时候都要使用这种语言。例如账号转账透支策略这些都是非常重要的领域概念如果这些命名都和我们日常讨论以及PRD中的描述保持一致将会极大提升代码的可读性减少认知成本。
显性化就是将隐式的业务逻辑从一推if-else里面抽取出来用通用语言去命名、去写代码、去扩展让其变成显示概念比如“透支策略”这个重要的业务概念按照事务脚本的写法其含义完全淹没在代码逻辑中没有突显出来看代码的人自然也是一脸懵逼而领域模型里面将其用策略模式抽象出来不仅提高了代码的可读性可扩展性也好了很多。
如何进行领域建模
建模方法
领域建模这个话题太大关于此的长篇大论和书籍也很多比如什么通过语法和句法深入分析法在我看来这些方法论有些繁琐了。好的模型应该是建立在对业务深入理解的基础上如果业务理解不到位你再怎么分析句子也不可能产出好的模型。就我自己的经验而言建模也是一个不断迭代的过程所以一开始可以简单点来就采用两步建模法抓住一些核心概念然后写一些代码验证一下run一下看看顺不顺如果很顺滑说明没毛病否则就要看看是不是需要调整一下模型随着项目的进行和对业务理解的不断深入这种迭代将持续进行。
那什么是两步建模法呢也就是只需要两个步骤就能建模了首先从User Story找名词和动词然后用UML类图画出领域模型。是不是很简约简约并不意味着简单对于业务架构师和系统分析师来说见功力的地方往往就在于此。
举个栗子比如让你设计一个中介系统一个典型的User Story可能是“小明去找工作中介说你留个电话有工作机会我会通知你”这里面的关键名词很可能就是我们需要的领域对象小明是求职者电话是求职者的属性中介包含了中介公司中介员工两个关键对象工作机会肯定也是关键领域对象通知这个动词暗示我们这里用观察者模式会比较合适。然后再梳理一下领域对象之间的关系一个求职者可以应聘多个工作机会一个工作机会也可以被多个求职者应聘M2M的关系中介公司可以包含多个员工O2M的关系。对于这样简单的场景这个建模就差不多了。
当然我们的业务场景往往比这个要复杂而且不是所有的名词都是领域对象也可能是属性也不是所有的动词都是方法也可能是领域对象所以要具体问题具体对待这个对待的过程需要我们有很好的业务理解力抽象能力以及建模的经验知道为什么公司的job model里那么强调技术人员的业务理解力和抽象能力了吧比如通常情况下价格和库存只是订单和商品的一个属性但是在阿里系电商业务场景下价格计算和库存扣减的复杂程度可以让你怀疑人生因此作为电商中台把价格和库存单独当成一个域Domain去对待是很必要的。
另外建模不是一个一次性的工作往往随着业务的变化以及我们对业务的理解越来越深入才能看清系统的全貌所以迭代重构是免不了的也就是要Agile Modelling。
模型统一和模型演化
建模的过程很像盲人摸象不同背景人用不同的视角看同一个东西其理解也是不一样的。比如两个盲人都摸到大象鼻子一个人认为是像蛇活的能动而另一个人认为像消防水管可以喷水那么他们将很难集成。双方都无法接受对方的模型因为那不符合自己的体验。事实上他们需要一个新的抽象这个抽象需要把蛇的“活着的特性”与消防水管的“喷水功能”合并到一起而这个抽象还应该排除先前两个模型中一些不确切的含义和属性比如毒牙或者卷起来放到消防车上去的行为。这就是模型的统一。
世界上唯一不变的就是变化模型和代码一样也需要不断的重构和精化每一次的精化之后开发人员应该对领域知识有了更加清晰的认识。这使得理解上的突破成为可能之后一系列快速的改变得到了更符合用户需要并更加切合实际的模型。其功能性及说明性急速增强而复杂性却随之消失。
这种突破需要我们对业务有更加深刻的领悟和思考然后再加上重构的勇气和能力勇气是项目工期很紧你敢不敢重构能力是你有没有完备的CI保证你的重构不破坏现有的业务逻辑。还是以开篇的转账来举个例子假如转账业务开始变的复杂要支持现金信用卡支付宝比特币等多种通道且没种通道的约束不一样还要支持一对多的转账。那么你还是用一个transfer(fromAccount, toAccount)就不合适了可能需要抽象出一个专门的领域对象Transaction这样才能更好的表达业务其演化过程如下领域服务
什么是领域服务
有些领域中的动作它们是一些动词看上去却不属于任何对象。它们代表了领域中的一个重要的行为所以不能忽略它们或者简单地把它们合并到某个实体或者值对象中。当这样的行为从领域中被识别出来时最佳实践是将它声明成一个服务。这样的对象不再拥有内置的状态。它的作用仅仅是为领域提供相应的功能。Service往往是以一个活动来命名而不是Entity来命名。例如开篇转账的例子转账transfer这个行为是一个非常重要的领域概念但是它是发生在两个账号之间的归属于账号Entity并不合适因为一个账号Entity没有必要去关联他需要转账的账号Entity这种情况下使用MoneyTransferDomainService就比较合适了。
识别领域服务主要看它是否满足以下三个特征
服务执行的操作代表了一个领域概念这个领域概念无法自然地隶属于一个实体或者值对象。被执行的操作涉及到领域中的其他的对象。操作是无状态的。
应用服务和领域服务如何划分
在领域建模中我们一般将系统划分三个大的层次即应用层Application Layer领域层Domain Layer和基础实施层Infrastructure Layer关于这三个层次的详细内容可以参考我的另一篇SOFA框架的分层设计。可以看到在App层和Domain层都有服务Service这两个Service如何划分呢什么样的功能应该放在应用层什么样的功能应该放在领域层呢
决定一个服务Service应该归属于哪一层是很困难的。如果所执行的操作概念上属于应用层那么服务就应该放到这个层。如果操作是关于领域对象的而且确实是与领域有关的、为领域的需要服务那么它就应该属于领域层。总的来说涉及到重要领域概念的行为应该放在Domain层而其它非领域逻辑的技术代码放在App层例如参数的解析上下文的组装调用领域服务消息发送等。还是银行转账的case为例下图给出了划分的建议业务可视化和可配置化
好的领域建模可以降低应用的复杂性而可视化和可配置化主要是帮助大家主要是非技术人员比如产品业务和客户直观地了解系统和配置系统提供了一种“code free”的解决方案也是SaaS软件的主要卖点。要注意的是可视化和可配置化难免会给系统增加额外的复杂度必须慎之又慎最好是能使可视化和配置化的逻辑与业务逻辑尽量少的耦合否则破坏了原有的架构把事情搞的更复杂就得不偿失了。
在可扩展设计中我已经介绍了我们SOFA架构是如何通过扩展点的设计来支撑不同业务差异化的需求的那么可否更进一步我们将领域的行为也叫能力和扩展点用可视化的方式呈现出来并对于一些不需要编码实现的扩展点用配置的方式去完成呢。当然是可以的比如还是开篇转账的例子对于透支策略OverdraftPolicy这个业务扩展点新来一个业务说透支额度不能超过1000我们可以完全结合规则引擎进行配置化完成而不需要编码。
所以我能想到的一种还比较优雅的方式是通过Annotation注解的方式对领域能力和扩展点进行标注然后在系统bootstrap阶段通过代码扫描的方式将这些能力点和扩展点收集起来上传到中心服务器然后再通过GUI的方式呈现出来从而做到业务的可视化和可配置化。大概的示意图如下有同学可能会问流程要不要可视化这里要分清楚两个概念业务逻辑流和工作流很多同学混淆了这两个概念。业务逻辑流是响应一次用户请求的业务处理过程其本身就是业务逻辑对其编排和可视化的意义并不是很大无外乎只是把代码逻辑可视化了在我们的SOFA框架中是通过扩展点和策略模式来处理业务的分支情况而我看到我们阿里很多的内部系统将这种响应一次用户请求的业务逻辑用很重的工作流引擎来做美其名曰流程可编排实质上往往是把简单的事情复杂化了。而工作流是指完成一项任务所需要不同节点的连接节点主要分为自动节点和人工节点其中每个人工节点都需要用户的参与也就是响应一次用户的请求比如审批流程中的经理审批节点CRM销售过程的业务员的处理节点等等。
此时可以考虑使用工作流引擎特别是当你的系统需要让用户自定义流程的时候那就不得不使用可视化和可配置的工作流引擎了除此之外最好不要自找麻烦。当然也不排除有用的特别合适的案例只是我还没看见如果有看见的同学也请告诉我一声一起交流学习。原文发布时间为2018-03-27 本文作者张建飞 原文链接
干货好文请关注扫描以下二维码