面向服务和面向领域的不同


2013-12-16 17:00 "@
sinaID99267"的内容
想求老大进一步用代码的方式来阐述一下这个问题,最好是一个简单易懂的示例,谢谢! ...

这个问题其实是SOA架构本身的问题,因为Spring是SOA架构中的一个实现,所以也带有这个问题。

SOA架构的问题来自于其面向服务自身,用服务来对业务切分,这样,可能破坏业务自身的聚合性。也就是说,业务逻辑会散落在多个服务之中,想修改代码,要修改多个服务。

服务的抽象主要是从重用角度抽象的,如有很多客户都要下订单,那么我们就抽象一个订单服务提供给这些客户;然后又有很多客户要签合同,那么我们就抽象一个合同服务于这些客户;这些服务都是从对外接口的角度粗粒度设计,或者只是纯粹从业务流程来考虑的,并没有从业务内部领域逻辑角度考虑,有人说,这两者不是一样吗?有时一样,有时不一样。比如某个单位无论给订单客户还是合同客户,都有一个统一的最低底线价格,按照面向服务的设计,这个底线价格会各自写到订单服务和合同服务中。

有人说,那订单服务与合同服务共同重用一个实体名叫价格策略。这种重用其实引发了依赖,紧耦合,破坏了松耦合。

比如订单服务代码:


public class OrderService{

public int getPrice(PricePlocy pricePlocy){
..
}
}

合同服务:


public class ContractService{

public int getPrice(PricePlocy pricePlocy){
..
}
}

这两个服务都依赖PricePlocy,就算PricePlocy是个接口,如果接口发生变化,这两个服务的代码就发生变化。

那么有人说,将PricePlocy独立出来成为一个PricePlocyService,三个服务通过ESB消息总线交互。那这样服务的粒度就太细了,如果再发生PricePlocy2,难道你再搞个PricePlocy2Service吗?

这里就发生了重用与松耦合矛盾的情况,重用的角度是从为客户服务这个外部因素考虑的,而不是从业务内部去发现领域规律。

如果从内部去考虑,发现内部聚合,自然外界接口就松耦合,比如订单服务和合同服务,背后有一个价格制定策略,这个是核心,是DNA,我们可能将定价算法作为一个聚合根,订单和合同只是定价这个实体的外部两个服务接口而已。

面向服务和面向领域的区别就如同销售部门和产品部门的区别一样(也类似项目和产品的区别),一个从外部考虑,一个从内部考虑,考虑方向完全不同。但是两者可以互补。如果片面从外部考虑,导致各个服务对领域的强行肢解,流程变动方便了,但是业务逻辑的一贯性凝聚性被破坏了。销售部门经常拉产品部门工程师为客户服务,结果耽误了产品自身的研发一贯性。

[该贴被banq于2013-12-16 21:33修改过]

结合我以前一篇帖子:依赖注入与事件编程
http://www.jdon.com/45264

文章提出了依赖注入的一个问题。

这里总结一下:
聚合 >松耦合>重用 ==> 事件驱动>依赖注入>继承

上面这篇文章是证明"事件驱动>依赖注入",至于“依赖注入>继承”见这个帖子:使用组合+依赖替代继承案例,虽然使用的AngularJS的依赖注入,但是原理适合所有编程语言:
http://www.jdon.com/45895

相关参考:
http://www.jdon.com/designpatterns/ooprimer.html
[该贴被banq于2013-12-17 10:07修改过]

Spring 并不是SOA,尽管它很像(各种Service)。

Spring与SOA的问题应该是:他们的切分没有基于一个逻辑系统,所以往往会有冗余和空白。逻辑不是由一个个Service组成,而是由语言组成的(语言是逻辑的载体,曾经某道友提过“原语”,也是接近这意思)。Service并没有语言特性,也没有处于语言外的逻辑系统特性,于是它所切分的逻辑是混乱的。

让程序构成语言,再用语言表达逻辑,这才是出路,否则表达业务逻辑就是空谈。业务逻辑是逻辑,什么是逻辑?你的代码做到了吗?

对于依赖注入,这东西根本不是谈逻辑的。考虑传统编程思想,DI的最大问题是不在控制范围之内。无论高内聚低耦合,还是全内聚零耦合,我认为这些问题是特定语言引起的,思考下我们的自然语言,有考虑过这些问题么?
[该贴被SpeedVan于2013-12-19 10:05修改过]


2013-12-19 09:16 "@
SpeedVan"的内容
Spring与SOA的问题应该是:他们的切分没有基于一个逻辑系统,所以往往会有冗余和空白。 ...

赞同,它们其实类似一个以项目决定一切的公司,公司老总是销售出身,眼睛只盯客户,这其实是第一个层次朴素的经商理念,真正一流企业是做标准,是保持这个行业的领域创新的Number 1。

我在另外一个帖子:http://www.jdon.com/45998#23144164也谈了服务的定位以及与CRUD区别:

基于领域分析后的新Service服务如同一个broker,也就是商人,做倒进倒出生意,把工厂(领域)的产品卖给客户,自己并不生产产品(业务逻辑)。

至于CRUD放入数据存储中,也就是Respository仓储中,CRUD本来就是数据存储的概念,不是业务领域概念,这个前提是你的系统复杂,不是简单得只有CRUD,那么DDD不适用。

DDD是在CRUD基础上有复杂业务逻辑,SOA解决了CRUD基础上的流程逻辑,但是并没有解决领域逻辑,聚合以及聚合的状态变化,聚合状态是因为受到外部流程变动而变化,但是聚合状态不是全局变量,不是流程想改就改,得通过领域规则这一关。

打个比喻,居委会要举行选举,选举有选举的流程,其中一个步骤是通知你参加,但是你可以不参加,不能因为外部流程,你没有自己的领域规则。

所以,领域要和两个敌人做斗争,前面是商人SOA服务,后者是数据库,和这两者划清界限,领域层就自然出来了。

与数据库划清界限是有关建模方面参考:建模风暴:
http://www.jdon.com/45999

总体来说:在需求分析这块经历三个阶段:
数据驱动SQL ---->服务驱动SOA ----->领域驱动

一个结合领域驱动+服务驱动SOA+数据驱动SQL架构图应该如下:

[该贴被banq于2013-12-19 12:51修改过]

"这两个服务都依赖PricePlocy,就算PricePlocy是个接口,如果接口发生变化,这两个服务的代码就发生变化。"
“依赖注入”还有个名字“控制反转”,因此前述语句的问题在于没有彻底理解“反转”一词,接口应该是调用方定义和实现,实现一般采用适配器模式来做,因此,接口是调用方拿来隔离调用依赖变化的,变化即隔离在适配器实现中,唯一不变的是变,变是好事,变是成长,关键是如何隔离变,“反转”至关重要!

"这两个服务都依赖PricePlocy,就算PricePlocy是个接口,如果接口发生变化,这两个服务的代码就发生变化。"
“依赖注入”还有个名字“控制反转”,因此前述语句的问题在于没有彻底理解“反转”一词,接口应该是调用方定义和实现,实现一般采用适配器模式来做,因此,接口是调用方拿来隔离调用依赖变化的,变化即隔离在适配器实现中,唯一不变的是变,变是好事,变是成长,关键是如何隔离变,“反转”至关重要!